Index: trunk/phase3/includes/ImagePage.php |
— | — | @@ -438,12 +438,15 @@ |
439 | 439 | |
440 | 440 | if($showLink) { |
441 | 441 | $filename = wfEscapeWikiText( $this->displayImg->getName() ); |
| 442 | + $medialink = $this->displayImg->isMissing() ? |
| 443 | + "'''$filename'''" : |
| 444 | + "[[Media:$filename|$filename]]"; |
442 | 445 | |
443 | 446 | if( !$this->displayImg->isSafeFile() ) { |
444 | 447 | $warning = wfMsgNoTrans( 'mediawarning' ); |
445 | 448 | $wgOut->addWikiText( <<<EOT |
446 | 449 | <div class="fullMedia"> |
447 | | -<span class="dangerousLink">[[Media:$filename|$filename]]</span>$dirmark |
| 450 | +<span class="dangerousLink">{$medialink}</span>$dirmark |
448 | 451 | <span class="fileInfo">$longDesc</span> |
449 | 452 | </div> |
450 | 453 | <div class="mediaWarning">$warning</div> |
— | — | @@ -452,7 +455,7 @@ |
453 | 456 | } else { |
454 | 457 | $wgOut->addWikiText( <<<EOT |
455 | 458 | <div class="fullMedia"> |
456 | | -[[Media:$filename|$filename]]$dirmark |
| 459 | +{$medialink}{$dirmark} |
457 | 460 | <span class="fileInfo">$longDesc</span> |
458 | 461 | </div> |
459 | 462 | EOT |
— | — | @@ -852,19 +855,24 @@ |
853 | 856 | if( !$file->userCan(File::DELETED_FILE) ) { |
854 | 857 | # Don't link to unviewable files |
855 | 858 | $row .= '<span class="history-deleted">' . $wgLang->timeAndDate( $timestamp, true ) . '</span>'; |
856 | | - } else if( $file->isDeleted(File::DELETED_FILE) ) { |
| 859 | + } elseif( $file->isDeleted(File::DELETED_FILE) ) { |
857 | 860 | $revdel = SpecialPage::getTitleFor( 'Revisiondelete' ); |
858 | 861 | # Make a link to review the image |
859 | 862 | $url = $this->skin->makeKnownLinkObj( $revdel, $wgLang->timeAndDate( $timestamp, true ), |
860 | 863 | "target=".$wgTitle->getPrefixedText()."&file=$sha1.".$this->current->getExtension() ); |
861 | 864 | $row .= '<span class="history-deleted">'.$url.'</span>'; |
| 865 | + } elseif( $file->isMissing() ) { |
| 866 | + # Don't link to missing files |
| 867 | + $row .= $wgLang->timeAndDate( $timestamp, true ); |
862 | 868 | } else { |
863 | 869 | $url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img ); |
864 | 870 | $row .= Xml::element( 'a', array( 'href' => $url ), $wgLang->timeAndDate( $timestamp, true ) ); |
865 | 871 | } |
866 | 872 | |
867 | 873 | // Thumbnail |
868 | | - if( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE ) && !$file->isDeleted( File::DELETED_FILE ) ) { |
| 874 | + if( $file->isMissing() ) { |
| 875 | + $row .= '</td><td><strong class="error">' . wfMsgHtml( 'filehist-missing' ) . '</strong>'; |
| 876 | + } elseif( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE ) && !$file->isDeleted( File::DELETED_FILE ) ) { |
869 | 877 | $params = array( |
870 | 878 | 'width' => '120', |
871 | 879 | 'height' => '120', |
Index: trunk/phase3/includes/filerepo/LocalFile.php |
— | — | @@ -49,6 +49,7 @@ |
50 | 50 | $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx) |
51 | 51 | $upgraded, # Whether the row was upgraded on load |
52 | 52 | $locked, # True if the image row is locked |
| 53 | + $missing, # True if file is not present in file system. Not to be cached in memcached |
53 | 54 | $deleted; # Bitfield akin to rev_deleted |
54 | 55 | |
55 | 56 | /**#@-*/ |
— | — | @@ -394,6 +395,14 @@ |
395 | 396 | /** getPath inherited */ |
396 | 397 | /** isVisible inhereted */ |
397 | 398 | |
| 399 | + function isMissing() { |
| 400 | + if( $this->missing === null ) { |
| 401 | + list( $fileExists ) = $this->repo->fileExistsBatch( array( $this->getVirtualUrl() ), FileRepo::FILES_ONLY ); |
| 402 | + $this->missing = !$fileExists; |
| 403 | + } |
| 404 | + return $this->missing; |
| 405 | + } |
| 406 | + |
398 | 407 | /** |
399 | 408 | * Return the width of the image |
400 | 409 | * |
— | — | @@ -1432,6 +1441,9 @@ |
1433 | 1442 | // them in a separate transaction, then run the file ops, then update the fa_name fields. |
1434 | 1443 | $this->doDBInserts(); |
1435 | 1444 | |
| 1445 | + // Removes non-existent file from the batch, so we don't get errors. |
| 1446 | + $this->deletionBatch = $this->removeNonexistentFiles( $this->deletionBatch ); |
| 1447 | + |
1436 | 1448 | // Execute the file deletion batch |
1437 | 1449 | $status = $this->file->repo->deleteBatch( $this->deletionBatch ); |
1438 | 1450 | if ( !$status->isGood() ) { |
— | — | @@ -1465,6 +1477,22 @@ |
1466 | 1478 | wfProfileOut( __METHOD__ ); |
1467 | 1479 | return $this->status; |
1468 | 1480 | } |
| 1481 | + |
| 1482 | + /** |
| 1483 | + * Removes non-existent files from a deletion batch. |
| 1484 | + */ |
| 1485 | + function removeNonexistentFiles( $batch ) { |
| 1486 | + $files = $newBatch = array(); |
| 1487 | + foreach( $batch as $batchItem ) { |
| 1488 | + list( $src, $dest ) = $batchItem; |
| 1489 | + $files[$src] = $this->file->repo->getVirtualUrl( 'public' ) . '/' . rawurlencode( $src ); |
| 1490 | + } |
| 1491 | + $result = $this->file->repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); |
| 1492 | + foreach( $batch as $batchItem ) |
| 1493 | + if( $result[$batchItem[0]] ) |
| 1494 | + $newBatch[] = $batchItem; |
| 1495 | + return $newBatch; |
| 1496 | + } |
1469 | 1497 | } |
1470 | 1498 | |
1471 | 1499 | #------------------------------------------------------------------------------ |
— | — | @@ -1657,6 +1685,9 @@ |
1658 | 1686 | $status->error( 'undelete-missing-filearchive', $id ); |
1659 | 1687 | } |
1660 | 1688 | |
| 1689 | + // Remove missing files from batch, so we don't get errors when undeleting them |
| 1690 | + $storeBatch = $this->removeNonexistentFiles( $storeBatch ); |
| 1691 | + |
1661 | 1692 | // Run the store batch |
1662 | 1693 | // Use the OVERWRITE_SAME flag to smooth over a common error |
1663 | 1694 | $storeStatus = $this->file->repo->storeBatch( $storeBatch, FileRepo::OVERWRITE_SAME ); |
— | — | @@ -1687,7 +1718,7 @@ |
1688 | 1719 | __METHOD__ ); |
1689 | 1720 | } |
1690 | 1721 | |
1691 | | - if( $status->successCount > 0 ) { |
| 1722 | + if( $status->successCount > 0 || !$storeBatch ) { // If store batch is empty (all files are missing), deletion is to be considered successful |
1692 | 1723 | if( !$exists ) { |
1693 | 1724 | wfDebug( __METHOD__." restored {$status->successCount} items, creating a new current\n" ); |
1694 | 1725 | |
— | — | @@ -1707,6 +1738,38 @@ |
1708 | 1739 | } |
1709 | 1740 | |
1710 | 1741 | /** |
| 1742 | + * Removes non-existent files from a store batch. |
| 1743 | + */ |
| 1744 | + function removeNonexistentFiles( $triplets ) { |
| 1745 | + $files = $filteredTriplets = array(); |
| 1746 | + foreach( $triplets as $file ) |
| 1747 | + $files[$file[0]] = $file[0]; |
| 1748 | + $result = $this->file->repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); |
| 1749 | + foreach( $triplets as $file ) |
| 1750 | + if( $result[$file[0]] ) |
| 1751 | + $filteredTriplets[] = $file; |
| 1752 | + return $filteredTriplets; |
| 1753 | + } |
| 1754 | + |
| 1755 | + /** |
| 1756 | + * Removes non-existent files from a cleanup batch. |
| 1757 | + */ |
| 1758 | + function removeNonexistentFromCleanup( $batch ) { |
| 1759 | + $files = $newBatch = array(); |
| 1760 | + $repo = $this->file->repo; |
| 1761 | + foreach( $batch as $file ) { |
| 1762 | + $files[$file] = $repo->getVirtualUrl( 'deleted' ) . '/' . |
| 1763 | + rawurlencode( $repo->getDeletedHashPath( $file ) . $file ); |
| 1764 | + } |
| 1765 | + |
| 1766 | + $result = $repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); |
| 1767 | + foreach( $batch as $file ) |
| 1768 | + if( $result[$file] ) |
| 1769 | + $newBatch[] = $file; |
| 1770 | + return $newBatch; |
| 1771 | + } |
| 1772 | + |
| 1773 | + /** |
1711 | 1774 | * Delete unused files in the deleted zone. |
1712 | 1775 | * This should be called from outside the transaction in which execute() was called. |
1713 | 1776 | */ |
— | — | @@ -1714,6 +1777,7 @@ |
1715 | 1778 | if ( !$this->cleanupBatch ) { |
1716 | 1779 | return $this->file->repo->newGood(); |
1717 | 1780 | } |
| 1781 | + $this->cleanupBatch = $this->removeNonexistentFromCleanup( $this->cleanupBatch ); |
1718 | 1782 | $status = $this->file->repo->cleanupDeletedBatch( $this->cleanupBatch ); |
1719 | 1783 | return $status; |
1720 | 1784 | } |
— | — | @@ -1793,11 +1857,7 @@ |
1794 | 1858 | $status = $repo->newGood(); |
1795 | 1859 | $triplets = $this->getMoveTriplets(); |
1796 | 1860 | |
1797 | | - $statusPreCheck = $this->checkFileExistence( 0 ); |
1798 | | - if( !$statusPreCheck->isOk() ) { |
1799 | | - wfDebugLog( 'imagemove', "Move of {$this->file->name} aborted due to pre-move file existence check failure" ); |
1800 | | - return $statusPreCheck; |
1801 | | - } |
| 1861 | + $triplets = $this->removeNonexistentFiles( $triplets ); |
1802 | 1862 | $statusDb = $this->doDBUpdates(); |
1803 | 1863 | wfDebugLog( 'imagemove', "Renamed {$this->file->name} in database: {$statusDb->successCount} successes, {$statusDb->failCount} failures" ); |
1804 | 1864 | $statusMove = $repo->storeBatch( $triplets, FSRepo::DELETE_SOURCE ); |
— | — | @@ -1805,12 +1865,6 @@ |
1806 | 1866 | if( !$statusMove->isOk() ) { |
1807 | 1867 | wfDebugLog( 'imagemove', "Error in moving files: " . $statusMove->getWikiText() ); |
1808 | 1868 | $this->db->rollback(); |
1809 | | - } else { |
1810 | | - $statusPostCheck = $this->checkFileExistence( 1 ); |
1811 | | - if( !$statusPostCheck->isOk() ) { |
1812 | | - // This clearly mustn't have happend. FSRepo::storeBatch should have given out an error in that case. |
1813 | | - wfDebugLog( 'imagemove', "ATTENTION! Move of {$this->file->name} has some files missing, while storeBatch() reported success" ); |
1814 | | - } |
1815 | 1869 | } |
1816 | 1870 | |
1817 | 1871 | $status->merge( $statusDb ); |
— | — | @@ -1874,21 +1928,20 @@ |
1875 | 1929 | } |
1876 | 1930 | |
1877 | 1931 | /* |
1878 | | - * Checks file existence. |
1879 | | - * Set $key = 0 for source files check |
1880 | | - * and $key = 1 for destination files check. |
| 1932 | + * Removes non-existent files from move batch. |
1881 | 1933 | */ |
1882 | | - function checkFileExistence( $key = 0 ) { |
| 1934 | + function removeNonexistentFiles( $triplets ) { |
1883 | 1935 | $files = array(); |
1884 | | - foreach( array_merge( array( $this->cur ), $this->olds ) as $file ) |
1885 | | - $files[$file[$key]] = $this->file->repo->getVirtualUrl() . '/public/' . rawurlencode( $file[$key] ); |
| 1936 | + foreach( $triplets as $file ) |
| 1937 | + $files[$file[0]] = $file[0]; |
1886 | 1938 | $result = $this->file->repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); |
1887 | | - $status = $this->file->repo->newGood(); |
1888 | | - foreach( $result as $filename => $exists ) |
1889 | | - if( !$exists ) { |
1890 | | - wfDebugLog( 'imagemove', "File {$filename} does not exist" ); |
1891 | | - $status->fatal( 'filenotfound', $filename ); |
| 1939 | + $filteredTriplets = array(); |
| 1940 | + foreach( $triplets as $file ) |
| 1941 | + if( $result[$file[0]] ) { |
| 1942 | + $filteredTriplets[] = $file; |
| 1943 | + } else { |
| 1944 | + wfDebugLog( 'imagemove', "File {$file[0]} does not exist" ); |
1892 | 1945 | } |
1893 | | - return $status; |
| 1946 | + return $filteredTriplets; |
1894 | 1947 | } |
1895 | 1948 | } |
Index: trunk/phase3/includes/filerepo/File.php |
— | — | @@ -306,6 +306,9 @@ |
307 | 307 | * or if it is an SVG image and SVG conversion is enabled. |
308 | 308 | */ |
309 | 309 | function canRender() { |
| 310 | + if( $this->isMissing() ) { |
| 311 | + return false; |
| 312 | + } |
310 | 313 | if ( !isset( $this->canRender ) ) { |
311 | 314 | $this->canRender = $this->getHandler() && $this->handler->canRender( $this ); |
312 | 315 | } |
— | — | @@ -1259,6 +1262,10 @@ |
1260 | 1263 | function redirectedFrom( $from ) { |
1261 | 1264 | $this->redirected = $from; |
1262 | 1265 | } |
| 1266 | + |
| 1267 | + function isMissing() { |
| 1268 | + return false; |
| 1269 | + } |
1263 | 1270 | } |
1264 | 1271 | /** |
1265 | 1272 | * Aliases for backwards compatibility with 1.6 |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -149,7 +149,7 @@ |
150 | 150 | * (bug 17714) Limited TIFF upload support now built in if 'tif' extension is |
151 | 151 | enabled. Image width and height are now recognized, and when using |
152 | 152 | ImageMagick, optional flattening to PNG or JPEG for inline display can be |
153 | | - enabled by setting $wgTiffThumbnailType |
| 153 | + enabled by setting $wgTiffThumbnailTypezz |
154 | 154 | * Renamed two input IDs on Special:Log from 'page' and 'user' to 'mw-log-page' |
155 | 155 | and 'mw-log-user', respectively |
156 | 156 | * Added $wgInvalidUsernameCharacters to disallow certain characters in |
— | — | @@ -179,8 +179,8 @@ |
180 | 180 | * (bug 16950) Show move log when viewing/creating a deleted page |
181 | 181 | * (bug 18242) Show the Subversion revision number per extensions in |
182 | 182 | Special:Version |
| 183 | +* (bug 18420) Missing file revisions are handled gracefully now |
183 | 184 | |
184 | | - |
185 | 185 | === Bug fixes in 1.15 === |
186 | 186 | * (bug 16968) Special:Upload no longer throws useless warnings. |
187 | 187 | * (bug 17000) Special:RevisionDelete now checks if the database is locked |