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