Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
38.46% |
25 / 65 |
|
40.00% |
6 / 15 |
CRAP | |
0.00% |
0 / 1 |
MediaTransformOutput | |
38.46% |
25 / 65 |
|
40.00% |
6 / 15 |
412.87 | |
0.00% |
0 / 1 |
getWidth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getHeight | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFile | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getExtension | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
getUrl | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getStoragePath | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setStoragePath | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
toHtml | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
isError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasFile | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
12 | |||
fileIsSource | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
getLocalCopyPath | |
44.44% |
4 / 9 |
|
0.00% |
0 / 1 |
9.29 | |||
streamFileWithStatus | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
6 | |||
streamFile | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
linkWrap | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
getDescLinkAttribs | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
90 |
1 | <?php |
2 | |
3 | /** |
4 | * Base class for the output of file transformation methods. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License along |
17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | * http://www.gnu.org/copyleft/gpl.html |
20 | * |
21 | * @file |
22 | * @ingroup Media |
23 | */ |
24 | |
25 | use MediaWiki\FileRepo\File\File; |
26 | use MediaWiki\MainConfigNames; |
27 | use MediaWiki\MediaWikiServices; |
28 | use MediaWiki\Status\Status; |
29 | use MediaWiki\Xml\Xml; |
30 | use Wikimedia\FileBackend\FileBackend; |
31 | use Wikimedia\FileBackend\HTTPFileStreamer; |
32 | |
33 | /** |
34 | * Base class for the output of MediaHandler::doTransform() and File::transform(). |
35 | * |
36 | * @stable to extend |
37 | * @ingroup Media |
38 | */ |
39 | abstract class MediaTransformOutput { |
40 | /** @var array Associative array mapping optional supplementary image files |
41 | * from pixel density (eg 1.5 or 2) to additional URLs. |
42 | */ |
43 | public $responsiveUrls = []; |
44 | |
45 | /** @var File */ |
46 | protected $file; |
47 | |
48 | /** @var int Image width */ |
49 | protected $width; |
50 | |
51 | /** @var int Image height */ |
52 | protected $height; |
53 | |
54 | /** @var string|false URL path to the thumb */ |
55 | protected $url; |
56 | |
57 | /** @var string|false */ |
58 | protected $page; |
59 | |
60 | /** @var string|null|false Filesystem path to the thumb */ |
61 | protected $path; |
62 | |
63 | /** @var string|false Language code, false if not set */ |
64 | protected $lang; |
65 | |
66 | /** @var string|false Permanent storage path */ |
67 | protected $storagePath = false; |
68 | |
69 | /** |
70 | * @return int Width of the output box |
71 | */ |
72 | public function getWidth() { |
73 | return $this->width; |
74 | } |
75 | |
76 | /** |
77 | * @return int Height of the output box |
78 | */ |
79 | public function getHeight() { |
80 | return $this->height; |
81 | } |
82 | |
83 | /** |
84 | * @return File |
85 | */ |
86 | public function getFile() { |
87 | return $this->file; |
88 | } |
89 | |
90 | /** |
91 | * Get the final extension of the thumbnail. |
92 | * Returns false for scripted transformations. |
93 | * @stable to override |
94 | * |
95 | * @return string|false |
96 | */ |
97 | public function getExtension() { |
98 | return $this->path ? FileBackend::extensionFromPath( $this->path ) : false; |
99 | } |
100 | |
101 | /** |
102 | * @stable to override |
103 | * |
104 | * @return string|false The thumbnail URL |
105 | */ |
106 | public function getUrl() { |
107 | return $this->url; |
108 | } |
109 | |
110 | /** |
111 | * @stable to override |
112 | * |
113 | * @return string|false The permanent thumbnail storage path |
114 | */ |
115 | public function getStoragePath() { |
116 | return $this->storagePath; |
117 | } |
118 | |
119 | /** |
120 | * @stable to override |
121 | * |
122 | * @param string $storagePath The permanent storage path |
123 | * @return void |
124 | */ |
125 | public function setStoragePath( $storagePath ) { |
126 | $this->storagePath = $storagePath; |
127 | if ( $this->path === false ) { |
128 | $this->path = $storagePath; |
129 | } |
130 | } |
131 | |
132 | /** |
133 | * Fetch HTML for this transform output |
134 | * |
135 | * @param array $options Associative array of options. Boolean options |
136 | * should be indicated with a value of true for true, and false or |
137 | * absent for false. |
138 | * |
139 | * alt Alternate text or caption |
140 | * desc-link Boolean, show a description link |
141 | * file-link Boolean, show a file download link |
142 | * custom-url-link Custom URL to link to |
143 | * custom-title-link Custom Title object to link to |
144 | * valign vertical-align property, if the output is an inline element |
145 | * img-class Class applied to the "<img>" tag, if there is such a tag |
146 | * |
147 | * For images, desc-link and file-link are implemented as a click-through. For |
148 | * sounds and videos, they may be displayed in other ways. |
149 | * |
150 | * @return string |
151 | */ |
152 | abstract public function toHtml( $options = [] ); |
153 | |
154 | /** |
155 | * This will be overridden to return true in error classes |
156 | * @return bool |
157 | */ |
158 | public function isError() { |
159 | return false; |
160 | } |
161 | |
162 | /** |
163 | * Check if an output thumbnail file actually exists. |
164 | * |
165 | * This will return false if there was an error, the |
166 | * thumbnail is to be handled client-side only, or if |
167 | * transformation was deferred via TRANSFORM_LATER. |
168 | * This file may exist as a new file in /tmp, a file |
169 | * in permanent storage, or even refer to the original. |
170 | * |
171 | * @return bool |
172 | */ |
173 | public function hasFile() { |
174 | // If TRANSFORM_LATER, $this->path will be false. |
175 | // Note: a null path means "use the source file". |
176 | return ( !$this->isError() && ( $this->path || $this->path === null ) ); |
177 | } |
178 | |
179 | /** |
180 | * Check if the output thumbnail is the same as the source. |
181 | * This can occur if the requested width was bigger than the source. |
182 | * |
183 | * @return bool |
184 | */ |
185 | public function fileIsSource() { |
186 | return ( !$this->isError() && $this->path === null ); |
187 | } |
188 | |
189 | /** |
190 | * Get the path of a file system copy of the thumbnail. |
191 | * Callers should never write to this path. |
192 | * |
193 | * @return string|false Returns false if there isn't one |
194 | */ |
195 | public function getLocalCopyPath() { |
196 | if ( $this->isError() ) { |
197 | return false; |
198 | } |
199 | |
200 | if ( $this->path === null ) { |
201 | return $this->file->getLocalRefPath(); // assume thumb was not scaled |
202 | } |
203 | if ( FileBackend::isStoragePath( $this->path ) ) { |
204 | $be = $this->file->getRepo()->getBackend(); |
205 | // The temp file will be process cached by FileBackend |
206 | $fsFile = $be->getLocalReference( [ 'src' => $this->path ] ); |
207 | |
208 | return $fsFile ? $fsFile->getPath() : false; |
209 | } |
210 | return $this->path; // may return false |
211 | } |
212 | |
213 | /** |
214 | * Stream the file if there were no errors |
215 | * |
216 | * @param array $headers Additional HTTP headers to send on success |
217 | * @return Status |
218 | * @since 1.27 |
219 | */ |
220 | public function streamFileWithStatus( $headers = [] ) { |
221 | if ( !$this->path ) { |
222 | return Status::newFatal( 'backend-fail-stream', '<no path>' ); |
223 | } |
224 | |
225 | $repo = $this->file->getRepo(); |
226 | |
227 | if ( $repo && FileBackend::isStoragePath( $this->path ) ) { |
228 | return Status::wrap( |
229 | $repo->getBackend()->streamFile( |
230 | [ 'src' => $this->path, 'headers' => $headers, ] |
231 | ) |
232 | ); |
233 | } else { |
234 | $streamer = new HTTPFileStreamer( |
235 | $this->getLocalCopyPath(), |
236 | $repo ? $repo->getBackend()->getStreamerOptions() : [] |
237 | ); |
238 | |
239 | $success = $streamer->stream( $headers ); |
240 | return $success ? Status::newGood() |
241 | : Status::newFatal( 'backend-fail-stream', $this->path ); |
242 | } |
243 | } |
244 | |
245 | /** |
246 | * Stream the file if there were no errors |
247 | * |
248 | * @deprecated since 1.26, use streamFileWithStatus |
249 | * @param array $headers Additional HTTP headers to send on success |
250 | * @return bool Success |
251 | */ |
252 | public function streamFile( $headers = [] ) { |
253 | return $this->streamFileWithStatus( $headers )->isOK(); |
254 | } |
255 | |
256 | /** |
257 | * Wrap some XHTML text in an anchor tag with the given attributes |
258 | * or, fallback to a span in the absence thereof. |
259 | * |
260 | * @param array $linkAttribs |
261 | * @param string $contents |
262 | * @return string |
263 | */ |
264 | protected function linkWrap( $linkAttribs, $contents ) { |
265 | if ( isset( $linkAttribs['href'] ) ) { |
266 | return Xml::tags( 'a', $linkAttribs, $contents ); |
267 | } |
268 | $parserEnableLegacyMediaDOM = MediaWikiServices::getInstance() |
269 | ->getMainConfig()->get( MainConfigNames::ParserEnableLegacyMediaDOM ); |
270 | if ( $parserEnableLegacyMediaDOM ) { |
271 | return $contents; |
272 | } |
273 | return Xml::tags( 'span', $linkAttribs ?: null, $contents ); |
274 | } |
275 | |
276 | /** |
277 | * @param string|null $title |
278 | * @param string|array $params Query parameters to add |
279 | * @return array |
280 | */ |
281 | public function getDescLinkAttribs( $title = null, $params = [] ) { |
282 | if ( is_array( $params ) ) { |
283 | $query = $params; |
284 | } else { |
285 | $query = []; |
286 | } |
287 | if ( $this->page && $this->page !== 1 ) { |
288 | $query['page'] = $this->page; |
289 | } |
290 | if ( $this->lang ) { |
291 | $query['lang'] = $this->lang; |
292 | } |
293 | |
294 | if ( is_string( $params ) && $params !== '' ) { |
295 | $query = $params . '&' . wfArrayToCgi( $query ); |
296 | } |
297 | |
298 | $attribs = [ |
299 | 'href' => $this->file->getTitle()->getLocalURL( $query ), |
300 | ]; |
301 | |
302 | $parserEnableLegacyMediaDOM = MediaWikiServices::getInstance() |
303 | ->getMainConfig()->get( MainConfigNames::ParserEnableLegacyMediaDOM ); |
304 | if ( $parserEnableLegacyMediaDOM ) { |
305 | $attribs['class'] = 'image'; |
306 | } else { |
307 | $attribs['class'] = 'mw-file-description'; |
308 | } |
309 | |
310 | if ( $title ) { |
311 | $attribs['title'] = $title; |
312 | } |
313 | |
314 | return $attribs; |
315 | } |
316 | } |