Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.54% |
62 / 67 |
|
80.00% |
8 / 10 |
CRAP | |
0.00% |
0 / 1 |
MediaFileHandler | |
92.54% |
62 / 67 |
|
80.00% |
8 / 10 |
22.20 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getPage | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getFile | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
run | |
78.95% |
15 / 19 |
|
0.00% |
0 / 1 |
5.23 | |||
getResponse | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
1 | |||
needsWriteAccess | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getParamSettings | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
getETag | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
getLastModified | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
hasRepresentation | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Rest\Handler; |
4 | |
5 | use File; |
6 | use MediaFileTrait; |
7 | use MediaWiki\Page\ExistingPageRecord; |
8 | use MediaWiki\Page\PageLookup; |
9 | use MediaWiki\Rest\Handler; |
10 | use MediaWiki\Rest\LocalizedHttpException; |
11 | use MediaWiki\Rest\Response; |
12 | use MediaWiki\Rest\SimpleHandler; |
13 | use RepoGroup; |
14 | use Wikimedia\Message\MessageValue; |
15 | use Wikimedia\ParamValidator\ParamValidator; |
16 | |
17 | /** |
18 | * Handler class for media meta-data |
19 | */ |
20 | class MediaFileHandler extends SimpleHandler { |
21 | use MediaFileTrait; |
22 | |
23 | private RepoGroup $repoGroup; |
24 | private PageLookup $pageLookup; |
25 | |
26 | /** |
27 | * @var ExistingPageRecord|false|null |
28 | */ |
29 | private $page = false; |
30 | |
31 | /** |
32 | * @var File|false|null |
33 | */ |
34 | private $file = false; |
35 | |
36 | public function __construct( |
37 | RepoGroup $repoGroup, |
38 | PageLookup $pageLookup |
39 | ) { |
40 | $this->repoGroup = $repoGroup; |
41 | $this->pageLookup = $pageLookup; |
42 | } |
43 | |
44 | /** |
45 | * @return ExistingPageRecord|null |
46 | */ |
47 | private function getPage(): ?ExistingPageRecord { |
48 | if ( $this->page === false ) { |
49 | $this->page = $this->pageLookup->getExistingPageByText( |
50 | $this->getValidatedParams()['title'], NS_FILE |
51 | ); |
52 | } |
53 | return $this->page; |
54 | } |
55 | |
56 | /** |
57 | * @return File|null |
58 | */ |
59 | private function getFile(): ?File { |
60 | if ( $this->file === false ) { |
61 | $page = $this->getPage(); |
62 | $this->file = |
63 | // @phan-suppress-next-line PhanTypeMismatchArgumentNullable |
64 | $this->repoGroup->findFile( $page, [ 'private' => $this->getAuthority() ] ) ?: null; |
65 | } |
66 | return $this->file; |
67 | } |
68 | |
69 | /** |
70 | * @param string $title |
71 | * @return Response |
72 | * @throws LocalizedHttpException |
73 | */ |
74 | public function run( $title ) { |
75 | $page = $this->getPage(); |
76 | |
77 | if ( !$page ) { |
78 | throw new LocalizedHttpException( |
79 | MessageValue::new( 'rest-nonexistent-title' )->plaintextParams( $title ), |
80 | 404 |
81 | ); |
82 | } |
83 | |
84 | if ( !$this->getAuthority()->authorizeRead( 'read', $page ) ) { |
85 | throw new LocalizedHttpException( |
86 | MessageValue::new( 'rest-permission-denied-title' )->plaintextParams( $title ), |
87 | 403 |
88 | ); |
89 | } |
90 | |
91 | $fileObj = $this->getFile(); |
92 | if ( !$fileObj || !$fileObj->exists() ) { |
93 | throw new LocalizedHttpException( |
94 | MessageValue::new( 'rest-cannot-load-file' )->plaintextParams( $title ), |
95 | 404 |
96 | ); |
97 | } |
98 | |
99 | $response = $this->getResponse( $fileObj ); |
100 | return $this->getResponseFactory()->createJson( $response ); |
101 | } |
102 | |
103 | /** |
104 | * @param File $file the file to load media links for |
105 | * @return array response data |
106 | */ |
107 | private function getResponse( File $file ): array { |
108 | [ $maxWidth, $maxHeight ] = self::getImageLimitsFromOption( |
109 | $this->getAuthority()->getUser(), 'imagesize' |
110 | ); |
111 | [ $maxThumbWidth, $maxThumbHeight ] = self::getImageLimitsFromOption( |
112 | $this->getAuthority()->getUser(), 'thumbsize' |
113 | ); |
114 | $transforms = [ |
115 | 'preferred' => [ |
116 | 'maxWidth' => $maxWidth, |
117 | 'maxHeight' => $maxHeight |
118 | ], |
119 | 'thumbnail' => [ |
120 | 'maxWidth' => $maxThumbWidth, |
121 | 'maxHeight' => $maxThumbHeight |
122 | ] |
123 | ]; |
124 | |
125 | return $this->getFileInfo( $file, $this->getAuthority(), $transforms ); |
126 | } |
127 | |
128 | public function needsWriteAccess() { |
129 | return false; |
130 | } |
131 | |
132 | public function getParamSettings() { |
133 | return [ |
134 | 'title' => [ |
135 | self::PARAM_SOURCE => 'path', |
136 | ParamValidator::PARAM_TYPE => 'string', |
137 | ParamValidator::PARAM_REQUIRED => true, |
138 | Handler::PARAM_DESCRIPTION => new MessageValue( 'rest-param-desc-media-file-title' ), |
139 | ], |
140 | ]; |
141 | } |
142 | |
143 | /** |
144 | * @return string|null |
145 | * @throws LocalizedHttpException |
146 | */ |
147 | protected function getETag(): ?string { |
148 | $file = $this->getFile(); |
149 | if ( !$file || !$file->exists() ) { |
150 | return null; |
151 | } |
152 | |
153 | return '"' . $file->getSha1() . '"'; |
154 | } |
155 | |
156 | /** |
157 | * @return string|null |
158 | * @throws LocalizedHttpException |
159 | */ |
160 | protected function getLastModified(): ?string { |
161 | $file = $this->getFile(); |
162 | if ( !$file || !$file->exists() ) { |
163 | return null; |
164 | } |
165 | |
166 | return $file->getTimestamp(); |
167 | } |
168 | |
169 | /** |
170 | * @return bool |
171 | */ |
172 | protected function hasRepresentation() { |
173 | $file = $this->getFile(); |
174 | return $file && $file->exists(); |
175 | } |
176 | } |