Index: branches/instantcommons/includes/Image.php |
— | — | @@ -1,5 +1,8 @@ |
2 | 2 | <?php |
3 | 3 | /** |
| 4 | + */ |
| 5 | + |
| 6 | +/** |
4 | 7 | * NOTE FOR WINDOWS USERS: |
5 | 8 | * To enable EXIF functions, add the folloing lines to the |
6 | 9 | * "Windows extensions" section of php.ini: |
— | — | @@ -11,16 +14,24 @@ |
12 | 15 | /** |
13 | 16 | * Bump this number when serialized cache records may be incompatible. |
14 | 17 | */ |
15 | | -define( 'MW_IMAGE_VERSION', 1 ); |
| 18 | +define( 'MW_IMAGE_VERSION', 2 ); |
16 | 19 | |
17 | 20 | /** |
18 | 21 | * Class to represent an image |
19 | 22 | * |
20 | 23 | * Provides methods to retrieve paths (physical, logical, URL), |
21 | 24 | * to generate thumbnails or for uploading. |
| 25 | + * |
| 26 | + * @addtogroup Media |
22 | 27 | */ |
23 | 28 | class Image |
24 | 29 | { |
| 30 | + const DELETED_FILE = 1; |
| 31 | + const DELETED_COMMENT = 2; |
| 32 | + const DELETED_USER = 4; |
| 33 | + const DELETED_RESTRICTED = 8; |
| 34 | + const RENDER_NOW = 1; |
| 35 | + |
25 | 36 | /**#@+ |
26 | 37 | * @private |
27 | 38 | */ |
— | — | @@ -30,8 +41,7 @@ |
31 | 42 | $title, # Title object for this image (constructor) |
32 | 43 | $fileExists, # does the image file exist on disk? (loadFromXxx) |
33 | 44 | $fromSharedDirectory, # load this image from $wgSharedUploadDirectory (loadFromXxx) |
34 | | - $fromInstantCommons, # load this image from the InstantCommons repository |
35 | | - $description, #added in for instantCommons |
| 45 | + $fromInstantCommons, # load this image from the InstantCommons server |
36 | 46 | $historyLine, # Number of line to return by nextHistoryLine() (constructor) |
37 | 47 | $historyRes, # result of the query for the image's history (nextHistoryLine) |
38 | 48 | $width, # \ |
— | — | @@ -40,6 +50,7 @@ |
41 | 51 | $attr, # / |
42 | 52 | $type, # MEDIATYPE_xxx (bitmap, drawing, audio...) |
43 | 53 | $mime, # MIME type, determined by MimeMagic::guessMimeType |
| 54 | + $extension, # The file extension (constructor) |
44 | 55 | $size, # Size in bytes (loadFromXxx) |
45 | 56 | $metadata, # Metadata |
46 | 57 | $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx) |
— | — | @@ -78,18 +89,16 @@ |
79 | 90 | } |
80 | 91 | $this->title =& $title; |
81 | 92 | $this->name = $title->getDBkey(); |
82 | | - $this->metadata = serialize ( array() ) ; |
| 93 | + $this->metadata = ''; |
83 | 94 | |
84 | 95 | $n = strrpos( $this->name, '.' ); |
85 | 96 | $this->extension = Image::normalizeExtension( $n ? |
86 | 97 | substr( $this->name, $n + 1 ) : '' ); |
87 | 98 | $this->historyLine = 0; |
88 | | - $this->page = 1; |
89 | 99 | |
90 | 100 | $this->dataLoaded = false; |
91 | 101 | } |
92 | 102 | |
93 | | - |
94 | 103 | /** |
95 | 104 | * Normalize a file extension to the common form, and ensure it's clean. |
96 | 105 | * Extensions with non-alphanumeric characters will be discarded. |
— | — | @@ -247,14 +256,13 @@ |
248 | 257 | # In case we're on a wgCapitalLinks=false wiki, we |
249 | 258 | # capitalize the first letter of the filename before |
250 | 259 | # looking it up in the shared repository. |
251 | | - $sharedImage = Image::newFromName( $wgContLang->ucfirst($this->name) ); |
| 260 | + $sharedImage = Image::newFromName( $wgContLang->ucfirst($this->name) ); |
252 | 261 | $this->fileExists = $sharedImage && file_exists( $sharedImage->getFullPath(true) ); |
253 | 262 | if ( $this->fileExists ) { |
254 | 263 | $this->name = $sharedImage->name; |
255 | 264 | $this->imagePath = $this->getFullPath(true); |
256 | 265 | $this->fromSharedDirectory = true; |
257 | 266 | } |
258 | | - |
259 | 267 | } |
260 | 268 | |
261 | 269 | |
— | — | @@ -263,32 +271,26 @@ |
264 | 272 | |
265 | 273 | $this->mime = $magic->guessMimeType($this->imagePath,true); |
266 | 274 | $this->type = $magic->getMediaType($this->imagePath,$this->mime); |
| 275 | + $handler = MediaHandler::getHandler( $this->mime ); |
267 | 276 | |
268 | 277 | # Get size in bytes |
269 | 278 | $this->size = filesize( $this->imagePath ); |
270 | 279 | |
271 | | - $magic=& MimeMagic::singleton(); |
272 | | - |
273 | | - # Height and width |
274 | | - wfSuppressWarnings(); |
275 | | - if( $this->mime == 'image/svg' ) { |
276 | | - $gis = wfGetSVGsize( $this->imagePath ); |
277 | | - } elseif( $this->mime == 'image/vnd.djvu' ) { |
278 | | - $deja = new DjVuImage( $this->imagePath ); |
279 | | - $gis = $deja->getImageSize(); |
280 | | - } elseif ( !$magic->isPHPImageType( $this->mime ) ) { |
281 | | - # Don't try to get the width and height of sound and video files, that's bad for performance |
282 | | - $gis = false; |
| 280 | + # Height, width and metadata |
| 281 | + if ( $handler ) { |
| 282 | + $gis = $handler->getImageSize( $this, $this->imagePath ); |
| 283 | + $this->metadata = $handler->getMetadata( $this, $this->imagePath ); |
283 | 284 | } else { |
284 | | - $gis = getimagesize( $this->imagePath ); |
| 285 | + $gis = false; |
| 286 | + $this->metadata = ''; |
285 | 287 | } |
286 | | - wfRestoreWarnings(); |
287 | 288 | |
288 | 289 | wfDebug(__METHOD__.': '.$this->imagePath." loaded, ".$this->size." bytes, ".$this->mime.".\n"); |
289 | 290 | } |
290 | 291 | else { |
291 | 292 | $this->mime = NULL; |
292 | 293 | $this->type = MEDIATYPE_UNKNOWN; |
| 294 | + $this->metadata = ''; |
293 | 295 | wfDebug(__METHOD__.': '.$this->imagePath." NOT FOUND!\n"); |
294 | 296 | } |
295 | 297 | |
— | — | @@ -307,13 +309,6 @@ |
308 | 310 | # as ther's only one thread of execution, this should be safe anyway. |
309 | 311 | $this->dataLoaded = true; |
310 | 312 | |
311 | | - |
312 | | - if ( $this->mime == 'image/vnd.djvu' ) { |
313 | | - $this->metadata = $deja->retrieveMetaData(); |
314 | | - } else { |
315 | | - $this->metadata = serialize( $this->retrieveExifData( $this->imagePath ) ); |
316 | | - } |
317 | | - |
318 | 313 | if ( isset( $gis['bits'] ) ) $this->bits = $gis['bits']; |
319 | 314 | else $this->bits = 0; |
320 | 315 | |
— | — | @@ -322,16 +317,16 @@ |
323 | 318 | |
324 | 319 | /** |
325 | 320 | * Load image metadata from the DB |
| 321 | + * @param $src - when using IC distinguish IC images from form images ($src='upload') |
326 | 322 | */ |
327 | 323 | function loadFromDB($src="") { |
328 | 324 | global $wgUseSharedUploads, $wgSharedUploadDBname, $wgSharedUploadDBprefix, $wgContLang, $wgUseInstantCommons, $wgInstantCommonsServerPath, $wgUploadDirectory; |
329 | 325 | wfProfileIn( __METHOD__ ); |
330 | 326 | |
331 | 327 | $dbr = wfGetDB( DB_SLAVE ); |
332 | | - $this->checkDBSchema($dbr); |
333 | 328 | |
334 | 329 | $row = $dbr->selectRow( 'image', |
335 | | - array( 'img_size', 'img_width', 'img_height', 'img_bits', |
| 330 | + array( 'img_size', 'img_width', 'img_height', 'img_bits', |
336 | 331 | 'img_media_type', 'img_major_mime', 'img_minor_mime', 'img_metadata','img_description' ), |
337 | 332 | array( 'img_name' => $this->name ), __METHOD__ ); |
338 | 333 | if ( $row ) { |
— | — | @@ -340,12 +335,8 @@ |
341 | 336 | $this->loadFromRow( $row ); |
342 | 337 | $this->imagePath = $this->getFullPath(); |
343 | 338 | // Check for rows from a previous schema, quietly upgrade them |
344 | | - // IC: When an image is uploaded in the background, it's height/width/size would have been set to 0 |
345 | | - // so force a reload from file |
346 | | - if ( is_null($this->type) || !($this->width && $this->height && $this->size)) { |
347 | | - $this->upgradeRow(); |
348 | | - } |
349 | | - } elseif ( ($wgUseSharedUploads && $wgSharedUploadDBname)) { |
| 339 | + $this->maybeUpgradeRow(); |
| 340 | + } elseif ( $wgUseSharedUploads && $wgSharedUploadDBname ) { |
350 | 341 | # In case we're on a wgCapitalLinks=false wiki, we |
351 | 342 | # capitalize the first letter of the filename before |
352 | 343 | # looking it up in the shared repository. |
— | — | @@ -365,94 +356,89 @@ |
366 | 357 | $this->loadFromRow( $row ); |
367 | 358 | |
368 | 359 | // Check for rows from a previous schema, quietly upgrade them |
369 | | - if ( is_null($this->type) ) { |
370 | | - $this->upgradeRow(); |
371 | | - } |
| 360 | + $this->maybeUpgradeRow(); |
372 | 361 | } |
373 | | - } elseif ( $wgUseInstantCommons && $wgInstantCommonsServerPath ){ |
374 | | - //NB: We enter into this loop even when we're uploading to the wiki. |
375 | | - //so skip if it's an upload |
376 | | - if($src!="upload") { |
377 | | - if($_POST['wpPreview']=='Show preview') { |
378 | | - //do not record the image. Show a placeholder image instead |
379 | | - }else{ |
380 | | - //TODO: Replace placeholder image with the file from the InstantCommonsServer |
381 | | - //store it in the image database and return an Image object It should |
382 | | - //return an object identical to a database row as above |
383 | | - |
384 | | - $rname=tempnam(wfTempDir(),'icresponse.xml'); |
385 | | - $url = $wgInstantCommonsServerPath.'/api.php?action=instantcommons&format=xml&media='.$this->name; |
386 | | - $fp = fopen($rname, "w"); |
387 | | - $xmlString = $this-> my_file_get_contents($url, $fp); |
388 | | - |
| 362 | + }elseif ( $wgUseInstantCommons && $wgInstantCommonsServerPath ){ |
| 363 | + //NB: We enter into this loop even when we're uploading to the wiki. |
| 364 | + //so skip if it's an upload |
| 365 | + if($src!="upload") { |
| 366 | + if($_POST['wpPreview']=='Show preview') { |
| 367 | + //do not record the image. Show a placeholder image instead |
| 368 | + }else{ |
| 369 | + //TODO: Replace placeholder image with the file from the InstantCommonsServer |
| 370 | + //store it in the image database and return an Image object It should |
| 371 | + //return an object identical to a database row as above |
| 372 | + $rname=tempnam(wfTempDir(),'icresponse.xml'); |
| 373 | + $url = $wgInstantCommonsServerPath.'/api.php?action=instantcommons&format=xml&media='.$this->name; |
| 374 | + $fp = fopen($rname, "w"); |
| 375 | + $xmlString = $this-> my_file_get_contents($url, $fp); |
389 | 376 | $p =& new ApiInstantCommons('instantcommons', 'maint'); |
390 | | - fclose($fp); |
391 | | - //TODO: Replace the xml-parser |
392 | | - if(trim($xmlString)!=""){ |
393 | | - $row = ($p->parse($xmlString)); |
394 | | - $row = $row[0]['children'][0]['children'][0]['attrs']; |
395 | | - } |
396 | | - if ( $row['NAME'] ) { |
397 | | - //create the local file directory ($this->mSavedFile) |
398 | | - UploadForm::saveUploadedFile( $row['NAME'], |
399 | | - $row['NAME'] |
400 | | - ); //this hack just creates the path locally |
401 | | - //now download the file to the final location |
402 | | - //TODO: This has to be done in the background! Otherwise the page |
403 | | - //hangs until the download is complete |
404 | | - |
405 | | - //TODO: As a workaround, check the size of the file returned. If greater than |
406 | | - //2.5kb (typical thumbnail size on my test wiki), show "Download in progress" |
407 | | - //image instead until the download is complete |
408 | | - $icFileUrl = $wgInstantCommonsServerPath.$row['URL']; |
409 | | - $fp = fopen("{$this->mSavedFile}", "w"); |
410 | | - if($row['SIZE'] > 3000) {wfDebug(join($row, ' | ')); |
411 | | - $this-> my_file_get_contents($icFileUrl, $this->mSavedFile, TRUE); |
412 | | - }else { |
413 | | - /* |
414 | | - $icFileUrl = $wgInstantCommonsServerPath.$row['URL']; |
415 | | - $icFp = fopen("{$this->mSavedFile}", "w"); |
416 | | - my_file_get_contents($icFileUrl, $icFp); |
417 | | - fclose($icFp);*/ |
418 | | - |
419 | | - $ch = curl_init($wgInstantCommonsServerPath.$row['URL']); |
420 | | - //$fp = fopen("{$this->mSavedFile}", "w"); |
421 | | - curl_setopt($ch, CURLOPT_FILE, $fp); |
422 | | - curl_setopt($ch, CURLOPT_HEADER, 0); |
423 | | - curl_exec($ch); |
424 | | - curl_close($ch); |
425 | | - } |
426 | | - |
427 | | - //set further properties |
428 | | - $this->fromInstantCommons = true; //TODO:THIS IS NOT STORED. For future use only! |
429 | | - $this->fileExists = true; |
430 | | - $this->imagePath = $this->getFullPath(false, true); |
431 | | - $this->width = $row['WIDTH']; |
432 | | - $this->height = $row['HEIGHT']; |
433 | | - $this->metadata = stripslashes($row['METADATA']); |
434 | | - $this->bits = $row['BITS']; |
435 | | - $this->type = $row['TYPE']; |
436 | | - $this->mime = $row['MIME']; |
437 | | - $this->size = $row['SIZE']; |
438 | | - $this->dataLoaded = $row['DATALOADED']; |
439 | | - $this->attr = $row['ATTR']; |
440 | | - $this->historyLine = $row['HISTORYLINE']; |
441 | | - $this->historyRes = $row['HISTORYRES']; |
442 | | - /** |
443 | | - * Update the upload log and create the description page |
444 | | - * if it's a new file. |
445 | | - */ |
446 | | - |
447 | | - $success = $this->recordUpload('Downloaded with InstantCommons!', $row['DESCRIPTION']); |
448 | | - if ( $success ) { |
449 | | - wfRunHooks( 'UploadComplete', array( &$this ) ); |
450 | | - //TODO: purge cache to show the image |
451 | | - } |
452 | | - } |
453 | | - } |
454 | | - } |
| 377 | + fclose($fp); |
| 378 | + //TODO: Replace the xml-parser with a preg_grep since it's a single line of XML that's returned. |
| 379 | + if(trim($xmlString)!=""){ |
| 380 | + $row = ($p->parse($xmlString)); |
| 381 | + $row = $row[0]['children'][0]['children'][0]['attrs']; |
| 382 | + } |
| 383 | + if ( $row['NAME'] ) { |
| 384 | + //create the local file directory ($this->mSavedFile) |
| 385 | + UploadForm::saveUploadedFile( $row['NAME'], |
| 386 | + $row['NAME'] |
| 387 | + ); //this hack just creates the path locally |
| 388 | + //now download the file to the final location |
| 389 | + //TODO: This has to be done in the background! Otherwise the page |
| 390 | + //hangs until the download is complete |
| 391 | + |
| 392 | + //TODO: As a workaround, check the size of the file returned. If greater than |
| 393 | + //2.5kb (typical thumbnail size on my test wiki), show "Download in progress" |
| 394 | + //image instead until the download is complete |
| 395 | + $icFileUrl = $wgInstantCommonsServerPath.$row['URL']; |
| 396 | + $fp = fopen("{$this->mSavedFile}", "w"); |
| 397 | + if($row['SIZE'] > 3000) {wfDebug(join($row, ' | ')); |
| 398 | + $this-> my_file_get_contents($icFileUrl, $this->mSavedFile, TRUE); |
| 399 | + }else { |
| 400 | + /* |
| 401 | + $icFileUrl = $wgInstantCommonsServerPath.$row['URL']; |
| 402 | + $icFp = fopen("{$this->mSavedFile}", "w"); |
| 403 | + my_file_get_contents($icFileUrl, $icFp); |
| 404 | + fclose($icFp);*/ |
| 405 | + |
| 406 | + $ch = curl_init($wgInstantCommonsServerPath.$row['URL']); |
| 407 | + //$fp = fopen("{$this->mSavedFile}", "w"); |
| 408 | + curl_setopt($ch, CURLOPT_FILE, $fp); |
| 409 | + curl_setopt($ch, CURLOPT_HEADER, 0); |
| 410 | + curl_exec($ch); |
| 411 | + curl_close($ch); |
| 412 | + } |
| 413 | + |
| 414 | + //set further properties |
| 415 | + $this->fromInstantCommons = true; //TODO:THIS IS NOT STORED. For future use only! |
| 416 | + $this->fileExists = true; |
| 417 | + $this->imagePath = $this->getFullPath(false, true); |
| 418 | + $this->width = $row['WIDTH']; |
| 419 | + $this->height = $row['HEIGHT']; |
| 420 | + $this->metadata = stripslashes($row['METADATA']); |
| 421 | + $this->bits = $row['BITS']; |
| 422 | + $this->type = $row['TYPE']; |
| 423 | + $this->mime = $row['MIME']; |
| 424 | + $this->size = $row['SIZE']; |
| 425 | + $this->dataLoaded = $row['DATALOADED']; |
| 426 | + $this->attr = $row['ATTR']; |
| 427 | + $this->historyLine = $row['HISTORYLINE']; |
| 428 | + $this->historyRes = $row['HISTORYRES']; |
| 429 | + /** |
| 430 | + * Update the upload log and create the description page |
| 431 | + * if it's a new file. |
| 432 | + */ |
| 433 | + |
| 434 | + $success = $this->recordUpload('Downloaded with InstantCommons!', $row['DESCRIPTION']); |
| 435 | + if ( $success ) { |
| 436 | + wfRunHooks( 'UploadComplete', array( &$this ) ); |
| 437 | + //TODO: purge cache to show the image |
| 438 | + } |
| 439 | + } |
| 440 | + } |
| 441 | + } |
455 | 442 | } |
456 | | - |
457 | 443 | |
458 | 444 | if ( !$row ) { |
459 | 445 | $this->size = 0; |
— | — | @@ -462,7 +448,7 @@ |
463 | 449 | $this->type = 0; |
464 | 450 | $this->fileExists = false; |
465 | 451 | $this->fromSharedDirectory = false; |
466 | | - $this->metadata = serialize ( array() ) ; |
| 452 | + $this->metadata = ''; |
467 | 453 | $this->mime = false; |
468 | 454 | } |
469 | 455 | |
— | — | @@ -470,23 +456,7 @@ |
471 | 457 | $this->dataLoaded = true; |
472 | 458 | wfProfileOut( __METHOD__ ); |
473 | 459 | } |
474 | | -function my_file_get_contents($url, $fp, $bg=FALSE, $timeout = 1){//ref: http://groups-beta.google.com/group/comp.lang.php/browse_thread/thread/8efbbaced3c45e3c/d63c7891cf8e380b?lnk=raot |
475 | | - if(!$bg){ |
476 | | - $ch = curl_init(); |
477 | | - curl_setopt ($ch, CURLOPT_URL, $url); |
478 | | - curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); |
479 | | - curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout); |
480 | | - curl_setopt ($ch, CURLOPT_TIMEOUT, $timeout); |
481 | | - $file_contents = curl_exec($ch); |
482 | | - curl_close($ch); |
483 | | - } else {//call curl in the background to download the file |
484 | | - $cmd = 'curl '.escapeshellcmd($url).' -o '.$fp.'&'; |
485 | | - wfDebug('Curl download initiated='.$cmd ); |
486 | | - pclose(popen($cmd, 'r')); |
487 | | - $file_contents = 1; |
488 | | - } |
489 | | - return $file_contents; |
490 | | - } |
| 460 | + |
491 | 461 | /* |
492 | 462 | * Load image metadata from a DB result row |
493 | 463 | */ |
— | — | @@ -497,7 +467,7 @@ |
498 | 468 | $this->bits = $row->img_bits; |
499 | 469 | $this->type = $row->img_media_type; |
500 | 470 | $this->description = $row->img_description; |
501 | | - |
| 471 | + |
502 | 472 | $major= $row->img_major_mime; |
503 | 473 | $minor= $row->img_minor_mime; |
504 | 474 | |
— | — | @@ -506,23 +476,22 @@ |
507 | 477 | if (!$minor) $minor= "unknown"; |
508 | 478 | $this->mime = $major.'/'.$minor; |
509 | 479 | } |
510 | | - |
511 | 480 | $this->metadata = $row->img_metadata; |
512 | | - if ( $this->metadata == "" ) $this->metadata = serialize ( array() ) ; |
513 | 481 | |
514 | 482 | $this->dataLoaded = true; |
515 | 483 | } |
516 | 484 | |
517 | 485 | /** |
518 | 486 | * Load image metadata from cache or DB, unless already loaded |
| 487 | + * @param $src - when using IC distinguish IC images from form images ($src='upload') |
519 | 488 | */ |
520 | 489 | function load($src="") { |
521 | 490 | global $wgSharedUploadDBname, $wgUseSharedUploads, $wgUseInstantCommons; |
522 | | - if ( !$this->dataLoaded ) { |
| 491 | + if ( !$this->dataLoaded ) { |
523 | 492 | if ( !$this->loadFromCache() ) { |
524 | 493 | $this->loadFromDB($src); |
525 | 494 | if ( !$wgSharedUploadDBname && $wgUseSharedUploads ) { |
526 | | - $this->loadFromFile(); |
| 495 | + $this->loadFromFile(); |
527 | 496 | } elseif ( $this->fileExists || !$wgUseSharedUploads ) { |
528 | 497 | // We can do negative caching for local images, because the cache |
529 | 498 | // will be purged on upload. But we can't do it when shared images |
— | — | @@ -535,8 +504,23 @@ |
536 | 505 | } |
537 | 506 | |
538 | 507 | /** |
539 | | - * Metadata was loaded from the database, but the row had a marker indicating it needs to be |
540 | | - * upgraded from the 1.4 schema, which had no width, height, bits or type. Upgrade the row. |
| 508 | + * Upgrade a row if it needs it |
| 509 | + * IC: When an image is uploaded in the background, it's height/width/size would have been set to 0 |
| 510 | + * so force a reload from file |
| 511 | + */ |
| 512 | + function maybeUpgradeRow() { |
| 513 | + if ( is_null($this->type) || $this->mime == 'image/svg' || !($this->width && $this->height && $this->size)) { |
| 514 | + $this->upgradeRow(); |
| 515 | + } else { |
| 516 | + $handler = $this->getHandler(); |
| 517 | + if ( $handler && !$handler->isMetadataValid( $this, $this->metadata ) ) { |
| 518 | + $this->upgradeRow(); |
| 519 | + } |
| 520 | + } |
| 521 | + } |
| 522 | + |
| 523 | + /** |
| 524 | + * Fix assorted version-related problems with the image row by reloading it from the file |
541 | 525 | */ |
542 | 526 | function upgradeRow() { |
543 | 527 | global $wgDBname, $wgSharedUploadDBname; |
— | — | @@ -557,11 +541,9 @@ |
558 | 542 | $dbw = wfGetDB( DB_MASTER ); |
559 | 543 | } |
560 | 544 | |
561 | | - $this->checkDBSchema($dbw); |
562 | | - |
563 | 545 | list( $major, $minor ) = self::splitMime( $this->mime ); |
564 | 546 | |
565 | | - wfDebug(__METHOD__.': upgrading '.$this->name." to 1.5 schema\n"); |
| 547 | + wfDebug(__METHOD__.': upgrading '.$this->name." to the current schema\n"); |
566 | 548 | |
567 | 549 | $dbw->update( 'image', |
568 | 550 | array(//IC: include image size |
— | — | @@ -655,26 +637,52 @@ |
656 | 638 | /** |
657 | 639 | * Return the width of the image |
658 | 640 | * |
659 | | - * Returns -1 if the file specified is not a known image type |
| 641 | + * Returns false on error |
660 | 642 | * @public |
661 | 643 | */ |
662 | | - function getWidth() { |
| 644 | + function getWidth( $page = 1 ) { |
663 | 645 | $this->load(); |
664 | | - return $this->width; |
| 646 | + if ( $this->isMultipage() ) { |
| 647 | + $dim = $this->getHandler()->getPageDimensions( $this, $page ); |
| 648 | + if ( $dim ) { |
| 649 | + return $dim['width']; |
| 650 | + } else { |
| 651 | + return false; |
| 652 | + } |
| 653 | + } else { |
| 654 | + return $this->width; |
| 655 | + } |
665 | 656 | } |
666 | 657 | |
667 | 658 | /** |
668 | 659 | * Return the height of the image |
669 | 660 | * |
670 | | - * Returns -1 if the file specified is not a known image type |
| 661 | + * Returns false on error |
671 | 662 | * @public |
672 | 663 | */ |
673 | | - function getHeight() { |
| 664 | + function getHeight( $page = 1 ) { |
674 | 665 | $this->load(); |
675 | | - return $this->height; |
| 666 | + if ( $this->isMultipage() ) { |
| 667 | + $dim = $this->getHandler()->getPageDimensions( $this, $page ); |
| 668 | + if ( $dim ) { |
| 669 | + return $dim['height']; |
| 670 | + } else { |
| 671 | + return false; |
| 672 | + } |
| 673 | + } else { |
| 674 | + return $this->height; |
| 675 | + } |
676 | 676 | } |
677 | 677 | |
678 | 678 | /** |
| 679 | + * Get handler-specific metadata |
| 680 | + */ |
| 681 | + function getMetadata() { |
| 682 | + $this->load(); |
| 683 | + return $this->metadata; |
| 684 | + } |
| 685 | + |
| 686 | + /** |
679 | 687 | * Return the size of the image file, in bytes |
680 | 688 | * @public |
681 | 689 | */ |
— | — | @@ -711,58 +719,10 @@ |
712 | 720 | * @todo remember the result of this check. |
713 | 721 | */ |
714 | 722 | function canRender() { |
715 | | - global $wgUseImageMagick, $wgDjvuRenderer; |
716 | | - |
717 | | - if( $this->getWidth()<=0 || $this->getHeight()<=0 ) return false; |
718 | | - |
719 | | - $mime= $this->getMimeType(); |
720 | | - |
721 | | - if (!$mime || $mime==='unknown' || $mime==='unknown/unknown') return false; |
722 | | - |
723 | | - #if it's SVG, check if there's a converter enabled |
724 | | - if ($mime === 'image/svg') { |
725 | | - global $wgSVGConverters, $wgSVGConverter; |
726 | | - |
727 | | - if ($wgSVGConverter && isset( $wgSVGConverters[$wgSVGConverter])) { |
728 | | - wfDebug( "Image::canRender: SVG is ready!\n" ); |
729 | | - return true; |
730 | | - } else { |
731 | | - wfDebug( "Image::canRender: SVG renderer missing\n" ); |
732 | | - } |
733 | | - } |
734 | | - |
735 | | - #image formats available on ALL browsers |
736 | | - if ( $mime === 'image/gif' |
737 | | - || $mime === 'image/png' |
738 | | - || $mime === 'image/jpeg' ) return true; |
739 | | - |
740 | | - #image formats that can be converted to the above formats |
741 | | - if ($wgUseImageMagick) { |
742 | | - #convertable by ImageMagick (there are more...) |
743 | | - if ( $mime === 'image/vnd.wap.wbmp' |
744 | | - || $mime === 'image/x-xbitmap' |
745 | | - || $mime === 'image/x-xpixmap' |
746 | | - #|| $mime === 'image/x-icon' #file may be split into multiple parts |
747 | | - || $mime === 'image/x-portable-anymap' |
748 | | - || $mime === 'image/x-portable-bitmap' |
749 | | - || $mime === 'image/x-portable-graymap' |
750 | | - || $mime === 'image/x-portable-pixmap' |
751 | | - #|| $mime === 'image/x-photoshop' #this takes a lot of CPU and RAM! |
752 | | - || $mime === 'image/x-rgb' |
753 | | - || $mime === 'image/x-bmp' |
754 | | - || $mime === 'image/tiff' ) return true; |
755 | | - } |
756 | | - else { |
757 | | - #convertable by the PHP GD image lib |
758 | | - if ( $mime === 'image/vnd.wap.wbmp' |
759 | | - || $mime === 'image/x-xbitmap' ) return true; |
760 | | - } |
761 | | - if ( $mime === 'image/vnd.djvu' && isset( $wgDjvuRenderer ) && $wgDjvuRenderer ) return true; |
762 | | - |
763 | | - return false; |
| 723 | + $handler = $this->getHandler(); |
| 724 | + return $handler && $handler->canRender(); |
764 | 725 | } |
765 | 726 | |
766 | | - |
767 | 727 | /** |
768 | 728 | * Return true if the file is of a type that can't be directly |
769 | 729 | * rendered by typical browsers and needs to be re-rasterized. |
— | — | @@ -774,13 +734,8 @@ |
775 | 735 | * @return bool |
776 | 736 | */ |
777 | 737 | function mustRender() { |
778 | | - $mime= $this->getMimeType(); |
779 | | - |
780 | | - if ( $mime === "image/gif" |
781 | | - || $mime === "image/png" |
782 | | - || $mime === "image/jpeg" ) return false; |
783 | | - |
784 | | - return true; |
| 738 | + $handler = $this->getHandler(); |
| 739 | + return $handler && $handler->mustRender(); |
785 | 740 | } |
786 | 741 | |
787 | 742 | /** |
— | — | @@ -846,15 +801,7 @@ |
847 | 802 | * @public |
848 | 803 | */ |
849 | 804 | function getEscapeLocalURL( $query=false) { |
850 | | - $this->getTitle(); |
851 | | - if ( $query === false ) { |
852 | | - if ( $this->page != 1 ) { |
853 | | - $query = 'page=' . $this->page; |
854 | | - } else { |
855 | | - $query = ''; |
856 | | - } |
857 | | - } |
858 | | - return $this->title->escapeLocalURL( $query ); |
| 805 | + return $this->getTitle()->escapeLocalURL( $query ); |
859 | 806 | } |
860 | 807 | |
861 | 808 | /** |
— | — | @@ -871,17 +818,17 @@ |
872 | 819 | * |
873 | 820 | * @param string $name Name of the image, without the leading "Image:" |
874 | 821 | * @param boolean $fromSharedDirectory Should this be in $wgSharedUploadPath? |
| 822 | + * @param boolean $fromInstantCommons Indicate whether this is an IC image |
875 | 823 | * @return string URL of $name image |
876 | 824 | * @public |
877 | | - * @static |
878 | 825 | */ |
879 | | - function imageUrl( $name, $fromSharedDirectory = false, $fromInstantCommons = false ) { |
| 826 | + static function imageUrl( $name, $fromSharedDirectory = false, $fromInstantCommons = false ) { |
880 | 827 | global $wgUploadPath,$wgUploadBaseUrl,$wgSharedUploadPath, $wgInstantCommonsServerPath; |
881 | 828 | if($fromInstantCommons) { //check if this is set first before checking shared directory |
882 | 829 | $base = ''; |
883 | 830 | $path = $wgInstantCommonsServerPath; |
884 | 831 | } |
885 | | - else if($fromSharedDirectory) { |
| 832 | + if($fromSharedDirectory) { |
886 | 833 | $base = ''; |
887 | 834 | $path = $wgSharedUploadPath; |
888 | 835 | } else { |
— | — | @@ -897,7 +844,7 @@ |
898 | 845 | * @return boolean Whether image file exist on disk. |
899 | 846 | * @public |
900 | 847 | */ |
901 | | - function exists($src="") { |
| 848 | + function exists($src="") { |
902 | 849 | $this->load($src); |
903 | 850 | return $this->fileExists; |
904 | 851 | } |
— | — | @@ -906,74 +853,83 @@ |
907 | 854 | * @todo document |
908 | 855 | * @private |
909 | 856 | */ |
910 | | - function thumbUrl( $width, $subdir='thumb') { |
| 857 | + function thumbUrlFromName( $thumbName, $subdir = 'thumb' ) { |
911 | 858 | global $wgUploadPath, $wgUploadBaseUrl, $wgSharedUploadPath, $wgInstantCommonsServerPath; |
912 | | - global $wgSharedThumbnailScriptPath, $wgThumbnailScriptPath; |
| 859 | + if($this->fromSharedDirectory) { |
| 860 | + $base = ''; |
| 861 | + $path = $wgSharedUploadPath; |
| 862 | + } else { |
| 863 | + $base = $wgUploadBaseUrl; |
| 864 | + $path = $wgUploadPath; |
| 865 | + } |
| 866 | + if ( Image::isHashed( $this->fromSharedDirectory ) ) { |
| 867 | + $hashdir = wfGetHashPath($this->name, $this->fromSharedDirectory, $this->fromInstantCommons ) . |
| 868 | + wfUrlencode( $this->name ); |
| 869 | + } else { |
| 870 | + $hashdir = ''; |
| 871 | + } |
| 872 | + $url = "{$base}{$path}/{$subdir}{$hashdir}/" . wfUrlencode( $thumbName ); |
| 873 | + return $url; |
| 874 | + } |
913 | 875 | |
914 | | - // Generate thumb.php URL if possible |
915 | | - $script = false; |
916 | | - $url = false; |
| 876 | + /** |
| 877 | + * @deprecated Use $image->transform()->getUrl() or thumbUrlFromName() |
| 878 | + */ |
| 879 | + function thumbUrl( $width, $subdir = 'thumb' ) { |
| 880 | + $name = $this->thumbName( array( 'width' => $width ) ); |
| 881 | + if ( strval( $name ) !== '' ) { |
| 882 | + return array( false, $this->thumbUrlFromName( $name, $subdir ) ); |
| 883 | + } else { |
| 884 | + return array( false, false ); |
| 885 | + } |
| 886 | + } |
917 | 887 | |
| 888 | + function getTransformScript() { |
| 889 | + global $wgSharedThumbnailScriptPath, $wgThumbnailScriptPath; |
918 | 890 | if ( $this->fromSharedDirectory ) { |
919 | | - if ( $wgSharedThumbnailScriptPath ) { |
920 | | - $script = $wgSharedThumbnailScriptPath; |
921 | | - } |
| 891 | + $script = $wgSharedThumbnailScriptPath; |
922 | 892 | } else { |
923 | | - if ( $wgThumbnailScriptPath ) { |
924 | | - $script = $wgThumbnailScriptPath; |
925 | | - } |
| 893 | + $script = $wgThumbnailScriptPath; |
926 | 894 | } |
927 | 895 | if ( $script ) { |
928 | | - $url = $script . '?f=' . urlencode( $this->name ) . '&w=' . urlencode( $width ); |
929 | | - if( $this->mustRender() ) { |
930 | | - $url.= '&r=1'; |
931 | | - } |
| 896 | + return "$script?f=" . urlencode( $this->name ); |
932 | 897 | } else { |
933 | | - $name = $this->thumbName( $width ); |
934 | | - if($this->fromSharedDirectory) { |
935 | | - $base = ''; |
936 | | - $path = $wgSharedUploadPath; |
937 | | - } else { |
938 | | - $base = $wgUploadBaseUrl; |
939 | | - $path = $wgUploadPath; |
940 | | - } |
941 | | - if ( Image::isHashed( $this->fromSharedDirectory ) ) { |
942 | | - $url = "{$base}{$path}/{$subdir}" . |
943 | | - wfGetHashPath($this->name, $this->fromSharedDirectory, $this->fromInstantCommons) |
944 | | - . $this->name.'/'.$name; |
945 | | - $url = wfUrlencode( $url ); |
946 | | - } else { |
947 | | - $url = "{$base}{$path}/{$subdir}/{$name}"; |
948 | | - } |
| 898 | + return false; |
949 | 899 | } |
950 | | - return array( $script !== false, $url ); |
951 | 900 | } |
952 | 901 | |
953 | 902 | /** |
954 | | - * Return the file name of a thumbnail of the specified width |
| 903 | + * Get a ThumbnailImage which is the same size as the source |
| 904 | + */ |
| 905 | + function getUnscaledThumb( $page = false ) { |
| 906 | + if ( $page ) { |
| 907 | + $params = array( |
| 908 | + 'page' => $page, |
| 909 | + 'width' => $this->getWidth( $page ) |
| 910 | + ); |
| 911 | + } else { |
| 912 | + $params = array( 'width' => $this->getWidth() ); |
| 913 | + } |
| 914 | + return $this->transform( $params ); |
| 915 | + } |
| 916 | + |
| 917 | + /** |
| 918 | + * Return the file name of a thumbnail with the specified parameters |
955 | 919 | * |
956 | | - * @param integer $width Width of the thumbnail image |
957 | | - * @param boolean $shared Does the thumbnail come from the shared repository? |
| 920 | + * @param array $params Handler-specific parameters |
958 | 921 | * @private |
959 | 922 | */ |
960 | | - function thumbName( $width ) { |
961 | | - $thumb = $width."px-".$this->name; |
962 | | - if ( $this->page != 1 ) { |
963 | | - $thumb = "page{$this->page}-$thumb"; |
| 923 | + function thumbName( $params ) { |
| 924 | + $handler = $this->getHandler(); |
| 925 | + if ( !$handler ) { |
| 926 | + return null; |
964 | 927 | } |
965 | | - |
966 | | - if( $this->mustRender() ) { |
967 | | - if( $this->canRender() ) { |
968 | | - # Rasterize to PNG (for SVG vector images, etc) |
969 | | - $thumb .= '.png'; |
970 | | - } |
971 | | - else { |
972 | | - #should we use iconThumb here to get a symbolic thumbnail? |
973 | | - #or should we fail with an internal error? |
974 | | - return NULL; //can't make bitmap |
975 | | - } |
| 928 | + list( $thumbExt, /* $thumbMime */ ) = self::getThumbType( $this->extension, $this->mime ); |
| 929 | + $thumbName = $handler->makeParamString( $params ) . '-' . $this->name; |
| 930 | + if ( $thumbExt != $this->extension ) { |
| 931 | + $thumbName .= ".$thumbExt"; |
976 | 932 | } |
977 | | - return $thumb; |
| 933 | + return $thumbName; |
978 | 934 | } |
979 | 935 | |
980 | 936 | /** |
— | — | @@ -992,9 +948,13 @@ |
993 | 949 | * @param integer $height maximum height of the image (optional) |
994 | 950 | * @public |
995 | 951 | */ |
996 | | - function createThumb( $width, $height=-1 ) { |
997 | | - $thumb = $this->getThumbnail( $width, $height ); |
998 | | - if( is_null( $thumb ) ) return ''; |
| 952 | + function createThumb( $width, $height = -1 ) { |
| 953 | + $params = array( 'width' => $width ); |
| 954 | + if ( $height != -1 ) { |
| 955 | + $params['height'] = $height; |
| 956 | + } |
| 957 | + $thumb = $this->transform( $params ); |
| 958 | + if( is_null( $thumb ) || $thumb->isError() ) return ''; |
999 | 959 | return $thumb->getUrl(); |
1000 | 960 | } |
1001 | 961 | |
— | — | @@ -1012,149 +972,92 @@ |
1013 | 973 | * |
1014 | 974 | * @return ThumbnailImage or null on failure |
1015 | 975 | * @public |
| 976 | + * |
| 977 | + * @deprecated use transform() |
1016 | 978 | */ |
1017 | 979 | function getThumbnail( $width, $height=-1, $render = true ) { |
1018 | | - wfProfileIn( __METHOD__ ); |
1019 | | - if ($this->canRender()) { |
1020 | | - if ( $height > 0 ) { |
1021 | | - $this->load(); |
1022 | | - if ( $width > $this->width * $height / $this->height ) { |
1023 | | - $width = wfFitBoxWidth( $this->width, $this->height, $height ); |
1024 | | - } |
1025 | | - } |
1026 | | - if ( $render ) { |
1027 | | - $thumb = $this->renderThumb( $width ); |
1028 | | - } else { |
1029 | | - // Don't render, just return the URL |
1030 | | - if ( $this->validateThumbParams( $width, $height ) ) { |
1031 | | - if ( !$this->mustRender() && $width == $this->width && $height == $this->height ) { |
1032 | | - $url = $this->getURL(); |
1033 | | - } else { |
1034 | | - list( /* $isScriptUrl */, $url ) = $this->thumbUrl( $width ); |
1035 | | - } |
1036 | | - $thumb = new ThumbnailImage( $url, $width, $height ); |
1037 | | - } else { |
1038 | | - $thumb = null; |
1039 | | - } |
1040 | | - } |
1041 | | - } else { |
1042 | | - // not a bitmap or renderable image, don't try. |
1043 | | - $thumb = $this->iconThumb(); |
| 980 | + $params = array( 'width' => $width ); |
| 981 | + if ( $height != -1 ) { |
| 982 | + $params['height'] = $height; |
1044 | 983 | } |
1045 | | - wfProfileOut( __METHOD__ ); |
1046 | | - return $thumb; |
| 984 | + $flags = $render ? self::RENDER_NOW : 0; |
| 985 | + return $this->transform( $params, $flags ); |
1047 | 986 | } |
1048 | | - |
| 987 | + |
1049 | 988 | /** |
1050 | | - * @return ThumbnailImage |
| 989 | + * Transform a media file |
| 990 | + * |
| 991 | + * @param array $params An associative array of handler-specific parameters. Typical |
| 992 | + * keys are width, height and page. |
| 993 | + * @param integer $flags A bitfield, may contain self::RENDER_NOW to force rendering |
| 994 | + * @return MediaTransformOutput |
1051 | 995 | */ |
1052 | | - function iconThumb() { |
1053 | | - global $wgStylePath, $wgStyleDirectory; |
| 996 | + function transform( $params, $flags = 0 ) { |
| 997 | + global $wgGenerateThumbnailOnParse, $wgUseSquid, $wgIgnoreImageErrors; |
1054 | 998 | |
1055 | | - $try = array( 'fileicon-' . $this->extension . '.png', 'fileicon.png' ); |
1056 | | - foreach( $try as $icon ) { |
1057 | | - $path = '/common/images/icons/' . $icon; |
1058 | | - $filepath = $wgStyleDirectory . $path; |
1059 | | - if( file_exists( $filepath ) ) { |
1060 | | - return new ThumbnailImage( $wgStylePath . $path, 120, 120 ); |
| 999 | + wfProfileIn( __METHOD__ ); |
| 1000 | + do { |
| 1001 | + $handler = $this->getHandler(); |
| 1002 | + if ( !$handler || !$handler->canRender() ) { |
| 1003 | + // not a bitmap or renderable image, don't try. |
| 1004 | + $thumb = $this->iconThumb(); |
| 1005 | + break; |
1061 | 1006 | } |
1062 | | - } |
1063 | | - return null; |
1064 | | - } |
1065 | 1007 | |
1066 | | - /** |
1067 | | - * Validate thumbnail parameters and fill in the correct height |
1068 | | - * |
1069 | | - * @param integer &$width Specified width (input/output) |
1070 | | - * @param integer &$height Height (output only) |
1071 | | - * @return false to indicate that an error should be returned to the user. |
1072 | | - */ |
1073 | | - function validateThumbParams( &$width, &$height ) { |
1074 | | - global $wgSVGMaxSize, $wgMaxImageArea; |
| 1008 | + $script = $this->getTransformScript(); |
| 1009 | + if ( $script && !($flags & self::RENDER_NOW) ) { |
| 1010 | + // Use a script to transform on client request |
| 1011 | + $thumb = $handler->getScriptedTransform( $this, $script, $params ); |
| 1012 | + break; |
| 1013 | + } |
1075 | 1014 | |
1076 | | - $this->load(); |
| 1015 | + $normalisedParams = $params; |
| 1016 | + $handler->normaliseParams( $this, $normalisedParams ); |
| 1017 | + list( $thumbExt, $thumbMime ) = self::getThumbType( $this->extension, $this->mime ); |
| 1018 | + $thumbName = $this->thumbName( $normalisedParams ); |
| 1019 | + $thumbPath = wfImageThumbDir( $this->name, $this->fromSharedDirectory ) . "/$thumbName"; |
| 1020 | + $thumbUrl = $this->thumbUrlFromName( $thumbName ); |
1077 | 1021 | |
1078 | | - if ( ! $this->exists() ) |
1079 | | - { |
1080 | | - # If there is no image, there will be no thumbnail |
1081 | | - return false; |
1082 | | - } |
1083 | 1022 | |
1084 | | - $width = intval( $width ); |
| 1023 | + if ( !$wgGenerateThumbnailOnParse && !($flags & self::RENDER_NOW ) ) { |
| 1024 | + $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); |
| 1025 | + break; |
| 1026 | + } |
| 1027 | + |
| 1028 | + wfDebug( "Doing stat for $thumbPath\n" ); |
| 1029 | + $this->migrateThumbFile( $thumbName ); |
| 1030 | + if ( file_exists( $thumbPath ) ) { |
| 1031 | + $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); |
| 1032 | + break; |
| 1033 | + } |
1085 | 1034 | |
1086 | | - # Sanity check $width |
1087 | | - if( $width <= 0 || $this->width <= 0) { |
1088 | | - # BZZZT |
1089 | | - return false; |
1090 | | - } |
| 1035 | + $thumb = $handler->doTransform( $this, $thumbPath, $thumbUrl, $params ); |
1091 | 1036 | |
1092 | | - # Don't thumbnail an image so big that it will fill hard drives and send servers into swap |
1093 | | - # JPEG has the handy property of allowing thumbnailing without full decompression, so we make |
1094 | | - # an exception for it. |
1095 | | - if ( $this->getMediaType() == MEDIATYPE_BITMAP && |
1096 | | - $this->getMimeType() !== 'image/jpeg' && |
1097 | | - $this->width * $this->height > $wgMaxImageArea ) |
1098 | | - { |
1099 | | - return false; |
1100 | | - } |
| 1037 | + // Ignore errors if requested |
| 1038 | + if ( !$thumb ) { |
| 1039 | + $thumb = null; |
| 1040 | + } elseif ( $thumb->isError() ) { |
| 1041 | + $this->lastError = $thumb->toText(); |
| 1042 | + if ( $wgIgnoreImageErrors && !($flags & self::RENDER_NOW) ) { |
| 1043 | + $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); |
| 1044 | + } |
| 1045 | + } |
| 1046 | + |
| 1047 | + if ( $wgUseSquid ) { |
| 1048 | + wfPurgeSquidServers( array( $thumbUrl ) ); |
| 1049 | + } |
| 1050 | + } while (false); |
1101 | 1051 | |
1102 | | - # Don't make an image bigger than the source, or wgMaxSVGSize for SVGs |
1103 | | - if ( $this->mustRender() ) { |
1104 | | - $width = min( $width, $wgSVGMaxSize ); |
1105 | | - } elseif ( $width > $this->width - 1 ) { |
1106 | | - $width = $this->width; |
1107 | | - $height = $this->height; |
1108 | | - return true; |
1109 | | - } |
1110 | | - |
1111 | | - $height = round( $this->height * $width / $this->width ); |
1112 | | - return true; |
| 1052 | + wfProfileOut( __METHOD__ ); |
| 1053 | + return $thumb; |
1113 | 1054 | } |
1114 | 1055 | |
1115 | 1056 | /** |
1116 | | - * Create a thumbnail of the image having the specified width. |
1117 | | - * The thumbnail will not be created if the width is larger than the |
1118 | | - * image's width. Let the browser do the scaling in this case. |
1119 | | - * The thumbnail is stored on disk and is only computed if the thumbnail |
1120 | | - * file does not exist OR if it is older than the image. |
1121 | | - * Returns an object which can return the pathname, URL, and physical |
1122 | | - * pixel size of the thumbnail -- or null on failure. |
1123 | | - * |
1124 | | - * @return ThumbnailImage or null on failure |
1125 | | - * @private |
| 1057 | + * Fix thumbnail files from 1.4 or before, with extreme prejudice |
1126 | 1058 | */ |
1127 | | - function renderThumb( $width, $useScript = true ) { |
1128 | | - global $wgUseSquid, $wgThumbnailEpoch; |
1129 | | - |
1130 | | - wfProfileIn( __METHOD__ ); |
1131 | | - |
1132 | | - $this->load(); |
1133 | | - $height = -1; |
1134 | | - if ( !$this->validateThumbParams( $width, $height ) ) { |
1135 | | - # Validation error |
1136 | | - wfProfileOut( __METHOD__ ); |
1137 | | - return null; |
1138 | | - } |
1139 | | - |
1140 | | - if ( !$this->mustRender() && $width == $this->width && $height == $this->height ) { |
1141 | | - # validateThumbParams (or the user) wants us to return the unscaled image |
1142 | | - $thumb = new ThumbnailImage( $this->getURL(), $width, $height ); |
1143 | | - wfProfileOut( __METHOD__ ); |
1144 | | - return $thumb; |
1145 | | - } |
1146 | | - |
1147 | | - list( $isScriptUrl, $url ) = $this->thumbUrl( $width ); |
1148 | | - if ( $isScriptUrl && $useScript ) { |
1149 | | - // Use thumb.php to render the image |
1150 | | - $thumb = new ThumbnailImage( $url, $width, $height ); |
1151 | | - wfProfileOut( __METHOD__ ); |
1152 | | - return $thumb; |
1153 | | - } |
1154 | | - |
1155 | | - $thumbName = $this->thumbName( $width, $this->fromSharedDirectory ); |
| 1059 | + function migrateThumbFile( $thumbName ) { |
1156 | 1060 | $thumbDir = wfImageThumbDir( $this->name, $this->fromSharedDirectory ); |
1157 | | - $thumbPath = $thumbDir.'/'.$thumbName; |
1158 | | - |
| 1061 | + $thumbPath = "$thumbDir/$thumbName"; |
1159 | 1062 | if ( is_dir( $thumbPath ) ) { |
1160 | 1063 | // Directory where file should be |
1161 | 1064 | // This happened occasionally due to broken migration code in 1.5 |
— | — | @@ -1167,254 +1070,50 @@ |
1168 | 1071 | break; |
1169 | 1072 | } |
1170 | 1073 | } |
1171 | | - // Code below will ask if it exists, and the answer is now no |
| 1074 | + // Doesn't exist anymore |
1172 | 1075 | clearstatcache(); |
1173 | 1076 | } |
1174 | | - |
1175 | | - $done = true; |
1176 | | - if ( !file_exists( $thumbPath ) || |
1177 | | - filemtime( $thumbPath ) < wfTimestamp( TS_UNIX, $wgThumbnailEpoch ) ) |
1178 | | - { |
1179 | | - // Create the directory if it doesn't exist |
1180 | | - if ( is_file( $thumbDir ) ) { |
1181 | | - // File where thumb directory should be, destroy if possible |
1182 | | - @unlink( $thumbDir ); |
1183 | | - } |
1184 | | - wfMkdirParents( $thumbDir ); |
1185 | | - |
1186 | | - $oldThumbPath = wfDeprecatedThumbDir( $thumbName, 'thumb', $this->fromSharedDirectory ). |
1187 | | - '/'.$thumbName; |
1188 | | - $done = false; |
1189 | | - |
1190 | | - // Migration from old directory structure |
1191 | | - if ( is_file( $oldThumbPath ) ) { |
1192 | | - if ( filemtime($oldThumbPath) >= filemtime($this->imagePath) ) { |
1193 | | - if ( file_exists( $thumbPath ) ) { |
1194 | | - if ( !is_dir( $thumbPath ) ) { |
1195 | | - // Old image in the way of rename |
1196 | | - unlink( $thumbPath ); |
1197 | | - } else { |
1198 | | - // This should have been dealt with already |
1199 | | - throw new MWException( "Directory where image should be: $thumbPath" ); |
1200 | | - } |
1201 | | - } |
1202 | | - // Rename the old image into the new location |
1203 | | - rename( $oldThumbPath, $thumbPath ); |
1204 | | - $done = true; |
1205 | | - } else { |
1206 | | - unlink( $oldThumbPath ); |
1207 | | - } |
1208 | | - } |
1209 | | - if ( !$done ) { |
1210 | | - $this->lastError = $this->reallyRenderThumb( $thumbPath, $width, $height ); |
1211 | | - if ( $this->lastError === true ) { |
1212 | | - $done = true; |
1213 | | - } elseif( $GLOBALS['wgIgnoreImageErrors'] ) { |
1214 | | - // Log the error but output anyway. |
1215 | | - // With luck it's a transitory error... |
1216 | | - $done = true; |
1217 | | - } |
1218 | | - |
1219 | | - # Purge squid |
1220 | | - # This has to be done after the image is updated and present for all machines on NFS, |
1221 | | - # or else the old version might be stored into the squid again |
1222 | | - if ( $wgUseSquid ) { |
1223 | | - $urlArr = array( $url ); |
1224 | | - wfPurgeSquidServers($urlArr); |
1225 | | - } |
1226 | | - } |
| 1077 | + if ( is_file( $thumbDir ) ) { |
| 1078 | + // File where directory should be |
| 1079 | + unlink( $thumbDir ); |
| 1080 | + // Doesn't exist anymore |
| 1081 | + clearstatcache(); |
1227 | 1082 | } |
| 1083 | + } |
1228 | 1084 | |
1229 | | - if ( $done ) { |
1230 | | - $thumb = new ThumbnailImage( $url, $width, $height, $thumbPath ); |
1231 | | - } else { |
1232 | | - $thumb = null; |
1233 | | - } |
1234 | | - wfProfileOut( __METHOD__ ); |
1235 | | - return $thumb; |
1236 | | - } // END OF function renderThumb |
| 1085 | + /** |
| 1086 | + * Get a MediaHandler instance for this image |
| 1087 | + */ |
| 1088 | + function getHandler() { |
| 1089 | + return MediaHandler::getHandler( $this->getMimeType() ); |
| 1090 | + } |
1237 | 1091 | |
1238 | 1092 | /** |
1239 | | - * Really render a thumbnail |
1240 | | - * Call this only for images for which canRender() returns true. |
1241 | | - * |
1242 | | - * @param string $thumbPath Path to thumbnail |
1243 | | - * @param int $width Desired width in pixels |
1244 | | - * @param int $height Desired height in pixels |
1245 | | - * @return bool True on error, false or error string on failure. |
1246 | | - * @private |
| 1093 | + * Get a ThumbnailImage representing a file type icon |
| 1094 | + * @return ThumbnailImage |
1247 | 1095 | */ |
1248 | | - function reallyRenderThumb( $thumbPath, $width, $height ) { |
1249 | | - global $wgSVGConverters, $wgSVGConverter; |
1250 | | - global $wgUseImageMagick, $wgImageMagickConvertCommand; |
1251 | | - global $wgCustomConvertCommand; |
1252 | | - global $wgDjvuRenderer, $wgDjvuPostProcessor; |
| 1096 | + function iconThumb() { |
| 1097 | + global $wgStylePath, $wgStyleDirectory; |
1253 | 1098 | |
1254 | | - $this->load(); |
1255 | | - |
1256 | | - $err = false; |
1257 | | - $cmd = ""; |
1258 | | - $retval = 0; |
1259 | | - |
1260 | | - if( $this->mime === "image/svg" ) { |
1261 | | - #Right now we have only SVG |
1262 | | - |
1263 | | - global $wgSVGConverters, $wgSVGConverter; |
1264 | | - if( isset( $wgSVGConverters[$wgSVGConverter] ) ) { |
1265 | | - global $wgSVGConverterPath; |
1266 | | - $cmd = str_replace( |
1267 | | - array( '$path/', '$width', '$height', '$input', '$output' ), |
1268 | | - array( $wgSVGConverterPath ? "$wgSVGConverterPath/" : "", |
1269 | | - intval( $width ), |
1270 | | - intval( $height ), |
1271 | | - wfEscapeShellArg( $this->imagePath ), |
1272 | | - wfEscapeShellArg( $thumbPath ) ), |
1273 | | - $wgSVGConverters[$wgSVGConverter] ); |
1274 | | - wfProfileIn( 'rsvg' ); |
1275 | | - wfDebug( "reallyRenderThumb SVG: $cmd\n" ); |
1276 | | - $err = wfShellExec( $cmd, $retval ); |
1277 | | - wfProfileOut( 'rsvg' ); |
| 1099 | + $try = array( 'fileicon-' . $this->extension . '.png', 'fileicon.png' ); |
| 1100 | + foreach( $try as $icon ) { |
| 1101 | + $path = '/common/images/icons/' . $icon; |
| 1102 | + $filepath = $wgStyleDirectory . $path; |
| 1103 | + if( file_exists( $filepath ) ) { |
| 1104 | + return new ThumbnailImage( $wgStylePath . $path, 120, 120 ); |
1278 | 1105 | } |
1279 | | - } else { |
1280 | | - if ( $this->mime === "image/vnd.djvu" && $wgDjvuRenderer ) { |
1281 | | - // DJVU image |
1282 | | - // The file contains several images. First, extract the |
1283 | | - // page in hi-res, if it doesn't yet exist. Then, thumbnail |
1284 | | - // it. |
1285 | | - |
1286 | | - $cmd = "{$wgDjvuRenderer} -page={$this->page} -size=${width}x${height} " . |
1287 | | - wfEscapeShellArg( $this->imagePath ) . |
1288 | | - " | {$wgDjvuPostProcessor} > " . wfEscapeShellArg($thumbPath); |
1289 | | - wfProfileIn( 'ddjvu' ); |
1290 | | - wfDebug( "reallyRenderThumb DJVU: $cmd\n" ); |
1291 | | - $err = wfShellExec( $cmd, $retval ); |
1292 | | - wfProfileOut( 'ddjvu' ); |
1293 | | - |
1294 | | - } elseif ( $wgUseImageMagick ) { |
1295 | | - # use ImageMagick |
1296 | | - |
1297 | | - if ( $this->mime == 'image/jpeg' ) { |
1298 | | - $quality = "-quality 80"; // 80% |
1299 | | - } elseif ( $this->mime == 'image/png' ) { |
1300 | | - $quality = "-quality 95"; // zlib 9, adaptive filtering |
1301 | | - } else { |
1302 | | - $quality = ''; // default |
1303 | | - } |
1304 | | - |
1305 | | - # Specify white background color, will be used for transparent images |
1306 | | - # in Internet Explorer/Windows instead of default black. |
1307 | | - |
1308 | | - # Note, we specify "-size {$width}" and NOT "-size {$width}x{$height}". |
1309 | | - # It seems that ImageMagick has a bug wherein it produces thumbnails of |
1310 | | - # the wrong size in the second case. |
1311 | | - |
1312 | | - $cmd = wfEscapeShellArg($wgImageMagickConvertCommand) . |
1313 | | - " {$quality} -background white -size {$width} ". |
1314 | | - wfEscapeShellArg($this->imagePath) . |
1315 | | - // Coalesce is needed to scale animated GIFs properly (bug 1017). |
1316 | | - ' -coalesce ' . |
1317 | | - // For the -resize option a "!" is needed to force exact size, |
1318 | | - // or ImageMagick may decide your ratio is wrong and slice off |
1319 | | - // a pixel. |
1320 | | - " -thumbnail " . wfEscapeShellArg( "{$width}x{$height}!" ) . |
1321 | | - " -depth 8 " . |
1322 | | - wfEscapeShellArg($thumbPath) . " 2>&1"; |
1323 | | - wfDebug("reallyRenderThumb: running ImageMagick: $cmd\n"); |
1324 | | - wfProfileIn( 'convert' ); |
1325 | | - $err = wfShellExec( $cmd, $retval ); |
1326 | | - wfProfileOut( 'convert' ); |
1327 | | - } elseif( $wgCustomConvertCommand ) { |
1328 | | - # Use a custom convert command |
1329 | | - # Variables: %s %d %w %h |
1330 | | - $src = wfEscapeShellArg( $this->imagePath ); |
1331 | | - $dst = wfEscapeShellArg( $thumbPath ); |
1332 | | - $cmd = $wgCustomConvertCommand; |
1333 | | - $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames |
1334 | | - $cmd = str_replace( '%h', $height, str_replace( '%w', $width, $cmd ) ); # Size |
1335 | | - wfDebug( "reallyRenderThumb: Running custom convert command $cmd\n" ); |
1336 | | - wfProfileIn( 'convert' ); |
1337 | | - $err = wfShellExec( $cmd, $retval ); |
1338 | | - wfProfileOut( 'convert' ); |
1339 | | - } else { |
1340 | | - # Use PHP's builtin GD library functions. |
1341 | | - # |
1342 | | - # First find out what kind of file this is, and select the correct |
1343 | | - # input routine for this. |
1344 | | - |
1345 | | - $typemap = array( |
1346 | | - 'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ), |
1347 | | - 'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor', array( &$this, 'imageJpegWrapper' ) ), |
1348 | | - 'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ), |
1349 | | - 'image/vnd.wap.wmbp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ), |
1350 | | - 'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ), |
1351 | | - ); |
1352 | | - if( !isset( $typemap[$this->mime] ) ) { |
1353 | | - $err = 'Image type not supported'; |
1354 | | - wfDebug( "$err\n" ); |
1355 | | - return $err; |
1356 | | - } |
1357 | | - list( $loader, $colorStyle, $saveType ) = $typemap[$this->mime]; |
1358 | | - |
1359 | | - if( !function_exists( $loader ) ) { |
1360 | | - $err = "Incomplete GD library configuration: missing function $loader"; |
1361 | | - wfDebug( "$err\n" ); |
1362 | | - return $err; |
1363 | | - } |
1364 | | - if( $colorStyle == 'palette' ) { |
1365 | | - $truecolor = false; |
1366 | | - } elseif( $colorStyle == 'truecolor' ) { |
1367 | | - $truecolor = true; |
1368 | | - } elseif( $colorStyle == 'bits' ) { |
1369 | | - $truecolor = ( $this->bits > 8 ); |
1370 | | - } |
1371 | | - |
1372 | | - $src_image = call_user_func( $loader, $this->imagePath ); |
1373 | | - if ( $truecolor ) { |
1374 | | - $dst_image = imagecreatetruecolor( $width, $height ); |
1375 | | - } else { |
1376 | | - $dst_image = imagecreate( $width, $height ); |
1377 | | - } |
1378 | | - imagecopyresampled( $dst_image, $src_image, |
1379 | | - 0,0,0,0, |
1380 | | - $width, $height, $this->width, $this->height ); |
1381 | | - call_user_func( $saveType, $dst_image, $thumbPath ); |
1382 | | - imagedestroy( $dst_image ); |
1383 | | - imagedestroy( $src_image ); |
1384 | | - } |
1385 | 1106 | } |
1386 | | - |
1387 | | - # |
1388 | | - # Check for zero-sized thumbnails. Those can be generated when |
1389 | | - # no disk space is available or some other error occurs |
1390 | | - # |
1391 | | - if( file_exists( $thumbPath ) ) { |
1392 | | - $thumbstat = stat( $thumbPath ); |
1393 | | - if( $thumbstat['size'] == 0 || $retval != 0 ) { |
1394 | | - wfDebugLog( 'thumbnail', |
1395 | | - sprintf( 'Removing bad %d-byte thumbnail "%s"', |
1396 | | - $thumbstat['size'], $thumbPath ) ); |
1397 | | - unlink( $thumbPath ); |
1398 | | - } |
1399 | | - } |
1400 | | - if ( $retval != 0 ) { |
1401 | | - wfDebugLog( 'thumbnail', |
1402 | | - sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"', |
1403 | | - wfHostname(), $retval, trim($err), $cmd ) ); |
1404 | | - return wfMsg( 'thumbnail_error', $err ); |
1405 | | - } else { |
1406 | | - return true; |
1407 | | - } |
| 1107 | + return null; |
1408 | 1108 | } |
1409 | 1109 | |
| 1110 | + /** |
| 1111 | + * Get last thumbnailing error. |
| 1112 | + * Largely obsolete. |
| 1113 | + */ |
1410 | 1114 | function getLastError() { |
1411 | 1115 | return $this->lastError; |
1412 | 1116 | } |
1413 | 1117 | |
1414 | | - function imageJpegWrapper( $dst_image, $thumbPath ) { |
1415 | | - imageinterlace( $dst_image ); |
1416 | | - imagejpeg( $dst_image, $thumbPath, 95 ); |
1417 | | - } |
1418 | | - |
1419 | 1118 | /** |
1420 | 1119 | * Get all thumbnail names previously generated for this image |
1421 | 1120 | */ |
— | — | @@ -1466,9 +1165,10 @@ |
1467 | 1166 | $dir = wfImageThumbDir( $this->name, $shared ); |
1468 | 1167 | $urls = array(); |
1469 | 1168 | foreach ( $files as $file ) { |
1470 | | - $m = array(); |
1471 | | - if ( preg_match( '/^(\d+)px/', $file, $m ) ) { |
1472 | | - list( /* $isScriptUrl */, $url ) = $this->thumbUrl( $m[1] ); |
| 1169 | + # Check that the base image name is part of the thumb name |
| 1170 | + # This is a basic sanity check to avoid erasing unrelated directories |
| 1171 | + if ( strpos( $file, $this->name ) !== false ) { |
| 1172 | + $url = $this->thumbUrlFromName( $file ); |
1473 | 1173 | $urls[] = $url; |
1474 | 1174 | @unlink( "$dir/$file" ); |
1475 | 1175 | } |
— | — | @@ -1511,35 +1211,6 @@ |
1512 | 1212 | $update->doUpdate(); |
1513 | 1213 | } |
1514 | 1214 | |
1515 | | - function checkDBSchema(&$db) { |
1516 | | - static $checkDone = false; |
1517 | | - global $wgCheckDBSchema; |
1518 | | - if (!$wgCheckDBSchema || $checkDone) { |
1519 | | - return; |
1520 | | - } |
1521 | | - # img_name must be unique |
1522 | | - if ( !$db->indexUnique( 'image', 'img_name' ) && !$db->indexExists('image','PRIMARY') ) { |
1523 | | - throw new MWException( 'Database schema not up to date, please run maintenance/archives/patch-image_name_unique.sql' ); |
1524 | | - } |
1525 | | - $checkDone = true; |
1526 | | - |
1527 | | - # new fields must exist |
1528 | | - # |
1529 | | - # Not really, there's hundreds of checks like this that we could do and they're all pointless, because |
1530 | | - # if the fields are missing, the database will loudly report a query error, the first time you try to do |
1531 | | - # something. The only reason I put the above schema check in was because the absence of that particular |
1532 | | - # index would lead to an annoying subtle bug. No error message, just some very odd behaviour on duplicate |
1533 | | - # uploads. -- TS |
1534 | | - /* |
1535 | | - if ( !$db->fieldExists( 'image', 'img_media_type' ) |
1536 | | - || !$db->fieldExists( 'image', 'img_metadata' ) |
1537 | | - || !$db->fieldExists( 'image', 'img_width' ) ) { |
1538 | | - |
1539 | | - throw new MWException( 'Database schema not up to date, please run maintenance/update.php' ); |
1540 | | - } |
1541 | | - */ |
1542 | | - } |
1543 | | - |
1544 | 1215 | /** |
1545 | 1216 | * Return the image history of this image, line by line. |
1546 | 1217 | * starts with current version, then old versions. |
— | — | @@ -1553,8 +1224,6 @@ |
1554 | 1225 | function nextHistoryLine() { |
1555 | 1226 | $dbr = wfGetDB( DB_SLAVE ); |
1556 | 1227 | |
1557 | | - $this->checkDBSchema($dbr); |
1558 | | - |
1559 | 1228 | if ( $this->historyLine == 0 ) {// called for the first time, return line from cur |
1560 | 1229 | $this->historyRes = $dbr->select( 'image', |
1561 | 1230 | array( |
— | — | @@ -1614,35 +1283,27 @@ |
1615 | 1284 | * @param boolean $fromSharedDirectory Return the path to the file |
1616 | 1285 | * in a shared repository (see $wgUseSharedRepository and related |
1617 | 1286 | * options in DefaultSettings.php) instead of a local one. |
1618 | | - * |
| 1287 | + * @param boolean $fromInstantCommons Use local repository paths only if |
| 1288 | + * InstantCommons is not available |
1619 | 1289 | */ |
1620 | 1290 | function getFullPath( $fromSharedRepository = false, $fromInstantCommons = false ) { |
1621 | 1291 | global $wgUploadDirectory, $wgSharedUploadDirectory, $wgInstantCommonsServerPath; |
1622 | | - //use local repository paths only if InstantCommons path is not available |
1623 | | - $dir = $fromInstantCommons ? $wgInstantCommonsServerPath : ($fromSharedRepository ? $wgSharedUploadDirectory : |
1624 | | - $wgUploadDirectory); |
1625 | 1292 | |
| 1293 | + $dir = $fromInstantCommons ? $wgInstantCommonsServerPath : ($fromSharedRepository ? $wgSharedUploadDirectory : |
| 1294 | + $wgUploadDirectory); |
| 1295 | + |
1626 | 1296 | // $wgSharedUploadDirectory may be false, if thumb.php is used |
1627 | 1297 | if ( $dir ) { |
1628 | | - if($fromInstantCommons) |
1629 | | - {//TODO: Not the best thing to do here as it adds another 10k to |
1630 | | - //the default mediawiki install. Maybe point it to the default logo? |
1631 | | - $fullpath = 'downloading.png'; |
1632 | | - } |
1633 | | - else |
1634 | | - { |
1635 | | - $fullpath = $dir . wfGetHashPath($this->name, $fromSharedRepository, $fromInstantCommons) . $this->name; |
1636 | | - } |
| 1298 | + $fullpath = $dir . wfGetHashPath($this->name, $fromSharedRepository, $fromInstantCommons) . $this->name; |
1637 | 1299 | } else { |
1638 | 1300 | $fullpath = false; |
1639 | 1301 | } |
1640 | | - |
| 1302 | + |
1641 | 1303 | return $fullpath; |
1642 | 1304 | } |
1643 | 1305 | |
1644 | 1306 | /** |
1645 | 1307 | * @return bool |
1646 | | - * @static |
1647 | 1308 | */ |
1648 | 1309 | public static function isHashed( $shared ) { |
1649 | 1310 | global $wgHashedUploadDirectory, $wgHashedSharedUploadDirectory; |
— | — | @@ -1657,8 +1318,6 @@ |
1658 | 1319 | |
1659 | 1320 | $dbw = wfGetDB( DB_MASTER ); |
1660 | 1321 | |
1661 | | - $this->checkDBSchema($dbw); |
1662 | | - |
1663 | 1322 | // Delete thumbnails and refresh the metadata cache |
1664 | 1323 | $this->purgeCache(); |
1665 | 1324 | |
— | — | @@ -1740,7 +1399,7 @@ |
1741 | 1400 | |
1742 | 1401 | # Update the current image row |
1743 | 1402 | $dbw->update( 'image', |
1744 | | - array( // SET |
| 1403 | + array( /* SET */ |
1745 | 1404 | 'img_size' => $this->size, |
1746 | 1405 | 'img_width' => intval( $this->width ), |
1747 | 1406 | 'img_height' => intval( $this->height ), |
— | — | @@ -1753,7 +1412,7 @@ |
1754 | 1413 | 'img_user' => $wgUser->getID(), |
1755 | 1414 | 'img_user_text' => $wgUser->getName(), |
1756 | 1415 | 'img_metadata' => $this->metadata, |
1757 | | - ), array( // WHERE |
| 1416 | + ), array( /* WHERE */ |
1758 | 1417 | 'img_name' => $this->name |
1759 | 1418 | ), __METHOD__ |
1760 | 1419 | ); |
— | — | @@ -1784,6 +1443,9 @@ |
1785 | 1444 | $article->insertNewArticle( $textdesc, $desc, $minor, $watch, $suppressRC ); |
1786 | 1445 | } |
1787 | 1446 | |
| 1447 | + # Hooks, hooks, the magic of hooks... |
| 1448 | + wfRunHooks( 'FileUpload', array( $this ) ); |
| 1449 | + |
1788 | 1450 | # Add the log entry |
1789 | 1451 | $log = new LogPage( 'upload' ); |
1790 | 1452 | $log->addEntry( 'upload', $descTitle, $desc ); |
— | — | @@ -1791,11 +1453,11 @@ |
1792 | 1454 | # Commit the transaction now, in case something goes wrong later |
1793 | 1455 | # The most important thing is that images don't get lost, especially archives |
1794 | 1456 | $dbw->immediateCommit(); |
1795 | | - |
| 1457 | + |
1796 | 1458 | # Invalidate cache for all pages using this image |
1797 | 1459 | $update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' ); |
1798 | 1460 | $update->doUpdate(); |
1799 | | - |
| 1461 | + |
1800 | 1462 | return true; |
1801 | 1463 | } |
1802 | 1464 | |
— | — | @@ -1836,76 +1498,24 @@ |
1837 | 1499 | return $retVal; |
1838 | 1500 | } |
1839 | 1501 | |
1840 | | - /** |
1841 | | - * Retrive Exif data from the file and prune unrecognized tags |
1842 | | - * and/or tags with invalid contents |
1843 | | - * |
1844 | | - * @param $filename |
1845 | | - * @return array |
1846 | | - */ |
1847 | | - private function retrieveExifData( $filename ) { |
1848 | | - global $wgShowEXIF; |
1849 | | - |
1850 | | - /* |
1851 | | - if ( $this->getMimeType() !== "image/jpeg" ) |
| 1502 | + function getExifData() { |
| 1503 | + $handler = $this->getHandler(); |
| 1504 | + if ( !$handler || $handler->getMetadataType( $this ) != 'exif' ) { |
1852 | 1505 | return array(); |
1853 | | - */ |
1854 | | - |
1855 | | - if( $wgShowEXIF && file_exists( $filename ) ) { |
1856 | | - $exif = new Exif( $filename ); |
1857 | | - return $exif->getFilteredData(); |
1858 | 1506 | } |
1859 | | - |
1860 | | - return array(); |
1861 | | - } |
1862 | | - |
1863 | | - function getExifData() { |
1864 | | - global $wgRequest; |
1865 | | - if ( $this->metadata === '0' || $this->mime == 'image/vnd.djvu' ) |
| 1507 | + if ( !$this->metadata ) { |
1866 | 1508 | return array(); |
1867 | | - |
1868 | | - $purge = $wgRequest->getVal( 'action' ) == 'purge'; |
1869 | | - $ret = unserialize( $this->metadata ); |
1870 | | - |
1871 | | - $oldver = isset( $ret['MEDIAWIKI_EXIF_VERSION'] ) ? $ret['MEDIAWIKI_EXIF_VERSION'] : 0; |
1872 | | - $newver = Exif::version(); |
1873 | | - |
1874 | | - if ( !count( $ret ) || $purge || $oldver != $newver ) { |
1875 | | - $this->purgeMetadataCache(); |
1876 | | - $this->updateExifData( $newver ); |
1877 | 1509 | } |
1878 | | - if ( isset( $ret['MEDIAWIKI_EXIF_VERSION'] ) ) |
1879 | | - unset( $ret['MEDIAWIKI_EXIF_VERSION'] ); |
1880 | | - $format = new FormatExif( $ret ); |
| 1510 | + $exif = unserialize( $this->metadata ); |
| 1511 | + if ( !$exif ) { |
| 1512 | + return array(); |
| 1513 | + } |
| 1514 | + unset( $exif['MEDIAWIKI_EXIF_VERSION'] ); |
| 1515 | + $format = new FormatExif( $exif ); |
1881 | 1516 | |
1882 | 1517 | return $format->getFormattedData(); |
1883 | 1518 | } |
1884 | 1519 | |
1885 | | - function updateExifData( $version ) { |
1886 | | - if ( $this->getImagePath() === false ) # Not a local image |
1887 | | - return; |
1888 | | - |
1889 | | - # Get EXIF data from image |
1890 | | - $exif = $this->retrieveExifData( $this->imagePath ); |
1891 | | - if ( count( $exif ) ) { |
1892 | | - $exif['MEDIAWIKI_EXIF_VERSION'] = $version; |
1893 | | - $this->metadata = serialize( $exif ); |
1894 | | - } else { |
1895 | | - $this->metadata = '0'; |
1896 | | - } |
1897 | | - |
1898 | | - # Update EXIF data in database |
1899 | | - $dbw = wfGetDB( DB_MASTER ); |
1900 | | - |
1901 | | - $this->checkDBSchema($dbw); |
1902 | | - |
1903 | | - $dbw->update( 'image', |
1904 | | - array( 'img_metadata' => $this->metadata ), |
1905 | | - array( 'img_name' => $this->name ), |
1906 | | - __METHOD__ |
1907 | | - ); |
1908 | | - } |
1909 | | - |
1910 | 1520 | /** |
1911 | 1521 | * Returns true if the image does not come from the shared |
1912 | 1522 | * image repository. |
— | — | @@ -1937,7 +1547,7 @@ |
1938 | 1548 | * @param $reason |
1939 | 1549 | * @return true on success, false on some kind of failure |
1940 | 1550 | */ |
1941 | | - function delete( $reason ) { |
| 1551 | + function delete( $reason, $suppress=false ) { |
1942 | 1552 | $transaction = new FSTransaction(); |
1943 | 1553 | $urlArr = array( $this->getURL() ); |
1944 | 1554 | |
— | — | @@ -1958,7 +1568,7 @@ |
1959 | 1569 | while( $row = $dbw->fetchObject( $result ) ) { |
1960 | 1570 | $oldName = $row->oi_archive_name; |
1961 | 1571 | |
1962 | | - $transaction->add( $this->prepareDeleteOld( $oldName, $reason ) ); |
| 1572 | + $transaction->add( $this->prepareDeleteOld( $oldName, $reason, $suppress ) ); |
1963 | 1573 | |
1964 | 1574 | // We'll need to purge this URL from caches... |
1965 | 1575 | $urlArr[] = wfImageArchiveUrl( $oldName ); |
— | — | @@ -1966,7 +1576,7 @@ |
1967 | 1577 | $dbw->freeResult( $result ); |
1968 | 1578 | |
1969 | 1579 | // And the current version... |
1970 | | - $transaction->add( $this->prepareDeleteCurrent( $reason ) ); |
| 1580 | + $transaction->add( $this->prepareDeleteCurrent( $reason, $suppress ) ); |
1971 | 1581 | |
1972 | 1582 | $dbw->immediateCommit(); |
1973 | 1583 | } catch( MWException $e ) { |
— | — | @@ -2003,7 +1613,7 @@ |
2004 | 1614 | * @throws MWException or FSException on database or filestore failure |
2005 | 1615 | * @return true on success, false on some kind of failure |
2006 | 1616 | */ |
2007 | | - function deleteOld( $archiveName, $reason ) { |
| 1617 | + function deleteOld( $archiveName, $reason, $suppress=false ) { |
2008 | 1618 | $transaction = new FSTransaction(); |
2009 | 1619 | $urlArr = array(); |
2010 | 1620 | |
— | — | @@ -2016,7 +1626,7 @@ |
2017 | 1627 | try { |
2018 | 1628 | $dbw = wfGetDB( DB_MASTER ); |
2019 | 1629 | $dbw->begin(); |
2020 | | - $transaction->add( $this->prepareDeleteOld( $archiveName, $reason ) ); |
| 1630 | + $transaction->add( $this->prepareDeleteOld( $archiveName, $reason, $suppress ) ); |
2021 | 1631 | $dbw->immediateCommit(); |
2022 | 1632 | } catch( MWException $e ) { |
2023 | 1633 | wfDebug( __METHOD__.": db error, rolling back file transaction\n" ); |
— | — | @@ -2047,7 +1657,7 @@ |
2048 | 1658 | * May throw a database error. |
2049 | 1659 | * @return true on success, false on failure |
2050 | 1660 | */ |
2051 | | - private function prepareDeleteCurrent( $reason ) { |
| 1661 | + private function prepareDeleteCurrent( $reason, $suppress=false ) { |
2052 | 1662 | return $this->prepareDeleteVersion( |
2053 | 1663 | $this->getFullPath(), |
2054 | 1664 | $reason, |
— | — | @@ -2068,6 +1678,7 @@ |
2069 | 1679 | 'fa_user_text' => 'img_user_text', |
2070 | 1680 | 'fa_timestamp' => 'img_timestamp' ), |
2071 | 1681 | array( 'img_name' => $this->name ), |
| 1682 | + $suppress, |
2072 | 1683 | __METHOD__ ); |
2073 | 1684 | } |
2074 | 1685 | |
— | — | @@ -2076,7 +1687,7 @@ |
2077 | 1688 | * May throw a database error. |
2078 | 1689 | * @return true on success, false on failure |
2079 | 1690 | */ |
2080 | | - private function prepareDeleteOld( $archiveName, $reason ) { |
| 1691 | + private function prepareDeleteOld( $archiveName, $reason, $suppress=false ) { |
2081 | 1692 | $oldpath = wfImageArchiveDir( $this->name ) . |
2082 | 1693 | DIRECTORY_SEPARATOR . $archiveName; |
2083 | 1694 | return $this->prepareDeleteVersion( |
— | — | @@ -2101,6 +1712,7 @@ |
2102 | 1713 | array( |
2103 | 1714 | 'oi_name' => $this->name, |
2104 | 1715 | 'oi_archive_name' => $archiveName ), |
| 1716 | + $suppress, |
2105 | 1717 | __METHOD__ ); |
2106 | 1718 | } |
2107 | 1719 | |
— | — | @@ -2113,7 +1725,7 @@ |
2114 | 1726 | * |
2115 | 1727 | * @return FSTransaction |
2116 | 1728 | */ |
2117 | | - private function prepareDeleteVersion( $path, $reason, $table, $fieldMap, $where, $fname ) { |
| 1729 | + private function prepareDeleteVersion( $path, $reason, $table, $fieldMap, $where, $suppress=false, $fname ) { |
2118 | 1730 | global $wgUser, $wgSaveDeletedFiles; |
2119 | 1731 | |
2120 | 1732 | // Dupe the file into the file store |
— | — | @@ -2143,6 +1755,17 @@ |
2144 | 1756 | throw new MWException( "Could not archive and delete file $path" ); |
2145 | 1757 | return false; |
2146 | 1758 | } |
| 1759 | + |
| 1760 | + // Bitfields to further supress the image content |
| 1761 | + // Note that currently, live images are stored elsewhere |
| 1762 | + // and cannot be partially deleted |
| 1763 | + $bitfield = 0; |
| 1764 | + if ( $suppress ) { |
| 1765 | + $bitfield |= self::DELETED_FILE; |
| 1766 | + $bitfield |= self::DELETED_COMMENT; |
| 1767 | + $bitfield |= self::DELETED_USER; |
| 1768 | + $bitfield |= self::DELETED_RESTRICTED; |
| 1769 | + } |
2147 | 1770 | |
2148 | 1771 | $dbw = wfGetDB( DB_MASTER ); |
2149 | 1772 | $storageMap = array( |
— | — | @@ -2151,7 +1774,8 @@ |
2152 | 1775 | |
2153 | 1776 | 'fa_deleted_user' => $dbw->addQuotes( $wgUser->getId() ), |
2154 | 1777 | 'fa_deleted_timestamp' => $dbw->timestamp(), |
2155 | | - 'fa_deleted_reason' => $dbw->addQuotes( $reason ) ); |
| 1778 | + 'fa_deleted_reason' => $dbw->addQuotes( $reason ), |
| 1779 | + 'fa_deleted' => $bitfield); |
2156 | 1780 | $allFields = array_merge( $storageMap, $fieldMap ); |
2157 | 1781 | |
2158 | 1782 | try { |
— | — | @@ -2181,7 +1805,9 @@ |
2182 | 1806 | * @return the number of file revisions restored if successful, |
2183 | 1807 | * or false on failure |
2184 | 1808 | */ |
2185 | | - function restore( $versions=array() ) { |
| 1809 | + function restore( $versions=array(), $Unsuppress=false ) { |
| 1810 | + global $wgUser; |
| 1811 | + |
2186 | 1812 | if( !FileStore::lock() ) { |
2187 | 1813 | wfDebug( __METHOD__." could not acquire filestore lock\n" ); |
2188 | 1814 | return false; |
— | — | @@ -2230,6 +1856,12 @@ |
2231 | 1857 | |
2232 | 1858 | $revisions = 0; |
2233 | 1859 | while( $row = $dbw->fetchObject( $result ) ) { |
| 1860 | + if ( $Unsuppress ) { |
| 1861 | + // Currently, fa_deleted flags fall off upon restore, lets be careful about this |
| 1862 | + } else if ( ($row->fa_deleted & Revision::DELETED_RESTRICTED) && !$wgUser->isAllowed('hiderevision') ) { |
| 1863 | + // Skip restoring file revisions that the user cannot restore |
| 1864 | + continue; |
| 1865 | + } |
2234 | 1866 | $revisions++; |
2235 | 1867 | $store = FileStore::get( $row->fa_storage_group ); |
2236 | 1868 | if( !$store ) { |
— | — | @@ -2248,12 +1880,17 @@ |
2249 | 1881 | // an archived file revision. |
2250 | 1882 | if( is_null( $row->fa_metadata ) ) { |
2251 | 1883 | $tempFile = $store->filePath( $row->fa_storage_key ); |
2252 | | - $metadata = serialize( $this->retrieveExifData( $tempFile ) ); |
2253 | 1884 | |
2254 | 1885 | $magic = MimeMagic::singleton(); |
2255 | 1886 | $mime = $magic->guessMimeType( $tempFile, true ); |
2256 | 1887 | $media_type = $magic->getMediaType( $tempFile, $mime ); |
2257 | 1888 | list( $major_mime, $minor_mime ) = self::splitMime( $mime ); |
| 1889 | + $handler = MediaHandler::getHandler( $mime ); |
| 1890 | + if ( $handler ) { |
| 1891 | + $metadata = $handler->getMetadata( $image, $tempFile ); |
| 1892 | + } else { |
| 1893 | + $metadata = ''; |
| 1894 | + } |
2258 | 1895 | } else { |
2259 | 1896 | $metadata = $row->fa_metadata; |
2260 | 1897 | $major_mime = $row->fa_major_mime; |
— | — | @@ -2307,7 +1944,7 @@ |
2308 | 1945 | } |
2309 | 1946 | |
2310 | 1947 | $dbw->insert( $table, $fields, __METHOD__ ); |
2311 | | - /// @fixme this delete is not totally safe, potentially |
| 1948 | + // @todo this delete is not totally safe, potentially |
2312 | 1949 | $dbw->delete( 'filearchive', |
2313 | 1950 | array( 'fa_id' => $row->fa_id ), |
2314 | 1951 | __METHOD__ ); |
— | — | @@ -2361,71 +1998,14 @@ |
2362 | 1999 | } |
2363 | 2000 | |
2364 | 2001 | /** |
2365 | | - * Select a page from a multipage document. Determines the page used for |
2366 | | - * rendering thumbnails. |
2367 | | - * |
2368 | | - * @param $page Integer: page number, starting with 1 |
2369 | | - */ |
2370 | | - function selectPage( $page ) { |
2371 | | - if( $this->initializeMultiPageXML() ) { |
2372 | | - wfDebug( __METHOD__." selecting page $page \n" ); |
2373 | | - $this->page = $page; |
2374 | | - $o = $this->multiPageXML->BODY[0]->OBJECT[$page-1]; |
2375 | | - $this->height = intval( $o['height'] ); |
2376 | | - $this->width = intval( $o['width'] ); |
2377 | | - } else { |
2378 | | - wfDebug( __METHOD__." selectPage($page) for bogus multipage xml on '$this->name'\n" ); |
2379 | | - return; |
2380 | | - } |
2381 | | - } |
2382 | | - |
2383 | | - /** |
2384 | | - * Lazy-initialize multipage XML metadata for DjVu files. |
2385 | | - * @return bool true if $this->multiPageXML is set up and ready; |
2386 | | - * false if corrupt or otherwise failing |
2387 | | - */ |
2388 | | - function initializeMultiPageXML() { |
2389 | | - $this->load(); |
2390 | | - if ( isset( $this->multiPageXML ) ) { |
2391 | | - return true; |
2392 | | - } |
2393 | | - |
2394 | | - # |
2395 | | - # Check for files uploaded prior to DJVU support activation, |
2396 | | - # or damaged. |
2397 | | - # |
2398 | | - if( empty( $this->metadata ) || $this->metadata == serialize( array() ) ) { |
2399 | | - $deja = new DjVuImage( $this->imagePath ); |
2400 | | - $this->metadata = $deja->retrieveMetaData(); |
2401 | | - $this->purgeMetadataCache(); |
2402 | | - |
2403 | | - # Update metadata in the database |
2404 | | - $dbw = wfGetDB( DB_MASTER ); |
2405 | | - $dbw->update( 'image', |
2406 | | - array( 'img_metadata' => $this->metadata ), |
2407 | | - array( 'img_name' => $this->name ), |
2408 | | - __METHOD__ |
2409 | | - ); |
2410 | | - } |
2411 | | - wfSuppressWarnings(); |
2412 | | - try { |
2413 | | - $this->multiPageXML = new SimpleXMLElement( $this->metadata ); |
2414 | | - } catch( Exception $e ) { |
2415 | | - wfDebug( "Bogus multipage XML metadata on '$this->name'\n" ); |
2416 | | - $this->multiPageXML = null; |
2417 | | - } |
2418 | | - wfRestoreWarnings(); |
2419 | | - return isset( $this->multiPageXML ); |
2420 | | - } |
2421 | | - |
2422 | | - /** |
2423 | 2002 | * Returns 'true' if this image is a multipage document, e.g. a DJVU |
2424 | 2003 | * document. |
2425 | 2004 | * |
2426 | 2005 | * @return Bool |
2427 | 2006 | */ |
2428 | 2007 | function isMultipage() { |
2429 | | - return ( $this->mime == 'image/vnd.djvu' ); |
| 2008 | + $handler = $this->getHandler(); |
| 2009 | + return $handler && $handler->isMultiPage(); |
2430 | 2010 | } |
2431 | 2011 | |
2432 | 2012 | /** |
— | — | @@ -2433,13 +2013,10 @@ |
2434 | 2014 | * documents which aren't multipage documents |
2435 | 2015 | */ |
2436 | 2016 | function pageCount() { |
2437 | | - if ( ! $this->isMultipage() ) { |
2438 | | - return null; |
2439 | | - } |
2440 | | - if( $this->initializeMultiPageXML() ) { |
2441 | | - return count( $this->multiPageXML->xpath( '//OBJECT' ) ); |
| 2017 | + $handler = $this->getHandler(); |
| 2018 | + if ( $handler && $handler->isMultiPage() ) { |
| 2019 | + return $handler->pageCount( $this ); |
2442 | 2020 | } else { |
2443 | | - wfDebug( "Requested pageCount() for bogus multi-page metadata for '$this->name'\n" ); |
2444 | 2021 | return null; |
2445 | 2022 | } |
2446 | 2023 | } |
— | — | @@ -2456,60 +2033,161 @@ |
2457 | 2034 | return $dbc; |
2458 | 2035 | } |
2459 | 2036 | |
| 2037 | + /** |
| 2038 | + * Calculate the height of a thumbnail using the source and destination width |
| 2039 | + */ |
| 2040 | + static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) { |
| 2041 | + // Exact integer multiply followed by division |
| 2042 | + if ( $srcWidth == 0 ) { |
| 2043 | + return 0; |
| 2044 | + } else { |
| 2045 | + return round( $srcHeight * $dstWidth / $srcWidth ); |
| 2046 | + } |
| 2047 | + } |
| 2048 | + |
| 2049 | + /** |
| 2050 | + * Get an image size array like that returned by getimagesize(), or false if it |
| 2051 | + * can't be determined. |
| 2052 | + * |
| 2053 | + * @param string $fileName The filename |
| 2054 | + * @return array |
| 2055 | + */ |
| 2056 | + function getImageSize( $fileName ) { |
| 2057 | + $handler = $this->getHandler(); |
| 2058 | + return $handler->getImageSize( $this, $fileName ); |
| 2059 | + } |
| 2060 | + |
| 2061 | + /** |
| 2062 | + * Get the thumbnail extension and MIME type for a given source MIME type |
| 2063 | + * @return array thumbnail extension and MIME type |
| 2064 | + */ |
| 2065 | + static function getThumbType( $ext, $mime ) { |
| 2066 | + $handler = MediaHandler::getHandler( $mime ); |
| 2067 | + if ( $handler ) { |
| 2068 | + return $handler->getThumbType( $ext, $mime ); |
| 2069 | + } else { |
| 2070 | + return array( $ext, $mime ); |
| 2071 | + } |
| 2072 | + } |
| 2073 | + |
2460 | 2074 | } //class |
2461 | 2075 | |
| 2076 | + |
2462 | 2077 | /** |
2463 | | - * Wrapper class for thumbnail images |
| 2078 | + * @addtogroup Media |
2464 | 2079 | */ |
2465 | | -class ThumbnailImage { |
| 2080 | +class ArchivedFile |
| 2081 | +{ |
2466 | 2082 | /** |
2467 | | - * @param string $path Filesystem path to the thumb |
2468 | | - * @param string $url URL path to the thumb |
2469 | | - * @private |
| 2083 | + * Returns a file object from the filearchive table |
| 2084 | + * In the future, all current and old image storage |
| 2085 | + * may use FileStore. There will be a "old" storage |
| 2086 | + * for current and previous file revisions as well as |
| 2087 | + * the "deleted" group for archived revisions |
| 2088 | + * @param $title, the corresponding image page title |
| 2089 | + * @param $id, the image id, a unique key |
| 2090 | + * @param $key, optional storage key |
| 2091 | + * @return ResultWrapper |
2470 | 2092 | */ |
2471 | | - function ThumbnailImage( $url, $width, $height, $path = false ) { |
2472 | | - $this->url = $url; |
2473 | | - $this->width = round( $width ); |
2474 | | - $this->height = round( $height ); |
2475 | | - # These should be integers when they get here. |
2476 | | - # If not, there's a bug somewhere. But let's at |
2477 | | - # least produce valid HTML code regardless. |
2478 | | - $this->path = $path; |
| 2093 | + function ArchivedFile( $title, $id=0, $key='' ) { |
| 2094 | + if( !is_object( $title ) ) { |
| 2095 | + throw new MWException( 'Image constructor given bogus title.' ); |
| 2096 | + } |
| 2097 | + $conds = ($id) ? "fa_id = $id" : "fa_storage_key = '$key'"; |
| 2098 | + if( $title->getNamespace() == NS_IMAGE ) { |
| 2099 | + $dbr = wfGetDB( DB_SLAVE ); |
| 2100 | + $res = $dbr->select( 'filearchive', |
| 2101 | + array( |
| 2102 | + 'fa_id', |
| 2103 | + 'fa_name', |
| 2104 | + 'fa_storage_key', |
| 2105 | + 'fa_storage_group', |
| 2106 | + 'fa_size', |
| 2107 | + 'fa_bits', |
| 2108 | + 'fa_width', |
| 2109 | + 'fa_height', |
| 2110 | + 'fa_metadata', |
| 2111 | + 'fa_media_type', |
| 2112 | + 'fa_major_mime', |
| 2113 | + 'fa_minor_mime', |
| 2114 | + 'fa_description', |
| 2115 | + 'fa_user', |
| 2116 | + 'fa_user_text', |
| 2117 | + 'fa_timestamp', |
| 2118 | + 'fa_deleted' ), |
| 2119 | + array( |
| 2120 | + 'fa_name' => $title->getDbKey(), |
| 2121 | + $conds ), |
| 2122 | + __METHOD__, |
| 2123 | + array( 'ORDER BY' => 'fa_timestamp DESC' ) ); |
| 2124 | + |
| 2125 | + if ( $dbr->numRows( $res ) == 0 ) { |
| 2126 | + // this revision does not exist? |
| 2127 | + return; |
| 2128 | + } |
| 2129 | + $ret = $dbr->resultObject( $res ); |
| 2130 | + $row = $ret->fetchObject(); |
| 2131 | + |
| 2132 | + // initialize fields for filestore image object |
| 2133 | + $this->mId = intval($row->fa_id); |
| 2134 | + $this->mName = $row->fa_name; |
| 2135 | + $this->mGroup = $row->fa_storage_group; |
| 2136 | + $this->mKey = $row->fa_storage_key; |
| 2137 | + $this->mSize = $row->fa_size; |
| 2138 | + $this->mBits = $row->fa_bits; |
| 2139 | + $this->mWidth = $row->fa_width; |
| 2140 | + $this->mHeight = $row->fa_height; |
| 2141 | + $this->mMetaData = $row->fa_metadata; |
| 2142 | + $this->mMime = "$row->fa_major_mime/$row->fa_minor_mime"; |
| 2143 | + $this->mType = $row->fa_media_type; |
| 2144 | + $this->mDescription = $row->fa_description; |
| 2145 | + $this->mUser = $row->fa_user; |
| 2146 | + $this->mUserText = $row->fa_user_text; |
| 2147 | + $this->mTimestamp = $row->fa_timestamp; |
| 2148 | + $this->mDeleted = $row->fa_deleted; |
| 2149 | + } else { |
| 2150 | + throw new MWException( 'This title does not correspond to an image page.' ); |
| 2151 | + return; |
| 2152 | + } |
| 2153 | + return true; |
2479 | 2154 | } |
2480 | 2155 | |
2481 | 2156 | /** |
2482 | | - * @return string The thumbnail URL |
| 2157 | + * int $field one of DELETED_* bitfield constants |
| 2158 | + * for file or revision rows |
| 2159 | + * @return bool |
2483 | 2160 | */ |
2484 | | - function getUrl() { |
2485 | | - return $this->url; |
| 2161 | + function isDeleted( $field ) { |
| 2162 | + return ($this->mDeleted & $field) == $field; |
2486 | 2163 | } |
2487 | | - |
| 2164 | + |
2488 | 2165 | /** |
2489 | | - * Return HTML <img ... /> tag for the thumbnail, will include |
2490 | | - * width and height attributes and a blank alt text (as required). |
2491 | | - * |
2492 | | - * You can set or override additional attributes by passing an |
2493 | | - * associative array of name => data pairs. The data will be escaped |
2494 | | - * for HTML output, so should be in plaintext. |
2495 | | - * |
2496 | | - * @param array $attribs |
2497 | | - * @return string |
2498 | | - * @public |
| 2166 | + * Determine if the current user is allowed to view a particular |
| 2167 | + * field of this FileStore image file, if it's marked as deleted. |
| 2168 | + * @param int $field |
| 2169 | + * @return bool |
2499 | 2170 | */ |
2500 | | - function toHtml( $attribs = array() ) { |
2501 | | - $attribs['src'] = $this->url; |
2502 | | - $attribs['width'] = $this->width; |
2503 | | - $attribs['height'] = $this->height; |
2504 | | - if( !isset( $attribs['alt'] ) ) $attribs['alt'] = ''; |
2505 | | - |
2506 | | - $html = '<img '; |
2507 | | - foreach( $attribs as $name => $data ) { |
2508 | | - $html .= $name . '="' . htmlspecialchars( $data ) . '" '; |
| 2171 | + function userCan( $field ) { |
| 2172 | + if( isset($this->mDeleted) && ($this->mDeleted & $field) == $field ) { |
| 2173 | + // images |
| 2174 | + global $wgUser; |
| 2175 | + $permission = ( $this->mDeleted & Revision::DELETED_RESTRICTED ) == Revision::DELETED_RESTRICTED |
| 2176 | + ? 'hiderevision' |
| 2177 | + : 'deleterevision'; |
| 2178 | + wfDebug( "Checking for $permission due to $field match on $this->mDeleted\n" ); |
| 2179 | + return $wgUser->isAllowed( $permission ); |
| 2180 | + } else { |
| 2181 | + return true; |
2509 | 2182 | } |
2510 | | - $html .= '/>'; |
2511 | | - return $html; |
2512 | 2183 | } |
2513 | | - |
2514 | 2184 | } |
2515 | 2185 | |
| 2186 | +/** |
| 2187 | + * Aliases for backwards compatibility with 1.6 |
| 2188 | + */ |
| 2189 | +define( 'MW_IMG_DELETED_FILE', Image::DELETED_FILE ); |
| 2190 | +define( 'MW_IMG_DELETED_COMMENT', Image::DELETED_COMMENT ); |
| 2191 | +define( 'MW_IMG_DELETED_USER', Image::DELETED_USER ); |
| 2192 | +define( 'MW_IMG_DELETED_RESTRICTED', Image::DELETED_RESTRICTED ); |
| 2193 | + |
2516 | 2194 | ?> |