Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.86% |
44 / 58 |
|
28.57% |
2 / 7 |
CRAP | |
0.00% |
0 / 1 |
ApiShortenUrl | |
75.86% |
44 / 58 |
|
28.57% |
2 / 7 |
24.08 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
88.46% |
23 / 26 |
|
0.00% |
0 / 1 |
8.10 | |||
recordInStatsD | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
4.13 | |||
checkUserRights | |
25.00% |
1 / 4 |
|
0.00% |
0 / 1 |
3.69 | |||
mustBePosted | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getAllowedParams | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
2 | |||
getExamplesMessages | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | /** |
4 | * Class ApiShortenUrl |
5 | * |
6 | * Implements action=shortenurl to provide url shortening services via the API. |
7 | * |
8 | * Even though this is a write action sometimes, we still use GET so we can be |
9 | * cached at varnish levels very easily. |
10 | */ |
11 | |
12 | namespace MediaWiki\Extension\UrlShortener; |
13 | |
14 | use ApiBase; |
15 | use ApiMain; |
16 | use ApiUsageException; |
17 | use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface; |
18 | use MediaWiki\Permissions\PermissionManager; |
19 | use MediaWiki\Status\Status; |
20 | use Wikimedia\ParamValidator\ParamValidator; |
21 | |
22 | class ApiShortenUrl extends ApiBase { |
23 | |
24 | private bool $qrCodeEnabled; |
25 | private int $qrCodeShortenLimit; |
26 | private PermissionManager $permissionManager; |
27 | private StatsdDataFactoryInterface $statsdDataFactory; |
28 | |
29 | /** |
30 | * @inheritDoc |
31 | */ |
32 | public function __construct( |
33 | ApiMain $mainModule, |
34 | $moduleName, |
35 | PermissionManager $permissionManager, |
36 | StatsdDataFactoryInterface $statsdDataFactory |
37 | ) { |
38 | parent::__construct( $mainModule, $moduleName ); |
39 | |
40 | $this->qrCodeEnabled = (bool)$this->getConfig()->get( 'UrlShortenerEnableQrCode' ); |
41 | $this->qrCodeShortenLimit = (int)$this->getConfig()->get( 'UrlShortenerQrCodeShortenLimit' ); |
42 | $this->permissionManager = $permissionManager; |
43 | $this->statsdDataFactory = $statsdDataFactory; |
44 | } |
45 | |
46 | public function execute() { |
47 | $this->checkUserRights(); |
48 | |
49 | if ( $this->getConfig()->get( 'UrlShortenerReadOnly' ) ) { |
50 | $this->dieWithError( 'apierror-urlshortener-disabled' ); |
51 | } |
52 | |
53 | $params = $this->extractRequestParams(); |
54 | |
55 | $url = $params['url']; |
56 | $qrCode = $this->qrCodeEnabled && $params['qrcode']; |
57 | |
58 | $validityCheck = UrlShortenerUtils::validateUrl( $url ); |
59 | if ( $validityCheck !== true ) { |
60 | $this->dieStatus( Status::newFatal( $validityCheck ) ); |
61 | } |
62 | |
63 | if ( $qrCode ) { |
64 | $status = UrlShortenerUtils::getQrCode( $url, $this->qrCodeShortenLimit, $this->getUser() ); |
65 | } else { |
66 | $status = UrlShortenerUtils::maybeCreateShortCode( $url, $this->getUser() ); |
67 | } |
68 | |
69 | if ( !$status->isOK() ) { |
70 | $this->dieStatus( $status ); |
71 | } |
72 | |
73 | $shortUrlsOrQrCode = $status->getValue(); |
74 | $urlShortened = isset( $shortUrlsOrQrCode[ 'url' ] ); |
75 | |
76 | $ret = []; |
77 | |
78 | // QR codes may not have short URLs, in which case we don't want them in the response. |
79 | if ( $urlShortened ) { |
80 | $ret['shorturl'] = UrlShortenerUtils::makeUrl( $shortUrlsOrQrCode[ 'url' ] ); |
81 | $ret['shorturlalt'] = UrlShortenerUtils::makeUrl( $shortUrlsOrQrCode[ 'alt' ] ); |
82 | } |
83 | |
84 | if ( $qrCode ) { |
85 | $ret['qrcode'] = $shortUrlsOrQrCode['qrcode']; |
86 | } |
87 | |
88 | $this->recordInStatsD( $urlShortened, $qrCode ); |
89 | |
90 | // You get the cached response, YOU get the cached response, EVERYONE gets the cached response. |
91 | $this->getMain()->setCacheMode( "public" ); |
92 | $this->getMain()->setCacheMaxAge( UrlShortenerUtils::CACHE_TTL_VALID ); |
93 | |
94 | $this->getResult()->addValue( null, $this->getModuleName(), $ret ); |
95 | } |
96 | |
97 | /** |
98 | * Record simple usage counts in statsd. |
99 | * |
100 | * @param bool $urlShortened |
101 | * @param bool $qrCode |
102 | * @return void |
103 | */ |
104 | private function recordInStatsD( bool $urlShortened, bool $qrCode ): void { |
105 | if ( $qrCode && $urlShortened ) { |
106 | $this->statsdDataFactory->increment( 'extension.UrlShortener.api.shorturl_and_qrcode' ); |
107 | } elseif ( $qrCode ) { |
108 | $this->statsdDataFactory->increment( 'extension.UrlShortener.api.qrcode' ); |
109 | } else { |
110 | $this->statsdDataFactory->increment( 'extension.UrlShortener.api.shorturl' ); |
111 | } |
112 | } |
113 | |
114 | /** |
115 | * Check that the user can create a short url |
116 | * @throws ApiUsageException if the user lacks the rights |
117 | */ |
118 | public function checkUserRights() { |
119 | if ( !$this->permissionManager->userHasRight( $this->getUser(), 'urlshortener-create-url' ) ) { |
120 | $this->dieWithError( [ 'apierror-permissiondenied', |
121 | $this->msg( "apierror-urlshortener-permissiondenied" ) ] |
122 | ); |
123 | } |
124 | } |
125 | |
126 | public function mustBePosted() { |
127 | return true; |
128 | } |
129 | |
130 | /** |
131 | * @inheritDoc |
132 | */ |
133 | protected function getAllowedParams() { |
134 | $params = [ |
135 | 'url' => [ |
136 | ParamValidator::PARAM_REQUIRED => true, |
137 | ], |
138 | ]; |
139 | if ( $this->qrCodeEnabled ) { |
140 | $params['qrcode'] = [ |
141 | ParamValidator::PARAM_TYPE => 'boolean', |
142 | ParamValidator::PARAM_DEFAULT => false, |
143 | ]; |
144 | } |
145 | return $params; |
146 | } |
147 | |
148 | /** |
149 | * @inheritDoc |
150 | */ |
151 | public function getExamplesMessages() { |
152 | return [ |
153 | 'action=shortenurl&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FArctica' |
154 | => 'apihelp-shortenurl-example-1', |
155 | 'action=shortenurl&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FArctica&qrcode=1' |
156 | => 'apihelp-shortenurl-example-2', |
157 | ]; |
158 | } |
159 | } |