2022-01-31 15:01:32 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace MediaWiki\Extension\DiscussionTools;
|
|
|
|
|
2024-10-19 21:39:18 +00:00
|
|
|
use MediaWiki\Api\ApiBase;
|
|
|
|
use MediaWiki\Api\ApiMain;
|
|
|
|
use MediaWiki\Api\ApiUsageException;
|
2022-11-21 13:49:21 +00:00
|
|
|
use MediaWiki\Extension\DiscussionTools\Hooks\HookUtils;
|
2022-09-08 00:29:35 +00:00
|
|
|
use MediaWiki\Extension\VisualEditor\VisualEditorParsoidClientFactory;
|
2022-09-02 01:49:44 +00:00
|
|
|
use MediaWiki\Revision\RevisionLookup;
|
2022-01-31 15:01:32 +00:00
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
2023-08-19 04:14:36 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2022-01-31 15:01:32 +00:00
|
|
|
use Wikimedia\ParamValidator\ParamValidator;
|
2023-08-08 21:16:30 +00:00
|
|
|
use Wikimedia\Parsoid\Core\ResourceLimitExceededException;
|
2022-01-31 15:01:32 +00:00
|
|
|
|
|
|
|
class ApiDiscussionToolsCompare extends ApiBase {
|
|
|
|
|
2022-10-21 19:34:18 +00:00
|
|
|
private CommentParser $commentParser;
|
|
|
|
private VisualEditorParsoidClientFactory $parsoidClientFactory;
|
|
|
|
private RevisionLookup $revisionLookup;
|
2022-09-02 01:49:44 +00:00
|
|
|
|
2022-09-02 02:05:02 +00:00
|
|
|
public function __construct(
|
|
|
|
ApiMain $main,
|
|
|
|
string $name,
|
2022-09-08 00:29:35 +00:00
|
|
|
VisualEditorParsoidClientFactory $parsoidClientFactory,
|
2022-09-02 01:49:44 +00:00
|
|
|
CommentParser $commentParser,
|
|
|
|
RevisionLookup $revisionLookup
|
2022-09-02 02:05:02 +00:00
|
|
|
) {
|
2022-01-31 15:01:32 +00:00
|
|
|
parent::__construct( $main, $name );
|
2022-09-08 00:29:35 +00:00
|
|
|
$this->parsoidClientFactory = $parsoidClientFactory;
|
2022-09-02 02:05:02 +00:00
|
|
|
$this->commentParser = $commentParser;
|
2022-09-02 01:49:44 +00:00
|
|
|
$this->revisionLookup = $revisionLookup;
|
2022-01-31 15:01:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-10-28 18:24:02 +00:00
|
|
|
* @throws ApiUsageException
|
2022-01-31 15:01:32 +00:00
|
|
|
*/
|
2024-04-26 01:38:05 +00:00
|
|
|
private function getRevision( array $params, string $prefix ): RevisionRecord {
|
|
|
|
if ( isset( $params["{$prefix}rev"] ) ) {
|
|
|
|
$rev = $this->revisionLookup->getRevisionById( $params["{$prefix}rev"] );
|
|
|
|
if ( !$rev ) {
|
|
|
|
$this->dieWithError( [ 'apierror-nosuchrevid', $params["{$prefix}rev"] ] );
|
2022-09-02 01:49:44 +00:00
|
|
|
}
|
|
|
|
|
2022-01-31 15:01:32 +00:00
|
|
|
} else {
|
2024-04-26 01:38:05 +00:00
|
|
|
$title = Title::newFromText( $params["{$prefix}title"] );
|
|
|
|
if ( !$title ) {
|
|
|
|
$this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params["{$prefix}title"] ) ] );
|
2022-01-31 15:01:32 +00:00
|
|
|
}
|
2024-04-26 01:38:05 +00:00
|
|
|
$rev = $this->revisionLookup->getRevisionByTitle( $title );
|
|
|
|
if ( !$rev ) {
|
2022-09-02 01:49:44 +00:00
|
|
|
$this->dieWithError(
|
2024-04-26 01:38:05 +00:00
|
|
|
[ 'apierror-missingrev-title', wfEscapeWikiText( $title->getPrefixedText() ) ],
|
2022-09-02 01:49:44 +00:00
|
|
|
'nosuchrevid'
|
|
|
|
);
|
|
|
|
}
|
2022-01-31 15:01:32 +00:00
|
|
|
}
|
2024-04-26 01:49:32 +00:00
|
|
|
// To keep things simple, don't allow viewing deleted revisions through this API
|
|
|
|
// (even if the current user could view them if we checked with userCan()).
|
|
|
|
if ( !$rev->audienceCan( RevisionRecord::DELETED_TEXT, RevisionRecord::FOR_PUBLIC ) ) {
|
|
|
|
$this->dieWithError( [ 'apierror-missingcontent-revid', $rev->getId() ], 'missingcontent' );
|
|
|
|
}
|
2024-04-26 01:38:05 +00:00
|
|
|
return $rev;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
* @throws ApiUsageException
|
|
|
|
*/
|
|
|
|
public function execute() {
|
|
|
|
$params = $this->extractRequestParams();
|
|
|
|
|
|
|
|
$this->requireOnlyOneParameter( $params, 'fromtitle', 'fromrev' );
|
|
|
|
$this->requireOnlyOneParameter( $params, 'totitle', 'torev' );
|
|
|
|
|
|
|
|
$toRev = $this->getRevision( $params, 'to' );
|
2022-01-31 15:01:32 +00:00
|
|
|
|
|
|
|
// When polling for new comments this is an important optimisation,
|
|
|
|
// as usually there is no new revision.
|
|
|
|
if ( $toRev->getId() === $params['fromrev'] ) {
|
|
|
|
$this->addResult( $toRev, $toRev );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-04-26 01:38:05 +00:00
|
|
|
$fromRev = $this->getRevision( $params, 'from' );
|
2022-01-31 15:01:32 +00:00
|
|
|
|
|
|
|
if ( $fromRev->hasSameContent( $toRev ) ) {
|
|
|
|
$this->addResult( $fromRev, $toRev );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-08 21:16:30 +00:00
|
|
|
try {
|
|
|
|
$fromItemSet = HookUtils::parseRevisionParsoidHtml( $fromRev, __METHOD__ );
|
|
|
|
$toItemSet = HookUtils::parseRevisionParsoidHtml( $toRev, __METHOD__ );
|
|
|
|
} catch ( ResourceLimitExceededException $e ) {
|
|
|
|
$this->dieWithException( $e );
|
|
|
|
}
|
2022-01-31 15:01:32 +00:00
|
|
|
|
|
|
|
$removedComments = [];
|
2022-02-19 06:31:34 +00:00
|
|
|
foreach ( $fromItemSet->getCommentItems() as $fromComment ) {
|
|
|
|
if ( !$toItemSet->findCommentById( $fromComment->getId() ) ) {
|
2022-01-31 15:01:32 +00:00
|
|
|
$removedComments[] = $fromComment->jsonSerializeForDiff();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$addedComments = [];
|
2022-02-19 06:31:34 +00:00
|
|
|
foreach ( $toItemSet->getCommentItems() as $toComment ) {
|
|
|
|
if ( !$fromItemSet->findCommentById( $toComment->getId() ) ) {
|
2022-01-31 15:01:32 +00:00
|
|
|
$addedComments[] = $toComment->jsonSerializeForDiff();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->addResult( $fromRev, $toRev, $removedComments, $addedComments );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the result object from revisions and comment lists
|
|
|
|
*
|
|
|
|
* @param RevisionRecord $fromRev From revision
|
|
|
|
* @param RevisionRecord $toRev To revision
|
|
|
|
* @param array $removedComments Removed comments
|
|
|
|
* @param array $addedComments Added comments
|
|
|
|
*/
|
|
|
|
protected function addResult(
|
|
|
|
RevisionRecord $fromRev, RevisionRecord $toRev, array $removedComments = [], array $addedComments = []
|
|
|
|
) {
|
|
|
|
$fromTitle = Title::newFromLinkTarget(
|
|
|
|
$fromRev->getPageAsLinkTarget()
|
|
|
|
);
|
|
|
|
$toTitle = Title::newFromLinkTarget(
|
|
|
|
$toRev->getPageAsLinkTarget()
|
|
|
|
);
|
|
|
|
$result = [
|
|
|
|
'fromrevid' => $fromRev->getId(),
|
|
|
|
'fromtitle' => $fromTitle->getPrefixedText(),
|
|
|
|
'torevid' => $toRev->getId(),
|
|
|
|
'totitle' => $toTitle->getPrefixedText(),
|
|
|
|
'removedcomments' => $removedComments,
|
|
|
|
'addedcomments' => $addedComments,
|
|
|
|
];
|
|
|
|
$this->getResult()->addValue( null, $this->getModuleName(), $result );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
*/
|
|
|
|
public function getAllowedParams() {
|
|
|
|
return [
|
|
|
|
'fromtitle' => [
|
|
|
|
ApiBase::PARAM_HELP_MSG => 'apihelp-compare-param-fromtitle',
|
|
|
|
],
|
|
|
|
'fromrev' => [
|
|
|
|
ParamValidator::PARAM_TYPE => 'integer',
|
|
|
|
ApiBase::PARAM_HELP_MSG => 'apihelp-compare-param-fromrev',
|
|
|
|
],
|
|
|
|
'totitle' => [
|
|
|
|
ApiBase::PARAM_HELP_MSG => 'apihelp-compare-param-totitle',
|
|
|
|
],
|
|
|
|
'torev' => [
|
|
|
|
ParamValidator::PARAM_TYPE => 'integer',
|
|
|
|
ApiBase::PARAM_HELP_MSG => 'apihelp-compare-param-torev',
|
|
|
|
],
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
*/
|
|
|
|
public function needsToken() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
*/
|
|
|
|
public function isInternal() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
*/
|
|
|
|
public function isWriteMode() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|