mediawiki-extensions-Discus.../includes/ApiDiscussionToolsFindComment.php
Ed Sanders 8b99c1fbbe Add more search methods to findNewestRevisionsByHeading
In production, indexing the full history of every page will take
too long, so we can't rely on being able to find the target heading
text in the history of the current page.

When this search fails, fall back to the following searches:
* The heading appears in the current revision of a sub-page
* The heading appears anywhere on the wiki, but only once

Bug: T356276
Change-Id: I90e92cb9c85aaf6fb2355f842450981bbe6abf2d
2024-02-01 18:32:36 +00:00

146 lines
3.5 KiB
PHP

<?php
namespace MediaWiki\Extension\DiscussionTools;
use ApiBase;
use ApiMain;
use ApiUsageException;
use MediaWiki\Extension\DiscussionTools\ThreadItem\DatabaseThreadItem;
use MediaWiki\Title\Title;
use MediaWiki\Title\TitleFormatter;
use Wikimedia\ParamValidator\ParamValidator;
class ApiDiscussionToolsFindComment extends ApiBase {
private ThreadItemStore $threadItemStore;
private TitleFormatter $titleFormatter;
public function __construct(
ApiMain $main,
string $name,
ThreadItemStore $threadItemStore,
TitleFormatter $titleFormatter
) {
parent::__construct( $main, $name );
$this->threadItemStore = $threadItemStore;
$this->titleFormatter = $titleFormatter;
}
/**
* @inheritDoc
* @throws ApiUsageException
*/
public function execute() {
$params = $this->extractRequestParams();
$values = [];
$this->requireAtLeastOneParameter( $params, 'idorname', 'heading', 'page' );
if ( $params['idorname'] ) {
$idOrName = $params['idorname'];
$byId = $this->threadItemStore->findNewestRevisionsById( $idOrName );
foreach ( $byId as $item ) {
$values[] = $this->getValue( $item, 'id' );
}
$byName = $this->threadItemStore->findNewestRevisionsByName( $idOrName );
foreach ( $byName as $item ) {
$values[] = $this->getValue( $item, 'name' );
}
} else {
$this->requireAtLeastOneParameter( $params, 'heading' );
$this->requireAtLeastOneParameter( $params, 'page' );
$heading = $params['heading'];
$page = $params['page'];
$title = Title::newFromText( $page );
if ( $title ) {
$articleId = $title->getArticleId();
if ( $articleId ) {
$byHeading = $this->threadItemStore->findNewestRevisionsByHeading(
$heading, $articleId, $title->getTitleValue()
);
foreach ( $byHeading as $item ) {
$values[] = $this->getValue( $item, 'heading' );
}
}
}
}
$redirects = 0;
foreach ( $values as $value ) {
if ( $value['couldredirect'] ) {
$redirects++;
if ( $redirects > 1 ) {
break;
}
}
}
foreach ( $values as $value ) {
if ( $redirects === 1 && $value['couldredirect'] ) {
$value['shouldredirect'] = true;
}
unset( $value['couldredirect'] );
$this->getResult()->addValue( $this->getModuleName(), null, $value );
}
}
/**
* Get a value to add to the results
*
* @param DatabaseThreadItem $item Thread item
* @param string $matchedBy How the thread item was matched (id, name or heading)
* @return array
*/
private function getValue( DatabaseThreadItem $item, string $matchedBy ): array {
$title = Title::castFromPageReference( $item->getPage() );
return [
'id' => $item->getId(),
'name' => $item->getName(),
'title' => $this->titleFormatter->getPrefixedText( $item->getPage() ),
'oldid' => !$item->getRevision()->isCurrent() ? $item->getRevision()->getId() : null,
'matchedby' => $matchedBy,
// Could this be an automatic redirect? Will be converted to 'shouldredirect'
// if there is only one of these in the result set.
// Matches logic in Special:GoToComment
'couldredirect' => $item->getRevision()->isCurrent() && !is_string( $item->getTranscludedFrom() )
];
}
/**
* @inheritDoc
*/
public function getAllowedParams() {
return [
'idorname' => [
ParamValidator::PARAM_TYPE => 'string',
],
'heading' => [
ParamValidator::PARAM_TYPE => 'string',
],
'page' => [
ParamValidator::PARAM_TYPE => 'string',
],
];
}
/**
* @inheritDoc
*/
public function needsToken() {
return false;
}
/**
* @inheritDoc
*/
public function isWriteMode() {
return false;
}
}