Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiDiscussionToolsFindComment
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 6
420
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
210
 getValue
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 needsToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isWriteMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\DiscussionTools;
4
5use MediaWiki\Api\ApiBase;
6use MediaWiki\Api\ApiMain;
7use MediaWiki\Api\ApiUsageException;
8use MediaWiki\Extension\DiscussionTools\ThreadItem\DatabaseThreadItem;
9use MediaWiki\Title\Title;
10use MediaWiki\Title\TitleFormatter;
11use Wikimedia\ParamValidator\ParamValidator;
12
13class ApiDiscussionToolsFindComment extends ApiBase {
14
15    public function __construct(
16        ApiMain $main,
17        string $name,
18        private readonly ThreadItemStore $threadItemStore,
19        private readonly TitleFormatter $titleFormatter,
20    ) {
21        parent::__construct( $main, $name );
22    }
23
24    /**
25     * @inheritDoc
26     * @throws ApiUsageException
27     */
28    public function execute() {
29        $params = $this->extractRequestParams();
30
31        $values = [];
32
33        $this->requireAtLeastOneParameter( $params, 'idorname', 'heading', 'page' );
34
35        if ( $params['idorname'] ) {
36            $idOrName = $params['idorname'];
37
38            $byId = $this->threadItemStore->findNewestRevisionsById( $idOrName );
39            foreach ( $byId as $item ) {
40                $values[] = $this->getValue( $item, 'id' );
41            }
42
43            $byName = $this->threadItemStore->findNewestRevisionsByName( $idOrName );
44            foreach ( $byName as $item ) {
45                $values[] = $this->getValue( $item, 'name' );
46            }
47        } else {
48            $this->requireAtLeastOneParameter( $params, 'heading' );
49            $this->requireAtLeastOneParameter( $params, 'page' );
50
51            $heading = $params['heading'];
52            $page = $params['page'];
53
54            $title = Title::newFromText( $page );
55            if ( $title ) {
56                $articleId = $title->getArticleId();
57
58                if ( $articleId ) {
59                    try {
60                        $byHeading = $this->threadItemStore->findNewestRevisionsByHeading(
61                            $heading, $articleId, $title->getTitleValue()
62                        );
63                    } catch ( PageNeverHadThreadsException ) {
64                        $this->dieWithError( [ 'apierror-discussiontools-findcomment-pagenevertalk' ] );
65                    }
66                    foreach ( $byHeading as $item ) {
67                        $values[] = $this->getValue( $item, 'heading' );
68                    }
69                } else {
70                    // TODO: Consider if we should still search if the article ID is not found
71                    $this->dieWithError( [ 'apierror-discussiontools-findcomment-pagenevertalk' ] );
72                }
73            } else {
74                // TODO: Consider if we should still search if the title is invalid
75                $this->dieWithError( [ 'apierror-discussiontools-findcomment-pagenevertalk' ] );
76            }
77        }
78
79        $redirects = 0;
80        foreach ( $values as $value ) {
81            if ( $value['couldredirect'] ) {
82                $redirects++;
83                if ( $redirects > 1 ) {
84                    break;
85                }
86            }
87        }
88        foreach ( $values as $value ) {
89            if ( $redirects === 1 && $value['couldredirect'] ) {
90                $value['shouldredirect'] = true;
91            }
92            $this->getResult()->addValue( $this->getModuleName(), null, $value );
93        }
94    }
95
96    /**
97     * Get a value to add to the results
98     *
99     * @param DatabaseThreadItem $item Thread item
100     * @param string $matchedBy How the thread item was matched (id, name or heading)
101     */
102    private function getValue( DatabaseThreadItem $item, string $matchedBy ): array {
103        $title = Title::castFromPageReference( $item->getPage() );
104
105        return [
106            'id' => $item->getId(),
107            'name' => $item->getName(),
108            'title' => $this->titleFormatter->getPrefixedText( $item->getPage() ),
109            'oldid' => !$item->getRevision()->isCurrent() ? $item->getRevision()->getId() : null,
110            'matchedby' => $matchedBy,
111            // Could this be an automatic redirect? Will be converted to 'shouldredirect'
112            // if there is only one of these in the result set.
113            'couldredirect' => $item->isCanonicalPermalink(),
114        ];
115    }
116
117    /**
118     * @inheritDoc
119     */
120    public function getAllowedParams() {
121        return [
122            'idorname' => [
123                ParamValidator::PARAM_TYPE => 'string',
124            ],
125            'heading' => [
126                ParamValidator::PARAM_TYPE => 'string',
127            ],
128            'page' => [
129                ParamValidator::PARAM_TYPE => 'string',
130            ],
131        ];
132    }
133
134    /**
135     * @inheritDoc
136     */
137    public function needsToken() {
138        return false;
139    }
140
141    /**
142     * @inheritDoc
143     */
144    public function isWriteMode() {
145        return false;
146    }
147}