Merge "Edit API for replies"

This commit is contained in:
jenkins-bot 2020-07-27 23:56:10 +00:00 committed by Gerrit Code Review
commit 740e6eb174
7 changed files with 230 additions and 42 deletions

View file

@ -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
View 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
View 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}}"
}

View 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;
}
}

View file

@ -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();
} );

View file

@ -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 () {

View file

@ -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
*/