Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
14.97% |
28 / 187 |
|
1.79% |
1 / 56 |
CRAP | |
0.00% |
0 / 1 |
MediaHandler | |
14.97% |
28 / 187 |
|
1.79% |
1 / 56 |
5525.53 | |
0.00% |
0 / 1 |
getHandler | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getParamMap | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
validateParam | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
makeParamString | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
parseParamString | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
normaliseParams | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getImageSize | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSizeAndMetadata | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMetadata | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
useLegacyMetadata | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
hasMostDerivedMethod | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getSizeAndMetadataWithFallback | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
182 | |||
getMetadataVersion | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
convertMetadataVersion | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMetadataType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isMetadataValid | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isFileMetadataValid | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCommonMetaArray | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getScriptedTransform | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTransform | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
doTransform | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getThumbType | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
canRender | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
mustRender | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isMultiPage | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
pageCount | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isVectorized | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isAnimatedImage | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
canAnimateThumbnail | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isEnabled | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPageDimensions | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPageText | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getEntireText | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
30 | |||
formatMetadata | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
formatMetadataHelper | |
92.00% |
23 / 25 |
|
0.00% |
0 / 1 |
7.03 | |||
formatTag | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
visibleMetadataFields | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
addMeta | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
getShortDesc | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getLongDesc | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGeneralShortDesc | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGeneralLongDesc | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
fitBoxWidth | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getDimensionsString | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
parserTransformHook | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
verifyUpload | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
removeBadFile | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
30 | |||
filterThumbnailPurgeList | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
canRotate | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRotation | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
logErrorForExternalProcess | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
getAvailableLanguages | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMatchedLanguage | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDefaultRenderLanguage | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getLength | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isExpensiveToThumbnail | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
supportsBucketing | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
sanitizeParamsForBucketing | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getWarningConfig | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPageRangesByDimensions | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
56 | |||
getContentHeaders | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
useSplitMetadata | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | use MediaWiki\Context\IContextSource; |
22 | use MediaWiki\HookContainer\HookRunner; |
23 | use MediaWiki\MediaWikiServices; |
24 | use MediaWiki\Parser\Parser; |
25 | use MediaWiki\Status\Status; |
26 | use Wikimedia\FileBackend\FSFile\FSFile; |
27 | |
28 | /** |
29 | * @defgroup Media Media |
30 | * |
31 | * Media handlers and other classes relating to Multimedia support, |
32 | * with the exception of FileRepo and FileBackend, which have their own groups. |
33 | */ |
34 | |
35 | /** |
36 | * Base media handler class |
37 | * |
38 | * @stable to extend |
39 | * @ingroup Media |
40 | */ |
41 | abstract class MediaHandler { |
42 | public const TRANSFORM_LATER = 1; |
43 | public const METADATA_GOOD = true; |
44 | public const METADATA_BAD = false; |
45 | public const METADATA_COMPATIBLE = 2; // for old but backwards compatible. |
46 | /** |
47 | * Max length of error logged by logErrorForExternalProcess() |
48 | */ |
49 | private const MAX_ERR_LOG_SIZE = 65535; |
50 | |
51 | /** |
52 | * Get a MediaHandler for a given MIME type from the instance cache |
53 | * |
54 | * @param string $type |
55 | * @return MediaHandler|false |
56 | */ |
57 | public static function getHandler( $type ) { |
58 | return MediaWikiServices::getInstance() |
59 | ->getMediaHandlerFactory()->getHandler( $type ); |
60 | } |
61 | |
62 | /** |
63 | * Get an associative array mapping magic word IDs to parameter names. |
64 | * Will be used by the parser to identify parameters. |
65 | * @return string[] |
66 | */ |
67 | abstract public function getParamMap(); |
68 | |
69 | /** |
70 | * Validate a thumbnail parameter at parse time. |
71 | * Return true to accept the parameter, and false to reject it. |
72 | * If you return false, the parser will do something quiet and forgiving. |
73 | * |
74 | * @param string $name |
75 | * @param mixed $value |
76 | * @return bool |
77 | */ |
78 | abstract public function validateParam( $name, $value ); |
79 | |
80 | /** |
81 | * Merge a parameter array into a string appropriate for inclusion in filenames |
82 | * |
83 | * @param array $params Array of parameters that have been through normaliseParams. |
84 | * @return string |
85 | */ |
86 | abstract public function makeParamString( $params ); |
87 | |
88 | /** |
89 | * Parse a param string made with makeParamString back into an array |
90 | * |
91 | * @param string $str The parameter string without file name (e.g. 122px) |
92 | * @return array|false Array of parameters or false on failure. |
93 | */ |
94 | abstract public function parseParamString( $str ); |
95 | |
96 | /** |
97 | * Changes the parameter array as necessary, ready for transformation. |
98 | * Should be idempotent. |
99 | * Returns false if the parameters are unacceptable and the transform should fail |
100 | * @param File $image |
101 | * @param array &$params |
102 | * @return bool |
103 | */ |
104 | abstract public function normaliseParams( $image, &$params ); |
105 | |
106 | /** |
107 | * Get an image size array like that returned by getimagesize(), or false if it |
108 | * can't be determined. |
109 | * |
110 | * This function is used for determining the width, height and bitdepth directly |
111 | * from an image. The results are stored in the database in the img_width, |
112 | * img_height, img_bits fields. |
113 | * |
114 | * @note If this is a multipage file, return the width and height of the |
115 | * first page. |
116 | * |
117 | * @deprecated since 1.37, override getSizeAndMetadata instead |
118 | * |
119 | * @param File|FSFile|false $image The image object, or false if there isn't one. |
120 | * Warning, FSFile::getPropsFromPath might pass an FSFile instead of File (!) |
121 | * @param string $path The filename |
122 | * @return array|false Follow the format of PHP getimagesize() internal function. |
123 | * See https://www.php.net/getimagesize. MediaWiki will only ever use the |
124 | * first two array keys (the width and height), and the 'bits' associative |
125 | * key. All other array keys are ignored. Returning a 'bits' key is optional |
126 | * as not all formats have a notion of "bitdepth". Returns false on failure. |
127 | */ |
128 | public function getImageSize( $image, $path ) { |
129 | return false; |
130 | } |
131 | |
132 | /** |
133 | * Get image size information and metadata array. |
134 | * |
135 | * If this returns null, the caller will fall back to getImageSize() and |
136 | * getMetadata(). |
137 | * |
138 | * If getImageSize() or getMetadata() are implemented in the most derived |
139 | * class, they will be used instead of this function. To override this |
140 | * behaviour, override useLegacyMetadata(). |
141 | * |
142 | * @stable to override |
143 | * @since 1.37 |
144 | * |
145 | * @param MediaHandlerState $state An object for saving process-local state. |
146 | * This is normally a File object which will be passed back to other |
147 | * MediaHandler methods like pageCount(), if they are called in the same |
148 | * request. The handler can use this object to save its state. |
149 | * @param string $path The filename |
150 | * @return array|null Null to fall back to getImageSize(), or an array with |
151 | * the following keys. All keys are optional. |
152 | * - width: The width. If multipage, return the first page width. (optional) |
153 | * - height: The height. If multipage, return the first page height. (optional) |
154 | * - bits: The number of bits for each color (optional) |
155 | * - metadata: A JSON-serializable array of metadata (optional) |
156 | */ |
157 | public function getSizeAndMetadata( $state, $path ) { |
158 | return null; |
159 | } |
160 | |
161 | /** |
162 | * Get handler-specific metadata which will be saved in the img_metadata field. |
163 | * @deprecated since 1.37 override getSizeAndMetadata() instead |
164 | * |
165 | * @param File|FSFile|false $image The image object, or false if there isn't one. |
166 | * Warning, FSFile::getPropsFromPath might pass an FSFile instead of File (!) |
167 | * @param string $path The filename |
168 | * @return string A string of metadata in php serialized form (Run through serialize()) |
169 | */ |
170 | public function getMetadata( $image, $path ) { |
171 | return ''; |
172 | } |
173 | |
174 | /** |
175 | * If this returns true, the new method getSizeAndMetadata() will not be |
176 | * called. The legacy methods getMetadata() and getImageSize() will be used |
177 | * instead. |
178 | * |
179 | * @since 1.37 |
180 | * @stable to override |
181 | * @return bool |
182 | */ |
183 | protected function useLegacyMetadata() { |
184 | return $this->hasMostDerivedMethod( 'getMetadata' ) |
185 | || $this->hasMostDerivedMethod( 'getImageSize' ); |
186 | } |
187 | |
188 | /** |
189 | * Check whether a method is implemented in the most derived class. |
190 | * |
191 | * @since 1.37 |
192 | * @param string $name |
193 | * @return bool |
194 | */ |
195 | protected function hasMostDerivedMethod( $name ) { |
196 | $rc = new ReflectionClass( $this ); |
197 | $rm = new ReflectionMethod( $this, $name ); |
198 | return $rm->getDeclaringClass()->getName() === $rc->getName(); |
199 | } |
200 | |
201 | /** |
202 | * Get the metadata array and the image size, with b/c fallback. |
203 | * |
204 | * The legacy methods will be used if useLegacyMetadata() returns true or |
205 | * if getSizeAndMetadata() returns null. |
206 | * |
207 | * Absent metadata will be normalized to an empty array. Absent width and |
208 | * height will be normalized to zero. |
209 | * |
210 | * @param File|FSFile $file This must be a File or FSFile to support the |
211 | * legacy methods. When the legacy methods are removed, this will be |
212 | * narrowed to MediaHandlerState. |
213 | * @param string $path |
214 | * @return array|false|null False on failure, or an array with the following keys: |
215 | * - width: The width. If multipage, return the first page width. |
216 | * - height: The height. If multipage, return the first page height. |
217 | * - bits: The number of bits for each color (optional) |
218 | * - metadata: A JSON-serializable array of metadata |
219 | * @since 1.37 |
220 | */ |
221 | final public function getSizeAndMetadataWithFallback( $file, $path ) { |
222 | if ( !$this->useLegacyMetadata() ) { |
223 | if ( $file instanceof MediaHandlerState ) { |
224 | $state = $file; |
225 | } else { |
226 | $state = new TrivialMediaHandlerState; |
227 | } |
228 | $info = $this->getSizeAndMetadata( $state, $path ); |
229 | if ( $info === false ) { |
230 | return false; |
231 | } |
232 | if ( $info !== null ) { |
233 | $info += [ 'width' => 0, 'height' => 0, 'metadata' => [] ]; |
234 | if ( !is_array( $info['metadata'] ) ) { |
235 | throw new InvalidArgumentException( 'Media handler ' . |
236 | static::class . ' returned ' . get_debug_type( $info['metadata'] ) . |
237 | ' for metadata, should be array' ); |
238 | } |
239 | return $info; |
240 | } |
241 | } |
242 | |
243 | $blob = $this->getMetadata( $file, $path ); |
244 | // @phan-suppress-next-line PhanParamTooMany |
245 | $size = $this->getImageSize( |
246 | $file, |
247 | $path, |
248 | $blob // Secret TimedMediaHandler parameter |
249 | ); |
250 | if ( $blob === false && $size === false ) { |
251 | return false; |
252 | } |
253 | if ( $size ) { |
254 | $info = [ |
255 | 'width' => $size[0] ?? 0, |
256 | 'height' => $size[1] ?? 0 |
257 | ]; |
258 | if ( isset( $size['bits'] ) ) { |
259 | $info['bits'] = $size['bits']; |
260 | } |
261 | } else { |
262 | $info = [ 'width' => 0, 'height' => 0 ]; |
263 | } |
264 | if ( $blob !== false ) { |
265 | // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged |
266 | $metadata = @unserialize( $blob ); |
267 | if ( $metadata === false ) { |
268 | // Unserialize error |
269 | $metadata = [ '_error' => $blob ]; |
270 | } elseif ( !is_array( $metadata ) ) { |
271 | $metadata = []; |
272 | } |
273 | $info['metadata'] = $metadata; |
274 | } else { |
275 | $info['metadata'] = []; |
276 | } |
277 | return $info; |
278 | } |
279 | |
280 | /** |
281 | * Get metadata version. |
282 | * |
283 | * This is not used for validating metadata, this is used for the api when returning |
284 | * metadata, since api content formats should stay the same over time, and so things |
285 | * using ForeignApiRepo can keep backwards compatibility |
286 | * |
287 | * All core media handlers share a common version number, and extensions can |
288 | * use the GetMetadataVersion hook to append to the array (they should append a unique |
289 | * string so not to get confusing). If there was a media handler named 'foo' with metadata |
290 | * version 3 it might add to the end of the array the element 'foo=3'. if the core metadata |
291 | * version is 2, the end version string would look like '2;foo=3'. |
292 | * |
293 | * @stable to override |
294 | * |
295 | * @return string Version string |
296 | */ |
297 | public static function getMetadataVersion() { |
298 | $version = [ '2' ]; // core metadata version |
299 | ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )->onGetMetadataVersion( $version ); |
300 | |
301 | return implode( ';', $version ); |
302 | } |
303 | |
304 | /** |
305 | * Convert metadata version. |
306 | * |
307 | * By default just returns $metadata, but can be used to allow |
308 | * media handlers to convert between metadata versions. |
309 | * @stable to override |
310 | * |
311 | * @param array $metadata Metadata array |
312 | * @param int|string $version Target version |
313 | * @return array Serialized metadata in specified version, or $metadata on fail. |
314 | */ |
315 | public function convertMetadataVersion( $metadata, $version = 1 ) { |
316 | return $metadata; |
317 | } |
318 | |
319 | /** |
320 | * Get a string describing the type of metadata, for display purposes. |
321 | * @stable to override |
322 | * |
323 | * @note This method is currently unused. |
324 | * @param File $image |
325 | * @return string|false |
326 | */ |
327 | public function getMetadataType( $image ) { |
328 | return false; |
329 | } |
330 | |
331 | /** |
332 | * Check if the metadata string is valid for this handler. |
333 | * If it returns MediaHandler::METADATA_BAD (or false), Image |
334 | * will reload the metadata from the file and update the database. |
335 | * MediaHandler::METADATA_GOOD for if the metadata is a-ok, |
336 | * MediaHandler::METADATA_COMPATIBLE if metadata is old but backwards |
337 | * compatible (which may or may not trigger a metadata reload). |
338 | * |
339 | * @note Returning self::METADATA_BAD will trigger a metadata reload from |
340 | * file on page view. Always returning this from a broken file, or suddenly |
341 | * triggering as bad metadata for a large number of files can cause |
342 | * performance problems. |
343 | * |
344 | * @deprecated since 1.37 use isFileMetadataValid |
345 | * @param File $image |
346 | * @param string $metadata The metadata in serialized form |
347 | * @return bool|int |
348 | */ |
349 | public function isMetadataValid( $image, $metadata ) { |
350 | return self::METADATA_GOOD; |
351 | } |
352 | |
353 | /** |
354 | * Check if the metadata is valid for this handler. |
355 | * If it returns MediaHandler::METADATA_BAD (or false), Image |
356 | * will reload the metadata from the file and update the database. |
357 | * MediaHandler::METADATA_GOOD for if the metadata is a-ok, |
358 | * MediaHandler::METADATA_COMPATIBLE if metadata is old but backwards |
359 | * compatible (which may or may not trigger a metadata reload). |
360 | * |
361 | * @note Returning self::METADATA_BAD will trigger a metadata reload from |
362 | * file on page view. Always returning this from a broken file, or suddenly |
363 | * triggering as bad metadata for a large number of files can cause |
364 | * performance problems. |
365 | * |
366 | * This was introduced in 1.37 to replace isMetadataValid(), which took a |
367 | * serialized string as a parameter. Handlers overriding this method are |
368 | * expected to use accessors to get the metadata out of the File. The |
369 | * reasons for the change were to get rid of serialization, and to allow |
370 | * handlers to partially load metadata with getMetadataItem(). For example |
371 | * a handler could just validate a version number. |
372 | * |
373 | * @stable to override |
374 | * @since 1.37 |
375 | * @param File $image |
376 | * @return bool|int |
377 | */ |
378 | public function isFileMetadataValid( $image ) { |
379 | return self::METADATA_GOOD; |
380 | } |
381 | |
382 | /** |
383 | * Get an array of standard (FormatMetadata type) metadata values. |
384 | * |
385 | * The returned data is largely the same as that from getMetadata(), |
386 | * but formatted in a standard, stable, handler-independent way. |
387 | * The idea being that some values like ImageDescription or Artist |
388 | * are universal and should be retrievable in a handler generic way. |
389 | * |
390 | * The specific properties are the type of properties that can be |
391 | * handled by the FormatMetadata class. These values are exposed to the |
392 | * user via the filemetadata parser function. |
393 | * |
394 | * Details of the response format of this function can be found at |
395 | * https://www.mediawiki.org/wiki/Manual:File_metadata_handling |
396 | * tl/dr: the response is an associative array of |
397 | * properties keyed by name, but the value can be complex. You probably |
398 | * want to call one of the FormatMetadata::flatten* functions on the |
399 | * property values before using them, or call |
400 | * FormatMetadata::getFormattedData() on the full response array, which |
401 | * transforms all values into prettified, human-readable text. |
402 | * |
403 | * Subclasses overriding this function must return a value which is a |
404 | * valid API response fragment (all associative array keys are valid |
405 | * XML tagnames). |
406 | * |
407 | * Note, if the file simply has no metadata, but the handler supports |
408 | * this interface, it should return an empty array, not false. |
409 | * |
410 | * @stable to override |
411 | * |
412 | * @param File $file |
413 | * @return array|false False if interface not supported |
414 | * @since 1.23 |
415 | */ |
416 | public function getCommonMetaArray( File $file ) { |
417 | return false; |
418 | } |
419 | |
420 | /** |
421 | * Get a MediaTransformOutput object representing an alternate of the transformed |
422 | * output which will call an intermediary thumbnail assist script. |
423 | * |
424 | * Used when the repository has a thumbnailScriptUrl option configured. |
425 | * |
426 | * Return false to fall back to the regular getTransform(). |
427 | * |
428 | * @stable to override |
429 | * |
430 | * @param File $image |
431 | * @param string $script |
432 | * @param array $params |
433 | * @return ThumbnailImage|false |
434 | */ |
435 | public function getScriptedTransform( $image, $script, $params ) { |
436 | return false; |
437 | } |
438 | |
439 | /** |
440 | * Get a MediaTransformOutput object representing the transformed output. Does not |
441 | * actually do the transform. |
442 | * |
443 | * @stable to override |
444 | * |
445 | * @param File $image |
446 | * @param string $dstPath Filesystem destination path |
447 | * @param string $dstUrl Destination URL to use in output HTML |
448 | * @param array $params Arbitrary set of parameters validated by $this->validateParam() |
449 | * @return MediaTransformOutput |
450 | */ |
451 | final public function getTransform( $image, $dstPath, $dstUrl, $params ) { |
452 | return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER ); |
453 | } |
454 | |
455 | /** |
456 | * Get a MediaTransformOutput object representing the transformed output. Does the |
457 | * transform unless $flags contains self::TRANSFORM_LATER. |
458 | * |
459 | * @stable to override |
460 | * |
461 | * @param File $image |
462 | * @param string $dstPath Filesystem destination path |
463 | * @param string $dstUrl Destination URL to use in output HTML |
464 | * @param array $params Arbitrary set of parameters validated by $this->validateParam() |
465 | * Note: These parameters have *not* gone through $this->normaliseParams() |
466 | * @param int $flags A bitfield, may contain self::TRANSFORM_LATER |
467 | * @return MediaTransformOutput |
468 | */ |
469 | abstract public function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ); |
470 | |
471 | /** |
472 | * Get the thumbnail extension and MIME type for a given source MIME type |
473 | * |
474 | * @stable to override |
475 | * |
476 | * @param string $ext Extension of original file |
477 | * @param string $mime MIME type of original file |
478 | * @param array|null $params Handler specific rendering parameters |
479 | * @return array Thumbnail extension and MIME type |
480 | */ |
481 | public function getThumbType( $ext, $mime, $params = null ) { |
482 | $magic = MediaWikiServices::getInstance()->getMimeAnalyzer(); |
483 | if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) { |
484 | // The extension is not valid for this MIME type and we do |
485 | // recognize the MIME type |
486 | $knownExt = $magic->getExtensionFromMimeTypeOrNull( $mime ); |
487 | if ( $knownExt !== null ) { |
488 | return [ $knownExt, $mime ]; |
489 | } |
490 | } |
491 | |
492 | // The extension is correct (true) or the MIME type is unknown to |
493 | // MediaWiki (null) |
494 | return [ $ext, $mime ]; |
495 | } |
496 | |
497 | /** |
498 | * True if the handled types can be transformed |
499 | * |
500 | * @stable to override |
501 | * |
502 | * @param File $file |
503 | * @return bool |
504 | */ |
505 | public function canRender( $file ) { |
506 | return true; |
507 | } |
508 | |
509 | /** |
510 | * True if handled types cannot be displayed directly in a browser |
511 | * but can be rendered |
512 | * |
513 | * @stable to override |
514 | * |
515 | * @param File $file |
516 | * @return bool |
517 | */ |
518 | public function mustRender( $file ) { |
519 | return false; |
520 | } |
521 | |
522 | /** |
523 | * True if the type has multi-page capabilities |
524 | * |
525 | * @stable to override |
526 | * |
527 | * @param File $file |
528 | * @return bool |
529 | */ |
530 | public function isMultiPage( $file ) { |
531 | return false; |
532 | } |
533 | |
534 | /** |
535 | * Page count for a multi-page document, false if unsupported or unknown |
536 | * |
537 | * @stable to override |
538 | * |
539 | * @param File $file |
540 | * @return int|false |
541 | */ |
542 | public function pageCount( File $file ) { |
543 | return false; |
544 | } |
545 | |
546 | /** |
547 | * The material is vectorized and thus scaling is lossless |
548 | * |
549 | * @stable to override |
550 | * |
551 | * @param File $file |
552 | * @return bool |
553 | */ |
554 | public function isVectorized( $file ) { |
555 | return false; |
556 | } |
557 | |
558 | /** |
559 | * The material is an image, and is animated. |
560 | * In particular, video material need not return true. |
561 | * @note Before 1.20, this was a method of ImageHandler only |
562 | * |
563 | * @stable to override |
564 | * |
565 | * @param File $file |
566 | * @return bool |
567 | */ |
568 | public function isAnimatedImage( $file ) { |
569 | return false; |
570 | } |
571 | |
572 | /** |
573 | * If the material is animated, we can animate the thumbnail |
574 | * @since 1.20 |
575 | * |
576 | * @stable to override |
577 | * |
578 | * @param File $file |
579 | * @return bool If material is not animated, handler may return any value. |
580 | */ |
581 | public function canAnimateThumbnail( $file ) { |
582 | return true; |
583 | } |
584 | |
585 | /** |
586 | * False if the handler is disabled for all files |
587 | * @stable to override |
588 | * |
589 | * @return bool |
590 | */ |
591 | public function isEnabled() { |
592 | return true; |
593 | } |
594 | |
595 | /** |
596 | * Get an associative array of page dimensions |
597 | * Currently "width" and "height" are understood, but this might be |
598 | * expanded in the future. |
599 | * Returns false if unknown. |
600 | * |
601 | * For a single page document format (!isMultipage()), this should return |
602 | * false. |
603 | * |
604 | * @note For non-paged media, use getImageSize. |
605 | * |
606 | * @stable to override |
607 | * |
608 | * @param File $image |
609 | * @param int $page What page to get dimensions of |
610 | * @return array|false |
611 | */ |
612 | public function getPageDimensions( File $image, $page ) { |
613 | return false; |
614 | } |
615 | |
616 | /** |
617 | * Generic getter for text layer. |
618 | * Currently overloaded by PDF and DjVu handlers |
619 | * @stable to override |
620 | * |
621 | * @param File $image |
622 | * @param int $page Page number to get information for |
623 | * @return string|false Page text or false when no text found or if |
624 | * unsupported. |
625 | */ |
626 | public function getPageText( File $image, $page ) { |
627 | return false; |
628 | } |
629 | |
630 | /** |
631 | * Get the text of the entire document. |
632 | * @param File $file |
633 | * @return string|false The text of the document or false if unsupported. |
634 | */ |
635 | public function getEntireText( File $file ) { |
636 | $numPages = $file->pageCount(); |
637 | if ( !$numPages ) { |
638 | // Not a multipage document |
639 | return $this->getPageText( $file, 1 ); |
640 | } |
641 | $document = ''; |
642 | for ( $i = 1; $i <= $numPages; $i++ ) { |
643 | $curPage = $this->getPageText( $file, $i ); |
644 | if ( is_string( $curPage ) ) { |
645 | $document .= $curPage . "\n"; |
646 | } |
647 | } |
648 | if ( $document !== '' ) { |
649 | return $document; |
650 | } |
651 | return false; |
652 | } |
653 | |
654 | /** |
655 | * Get an array structure that looks like this: |
656 | * |
657 | * [ |
658 | * 'visible' => [ |
659 | * 'Human-readable name' => 'Human readable value', |
660 | * ... |
661 | * ], |
662 | * 'collapsed' => [ |
663 | * 'Human-readable name' => 'Human readable value', |
664 | * ... |
665 | * ] |
666 | * ] |
667 | * The UI will format this into a table where the visible fields are always |
668 | * visible, and the collapsed fields are optionally visible. |
669 | * |
670 | * The function should return false if there is no metadata to display. |
671 | */ |
672 | |
673 | /** |
674 | * @todo FIXME: This interface is not very flexible. The media handler |
675 | * should generate HTML instead. It can do all the formatting according |
676 | * to some standard. That makes it possible to do things like visual |
677 | * indication of grouped and chained streams in ogg container files. |
678 | * @stable to override |
679 | * |
680 | * @param File $image |
681 | * @param IContextSource|false $context |
682 | * @return array|false |
683 | */ |
684 | public function formatMetadata( $image, $context = false ) { |
685 | return false; |
686 | } |
687 | |
688 | /** sorts the visible/invisible field. |
689 | * Split off from ImageHandler::formatMetadata, as used by more than |
690 | * one type of handler. |
691 | * |
692 | * This is used by the media handlers that use the FormatMetadata class |
693 | * |
694 | * @stable to override |
695 | * |
696 | * @param array $metadataArray |
697 | * @param IContextSource|false $context |
698 | * @return array[] Array for use displaying metadata. |
699 | */ |
700 | protected function formatMetadataHelper( $metadataArray, $context = false ) { |
701 | $result = [ |
702 | 'visible' => [], |
703 | 'collapsed' => [] |
704 | ]; |
705 | |
706 | // Allow this MediaHandler to override formatting on certain values |
707 | foreach ( $metadataArray as $tag => $vals ) { |
708 | $v = $this->formatTag( $tag, $vals, $context ); |
709 | if ( $v === false ) { |
710 | // Use default formatting |
711 | continue; |
712 | } |
713 | if ( $v === null ) { |
714 | // Remove this tag, don't format it for display |
715 | unset( $metadataArray[$tag] ); |
716 | } else { |
717 | // Allow subclass to override default formatting. |
718 | $metadataArray[$tag] = [ '_formatted' => $v ]; |
719 | if ( isset( $v['_type'] ) ) { |
720 | $metadataArray[$tag]['_type'] = $v['_type']; |
721 | unset( $metadataArray[$tag]['_formatted']['_type'] ); |
722 | } |
723 | } |
724 | } |
725 | |
726 | $formatted = FormatMetadata::getFormattedData( $metadataArray, $context ); |
727 | // Sort fields into visible and collapsed |
728 | $visibleFields = $this->visibleMetadataFields(); |
729 | foreach ( $formatted as $name => $value ) { |
730 | $tag = strtolower( $name ); |
731 | self::addMeta( $result, |
732 | in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed', |
733 | 'exif', |
734 | $tag, |
735 | $value |
736 | ); |
737 | } |
738 | |
739 | return $result; |
740 | } |
741 | |
742 | /** |
743 | * Override default formatting for the given metadata field. |
744 | * |
745 | * @stable to override |
746 | * |
747 | * @param string $key The metadata field key |
748 | * @param string|array $vals The unformatted value of this metadata field |
749 | * @param IContextSource|false $context Context to use (optional) |
750 | * @return false|null|string|array False to use default formatting, null |
751 | * to remove this tag from the formatted list; otherwise return |
752 | * a formatted HTML string (or array of them). |
753 | */ |
754 | protected function formatTag( string $key, $vals, $context = false ) { |
755 | return false; // Use default formatting |
756 | } |
757 | |
758 | /** |
759 | * Get a list of metadata items which should be displayed when |
760 | * the metadata table is collapsed. |
761 | * |
762 | * @stable to override |
763 | * |
764 | * @return string[] |
765 | */ |
766 | protected function visibleMetadataFields() { |
767 | return FormatMetadata::getVisibleFields(); |
768 | } |
769 | |
770 | /** |
771 | * This is used to generate an array element for each metadata value |
772 | * That array is then used to generate the table of metadata values |
773 | * on the image page |
774 | * |
775 | * @param array &$array An array containing elements for each type of visibility |
776 | * and each of those elements being an array of metadata items. This function adds |
777 | * a value to that array. |
778 | * @param string $visibility ('visible' or 'collapsed') if this value is hidden |
779 | * by default. |
780 | * @param string $type Type of metadata tag (currently always 'exif') |
781 | * @param string $id The name of the metadata tag (like 'artist' for example). |
782 | * its name in the table displayed is the message "$type-$id" (Ex exif-artist ). |
783 | * @param string $value Thingy goes into a wikitext table; it used to be escaped but |
784 | * that was incompatible with previous practise of customized display |
785 | * with wikitext formatting via messages such as 'exif-model-value'. |
786 | * So the escaping is taken back out, but generally this seems a confusing |
787 | * interface. |
788 | * @param bool|string $param Value to pass to the message for the name of the field |
789 | * as $1. Currently this parameter doesn't seem to ever be used. |
790 | * |
791 | * Note, everything here is passed through the parser later on (!) |
792 | */ |
793 | protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) { |
794 | $msg = wfMessage( "$type-$id", (string)$param ); |
795 | if ( $msg->exists() ) { |
796 | $name = $msg->text(); |
797 | } else { |
798 | // This is for future compatibility when using instant commons. |
799 | // So as to not display as ugly a name if a new metadata |
800 | // property is defined that we don't know about |
801 | // (not a major issue since such a property would be collapsed |
802 | // by default). |
803 | wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id ); |
804 | $name = wfEscapeWikiText( $id ); |
805 | } |
806 | $array[$visibility][] = [ |
807 | 'id' => "$type-$id", |
808 | 'name' => $name, |
809 | 'value' => $value |
810 | ]; |
811 | } |
812 | |
813 | /** |
814 | * Short description. Shown on Special:Search results. |
815 | * |
816 | * @stable to override |
817 | * |
818 | * @param File $file |
819 | * @return string |
820 | */ |
821 | public function getShortDesc( $file ) { |
822 | return self::getGeneralShortDesc( $file ); |
823 | } |
824 | |
825 | /** |
826 | * Long description. Shown under image on image description page surrounded by (). |
827 | * |
828 | * @stable to override |
829 | * |
830 | * @param File $file |
831 | * @return string |
832 | */ |
833 | public function getLongDesc( $file ) { |
834 | return self::getGeneralLongDesc( $file ); |
835 | } |
836 | |
837 | /** |
838 | * Used instead of getShortDesc if there is no handler registered for file. |
839 | * |
840 | * @param File $file |
841 | * @return string |
842 | */ |
843 | public static function getGeneralShortDesc( $file ) { |
844 | global $wgLang; |
845 | |
846 | return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) ); |
847 | } |
848 | |
849 | /** |
850 | * Used instead of getLongDesc if there is no handler registered for file. |
851 | * |
852 | * @param File $file |
853 | * @return string |
854 | */ |
855 | public static function getGeneralLongDesc( $file ) { |
856 | return wfMessage( 'file-info' )->sizeParams( $file->getSize() ) |
857 | ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )->parse(); |
858 | } |
859 | |
860 | /** |
861 | * Calculate the largest thumbnail width for a given original file size |
862 | * such that the thumbnail's height is at most $maxHeight. |
863 | * @param int $boxWidth Width of the thumbnail box. |
864 | * @param int $boxHeight Height of the thumbnail box. |
865 | * @param int $maxHeight Maximum height expected for the thumbnail. |
866 | * @return int |
867 | */ |
868 | public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) { |
869 | $idealWidth = $boxWidth * $maxHeight / $boxHeight; |
870 | $roundedUp = ceil( $idealWidth ); |
871 | if ( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) { |
872 | return (int)floor( $idealWidth ); |
873 | } |
874 | return $roundedUp; |
875 | } |
876 | |
877 | /** |
878 | * Shown in file history box on image description page. |
879 | * |
880 | * @stable to override |
881 | * |
882 | * @param File $file |
883 | * @return string Dimensions |
884 | */ |
885 | public function getDimensionsString( $file ) { |
886 | return ''; |
887 | } |
888 | |
889 | /** |
890 | * Modify the parser object post-transform. |
891 | * |
892 | * This is often used to do $parser->addOutputHook(), |
893 | * in order to add some javascript to render a viewer. |
894 | * See TimedMediaHandler or OggHandler for an example. |
895 | * |
896 | * @stable to override |
897 | * |
898 | * @param Parser $parser |
899 | * @param File $file |
900 | */ |
901 | public function parserTransformHook( $parser, $file ) { |
902 | } |
903 | |
904 | /** |
905 | * File validation hook called on upload. |
906 | * |
907 | * If the file at the given local path is not valid, or its MIME type does not |
908 | * match the handler class, a Status object should be returned containing |
909 | * relevant errors. |
910 | * |
911 | * @stable to override |
912 | * |
913 | * @param string $fileName The local path to the file. |
914 | * @return Status |
915 | */ |
916 | public function verifyUpload( $fileName ) { |
917 | return Status::newGood(); |
918 | } |
919 | |
920 | /** |
921 | * Check for zero-sized thumbnails. These can be generated when |
922 | * no disk space is available or some other error occurs |
923 | * |
924 | * @stable to override |
925 | * |
926 | * @param string $dstPath The location of the suspect file |
927 | * @param int $retval Return value of some shell process, file will be deleted if this is non-zero |
928 | * @return bool True if removed, false otherwise |
929 | */ |
930 | public function removeBadFile( $dstPath, $retval = 0 ) { |
931 | if ( file_exists( $dstPath ) ) { |
932 | $thumbstat = stat( $dstPath ); |
933 | if ( $thumbstat['size'] == 0 || $retval != 0 ) { |
934 | $result = unlink( $dstPath ); |
935 | |
936 | if ( $result ) { |
937 | wfDebugLog( 'thumbnail', |
938 | sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded', |
939 | $thumbstat['size'], $dstPath ) ); |
940 | } else { |
941 | wfDebugLog( 'thumbnail', |
942 | sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed', |
943 | $thumbstat['size'], $dstPath ) ); |
944 | } |
945 | |
946 | return true; |
947 | } |
948 | } |
949 | |
950 | return false; |
951 | } |
952 | |
953 | /** |
954 | * Remove files from the purge list. |
955 | * |
956 | * This is used by some video handlers to prevent ?action=purge |
957 | * from removing a transcoded video, which is expensive to |
958 | * regenerate. |
959 | * |
960 | * @see LocalFile::purgeThumbnails |
961 | * @stable to override |
962 | * |
963 | * @param array &$files |
964 | * @param array $options Purge options. Currently will always be |
965 | * an array with a single key 'forThumbRefresh' set to true. |
966 | */ |
967 | public function filterThumbnailPurgeList( &$files, $options ) { |
968 | // Do nothing |
969 | } |
970 | |
971 | /** |
972 | * True if the handler can rotate the media |
973 | * @since 1.24 non-static. From 1.21-1.23 was static |
974 | * @stable to override |
975 | * |
976 | * @return bool |
977 | */ |
978 | public function canRotate() { |
979 | return false; |
980 | } |
981 | |
982 | /** |
983 | * On supporting image formats, try to read out the low-level orientation |
984 | * of the file and return the angle that the file needs to be rotated to |
985 | * be viewed. |
986 | * |
987 | * This information is only useful when manipulating the original file; |
988 | * the width and height we normally work with is logical, and will match |
989 | * any produced output views. |
990 | * |
991 | * For files we don't know, we return 0. |
992 | * |
993 | * @stable to override |
994 | * |
995 | * @param File $file |
996 | * @return int 0, 90, 180 or 270 |
997 | */ |
998 | public function getRotation( $file ) { |
999 | return 0; |
1000 | } |
1001 | |
1002 | /** |
1003 | * Log an error that occurred in an external process |
1004 | * |
1005 | * Moved from BitmapHandler to MediaHandler with MediaWiki 1.23 |
1006 | * |
1007 | * @since 1.23 |
1008 | * @param int $retval |
1009 | * @param string $err Error reported by command. Anything longer than |
1010 | * MediaHandler::MAX_ERR_LOG_SIZE is stripped off. |
1011 | * @param string $cmd |
1012 | */ |
1013 | protected function logErrorForExternalProcess( $retval, $err, $cmd ) { |
1014 | # Keep error output limited (T59985) |
1015 | $errMessage = trim( substr( $err, 0, self::MAX_ERR_LOG_SIZE ) ); |
1016 | |
1017 | wfDebugLog( 'thumbnail', |
1018 | sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"', |
1019 | wfHostname(), $retval, $errMessage, $cmd ) ); |
1020 | } |
1021 | |
1022 | /** |
1023 | * Get list of languages file can be viewed in. |
1024 | * |
1025 | * @stable to override |
1026 | * |
1027 | * @param File $file |
1028 | * @return string[] Array of IETF language codes, or empty array if unsupported. |
1029 | * @since 1.23 |
1030 | */ |
1031 | public function getAvailableLanguages( File $file ) { |
1032 | return []; |
1033 | } |
1034 | |
1035 | /** |
1036 | * When overridden in a descendant class, returns a language code most suiting |
1037 | * |
1038 | * @stable to override |
1039 | * |
1040 | * @since 1.32 |
1041 | * |
1042 | * @param string $userPreferredLanguage IETF Language code requested |
1043 | * @param string[] $availableLanguages IETF Languages present in the file |
1044 | * @return string|null IETF Language code picked or null if not supported/available |
1045 | */ |
1046 | public function getMatchedLanguage( $userPreferredLanguage, array $availableLanguages ) { |
1047 | return null; |
1048 | } |
1049 | |
1050 | /** |
1051 | * On file types that support renderings in multiple languages, |
1052 | * which language is used by default if unspecified. |
1053 | * |
1054 | * If getAvailableLanguages returns a non-empty array, this must return |
1055 | * a valid language code. Otherwise can return null if files of this |
1056 | * type do not support alternative language renderings. |
1057 | * It can also return 'und' for explicitly requesting an undetermined language |
1058 | * |
1059 | * @stable to override |
1060 | * |
1061 | * @param File $file |
1062 | * @return string|null IETF Language code or null if multi-language not supported for filetype. |
1063 | * @since 1.23 |
1064 | */ |
1065 | public function getDefaultRenderLanguage( File $file ) { |
1066 | return null; |
1067 | } |
1068 | |
1069 | /** |
1070 | * If it's an audio file, return the length of the file. Otherwise 0. |
1071 | * |
1072 | * File::getLength() existed for a long time, but was calling a method |
1073 | * that only existed in some subclasses of this class (The TMH ones). |
1074 | * |
1075 | * @stable to override |
1076 | * |
1077 | * @param File $file |
1078 | * @return float Length in seconds |
1079 | * @since 1.23 |
1080 | */ |
1081 | public function getLength( $file ) { |
1082 | return 0.0; |
1083 | } |
1084 | |
1085 | /** |
1086 | * True if creating thumbnails from the file is large or otherwise resource-intensive. |
1087 | * @stable to override |
1088 | * |
1089 | * @param File $file |
1090 | * @return bool |
1091 | */ |
1092 | public function isExpensiveToThumbnail( $file ) { |
1093 | return false; |
1094 | } |
1095 | |
1096 | /** |
1097 | * Returns whether or not this handler supports the chained generation of thumbnails according |
1098 | * to buckets |
1099 | * @stable to override |
1100 | * |
1101 | * @return bool |
1102 | * @since 1.24 |
1103 | */ |
1104 | public function supportsBucketing() { |
1105 | return false; |
1106 | } |
1107 | |
1108 | /** |
1109 | * Returns a normalised params array for which parameters have been cleaned up for bucketing |
1110 | * purposes |
1111 | * @stable to override |
1112 | * |
1113 | * @param array $params |
1114 | * @return array |
1115 | */ |
1116 | public function sanitizeParamsForBucketing( $params ) { |
1117 | return $params; |
1118 | } |
1119 | |
1120 | /** |
1121 | * Gets configuration for the file warning message. Return value of |
1122 | * the following structure: |
1123 | * [ |
1124 | * // Required, module with messages loaded for the client |
1125 | * 'module' => 'example.filewarning.messages', |
1126 | * // Required, array of names of messages |
1127 | * 'messages' => [ |
1128 | * // Required, main warning message |
1129 | * 'main' => 'example-filewarning-main', |
1130 | * // Optional, header for warning dialog |
1131 | * 'header' => 'example-filewarning-header', |
1132 | * // Optional, footer for warning dialog |
1133 | * 'footer' => 'example-filewarning-footer', |
1134 | * // Optional, text for more-information link (see below) |
1135 | * 'info' => 'example-filewarning-info', |
1136 | * ], |
1137 | * // Optional, link for more information |
1138 | * 'link' => 'http://example.com', |
1139 | * ] |
1140 | * |
1141 | * Returns null if no warning is necessary. |
1142 | * @stable to override |
1143 | * @param File $file |
1144 | * @return array|null |
1145 | */ |
1146 | public function getWarningConfig( $file ) { |
1147 | return null; |
1148 | } |
1149 | |
1150 | /** |
1151 | * Converts a dimensions array about a potentially multipage document from an |
1152 | * exhaustive list of ordered page numbers to a list of page ranges |
1153 | * @param array[] $pagesByDimensions |
1154 | * @return string |
1155 | * @since 1.30 |
1156 | */ |
1157 | public static function getPageRangesByDimensions( $pagesByDimensions ) { |
1158 | $pageRangesByDimensions = []; |
1159 | |
1160 | foreach ( $pagesByDimensions as $dimensions => $pageList ) { |
1161 | $ranges = []; |
1162 | $firstPage = $pageList[0]; |
1163 | $lastPage = $firstPage - 1; |
1164 | |
1165 | foreach ( $pageList as $page ) { |
1166 | if ( $page > $lastPage + 1 ) { |
1167 | if ( $firstPage !== $lastPage ) { |
1168 | $ranges[] = "$firstPage-$lastPage"; |
1169 | } else { |
1170 | $ranges[] = "$firstPage"; |
1171 | } |
1172 | |
1173 | $firstPage = $page; |
1174 | } |
1175 | |
1176 | $lastPage = $page; |
1177 | } |
1178 | |
1179 | if ( $firstPage != $lastPage ) { |
1180 | $ranges[] = "$firstPage-$lastPage"; |
1181 | } else { |
1182 | $ranges[] = "$firstPage"; |
1183 | } |
1184 | |
1185 | $pageRangesByDimensions[ $dimensions ] = $ranges; |
1186 | } |
1187 | |
1188 | $dimensionsString = []; |
1189 | foreach ( $pageRangesByDimensions as $dimensions => $pageRanges ) { |
1190 | $dimensionsString[] = "$dimensions:" . implode( ',', $pageRanges ); |
1191 | } |
1192 | |
1193 | return implode( '/', $dimensionsString ); |
1194 | } |
1195 | |
1196 | /** |
1197 | * Get useful response headers for GET/HEAD requests for a file with the given metadata |
1198 | * @stable to override |
1199 | * |
1200 | * @param array $metadata Contains this handler's unserialized getMetadata() for a file |
1201 | * @return array |
1202 | * @since 1.30 |
1203 | */ |
1204 | public function getContentHeaders( $metadata ) { |
1205 | return [ 'X-Content-Dimensions' => '' ]; // T175689 |
1206 | } |
1207 | |
1208 | /** |
1209 | * If this returns true, LocalFile may split metadata up and store its |
1210 | * constituent items separately. This only makes sense if the handler calls |
1211 | * File::getMetadataItem() or File::getMetadataItems() instead of |
1212 | * requesting the whole array at once. |
1213 | * |
1214 | * @return bool |
1215 | */ |
1216 | public function useSplitMetadata() { |
1217 | return false; |
1218 | } |
1219 | } |