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 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiContentTranslationFavoriteSuggestions
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 8
240
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 / 28
0.00% covered (danger)
0.00%
0 / 1
42
 validateLanguages
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 18
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
 isInternal
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2declare( strict_types = 1 );
3
4namespace ContentTranslation\ActionApi;
5
6use ContentTranslation\Service\UserService;
7use ContentTranslation\Store\FavoriteSuggestionStore;
8use MediaWiki\Api\ApiBase;
9use MediaWiki\Api\ApiMain;
10use MediaWiki\Api\ApiUsageException;
11use MediaWiki\Languages\LanguageNameUtils;
12use TitleFactory;
13use Wikimedia\ParamValidator\ParamValidator;
14
15/**
16 * @author Nik Gkountas
17 * @license GPL-2.0-or-later
18 * @since 2025.06
19 */
20class ApiContentTranslationFavoriteSuggestions extends ApiBase {
21    public function __construct(
22        ApiMain $mainModule,
23        string $action,
24        private readonly FavoriteSuggestionStore $favoriteSuggestionStore,
25        private readonly UserService $userService,
26        private readonly TitleFactory $titleFactory,
27        private readonly LanguageNameUtils $languageNameUtils
28    ) {
29        parent::__construct( $mainModule, $action );
30    }
31
32    /**
33     * @return void
34     * @throws ApiUsageException
35     */
36    public function execute() {
37        $params = $this->extractRequestParams();
38        $user = $this->getUser();
39
40        if ( !$this->getUser()->isRegistered() ) {
41            $this->dieWithError( 'apierror-cx-mustbeloggedin-favorites', 'notloggedin' );
42        }
43
44        $this->validateLanguages();
45
46        $page = $params['title'];
47        $title = $this->titleFactory->newFromText( $page );
48        if ( !$title ) {
49            $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $page ) ] );
50        }
51
52        $translatorUserId = $this->userService->getGlobalUserId( $user );
53
54        $listId = $this->favoriteSuggestionStore->findListIdByUser( $translatorUserId );
55
56        if ( $listId === null ) {
57            $listId = $this->favoriteSuggestionStore->createList( $translatorUserId );
58        }
59
60        if ( $params['listaction'] === 'add' ) {
61            $success = $this->favoriteSuggestionStore->addSuggestion(
62                $listId,
63                $title->getPrefixedText(),
64                $params['from'],
65                $params['to']
66            );
67        } else {
68            $success = $this->favoriteSuggestionStore->removeSuggestion(
69                $listId,
70                $title->getPrefixedText(),
71                $params['from'],
72                $params['to']
73            );
74        }
75
76        $result = [ 'result' => $success ? 'success' : 'failure' ];
77
78        $this->getResult()->addValue( null, $this->getModuleName(), $result );
79    }
80
81    /**
82     * @throws ApiUsageException
83     */
84    private function validateLanguages(): void {
85        $params = $this->extractRequestParams();
86
87        if ( !$this->languageNameUtils->isKnownLanguageTag( $params['from'] ) ) {
88            $this->dieWithError( 'apierror-cx-invalidsourcelanguage', 'invalidsourcelanguage' );
89        }
90
91        if ( !$this->languageNameUtils->isKnownLanguageTag( $params['to'] ) ) {
92            $this->dieWithError( 'apierror-cx-invalidtargetlanguage', 'invalidtargetlanguage' );
93        }
94    }
95
96    /** @inheritDoc */
97    public function getAllowedParams() {
98        return [
99            'listaction' => [
100                ParamValidator::PARAM_REQUIRED => true,
101                ParamValidator::PARAM_TYPE => [ 'add', 'remove' ],
102            ],
103            'title' => [
104                ParamValidator::PARAM_REQUIRED => true,
105                ParamValidator::PARAM_TYPE => 'string'
106            ],
107            'from' => [
108                ParamValidator::PARAM_REQUIRED => true,
109                ParamValidator::PARAM_TYPE => 'string',
110            ],
111            'to' => [
112                ParamValidator::PARAM_REQUIRED => true,
113                ParamValidator::PARAM_TYPE => 'string',
114            ],
115        ];
116    }
117
118    /** @inheritDoc */
119    public function needsToken() {
120        return 'csrf';
121    }
122
123    /** @inheritDoc */
124    public function isWriteMode() {
125        return true;
126    }
127
128    /** @inheritDoc */
129    public function isInternal() {
130        return true;
131    }
132
133    /** @inheritDoc */
134    protected function getExamplesMessages() {
135        return [
136            'action=cxfavoritesuggestions&listaction=add&title=Title&from=en&to=es' =>
137                'apihelp-cxfavoritesuggestions-example-1',
138            'action=cxfavoritesuggestions&listaction=remove&title=Title&from=en&to=es' =>
139                'apihelp-cxfavoritesuggestions-example-2'
140        ];
141    }
142}