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