Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.54% covered (success)
92.54%
62 / 67
80.00% covered (warning)
80.00%
8 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
MediaFileHandler
92.54% covered (success)
92.54%
62 / 67
80.00% covered (warning)
80.00%
8 / 10
22.20
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
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getLastModified
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 hasRepresentation
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace MediaWiki\Rest\Handler;
4
5use File;
6use MediaFileTrait;
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 RepoGroup;
14use Wikimedia\Message\MessageValue;
15use Wikimedia\ParamValidator\ParamValidator;
16
17/**
18 * Handler class for media meta-data
19 */
20class 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}