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