Index: trunk/phase3/includes/Status.php |
— | — | @@ -175,7 +175,10 @@ |
176 | 176 | $result = array(); |
177 | 177 | foreach ( $this->errors as $error ) { |
178 | 178 | if ( $error['type'] == 'error' ) |
179 | | - $result[] = $error['message']; |
| 179 | + if( $error['params'] ) |
| 180 | + $result[] = array_merge( array( $error['message'] ), $error['params'] ); |
| 181 | + else |
| 182 | + $result[] = $error['message']; |
180 | 183 | } |
181 | 184 | return $result; |
182 | 185 | } |
Index: trunk/phase3/includes/filerepo/LocalFile.php |
— | — | @@ -1789,6 +1789,11 @@ |
1790 | 1790 | $status = $repo->newGood(); |
1791 | 1791 | $triplets = $this->getMoveTriplets(); |
1792 | 1792 | |
| 1793 | + $statusPreCheck = $this->checkFileExistance( 0 ); |
| 1794 | + if( !$statusPreCheck->isOk() ) { |
| 1795 | + wfDebugLog( 'imagemove', "Move of {$this->file->name} aborted due to pre-move file existance check failure" ); |
| 1796 | + return $statusPreCheck; |
| 1797 | + } |
1793 | 1798 | $statusDb = $this->doDBUpdates(); |
1794 | 1799 | wfDebugLog( 'imagemove', "Renamed {$this->file->name} in database: {$statusDb->successCount} successes, {$statusDb->failCount} failures" ); |
1795 | 1800 | $statusMove = $repo->storeBatch( $triplets, FSRepo::DELETE_SOURCE ); |
— | — | @@ -1796,7 +1801,14 @@ |
1797 | 1802 | if( !$statusMove->isOk() ) { |
1798 | 1803 | wfDebugLog( 'imagemove', "Error in moving files: " . $statusMove->getWikiText() ); |
1799 | 1804 | $this->db->rollback(); |
| 1805 | + } else { |
| 1806 | + $statusPostCheck = $this->checkFileExistance( 1 ); |
| 1807 | + if( !$statusPostCheck->isOk() ) { |
| 1808 | + // This clearly mustn't have happend. FSRepo::storeBatch should have given out an error in that case. |
| 1809 | + wfDebugLog( 'imagemove', "ATTENTION! Move of {$this->file->name} has some files missing, while storeBatch() reported success" ); |
| 1810 | + } |
1800 | 1811 | } |
| 1812 | + |
1801 | 1813 | $status->merge( $statusDb ); |
1802 | 1814 | $status->merge( $statusMove ); |
1803 | 1815 | return $status; |
— | — | @@ -1856,4 +1868,23 @@ |
1857 | 1869 | } |
1858 | 1870 | return $triplets; |
1859 | 1871 | } |
| 1872 | + |
| 1873 | + /* |
| 1874 | + * Checks file existance. |
| 1875 | + * Set $key = 0 for source files check |
| 1876 | + * and $key = 1 for destination files check. |
| 1877 | + */ |
| 1878 | + function checkFileExistance( $key = 0 ) { |
| 1879 | + $files = array(); |
| 1880 | + foreach( array_merge( array( $this->cur ), $this->olds ) as $file ) |
| 1881 | + $files[$file[$key]] = $this->file->repo->getVirtualUrl() . '/public/' . rawurlencode( $file[$key] ); |
| 1882 | + $result = $this->file->repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); |
| 1883 | + $status = $this->file->repo->newGood(); |
| 1884 | + foreach( $result as $filename => $exists ) |
| 1885 | + if( !$exists ) { |
| 1886 | + wfDebugLog( 'imagemove', "File {$filename} does not exist" ); |
| 1887 | + $status->fatal( 'filenotfound', $filename ); |
| 1888 | + } |
| 1889 | + return $status; |
| 1890 | + } |
1860 | 1891 | } |
Index: trunk/phase3/includes/filerepo/FSRepo.php |
— | — | @@ -213,6 +213,33 @@ |
214 | 214 | } |
215 | 215 | |
216 | 216 | /** |
| 217 | + * Checks existance of specified array of files. |
| 218 | + * |
| 219 | + * @param array $files URLs of files to check |
| 220 | + * @param integer $flags Bitwise combination of the following flags: |
| 221 | + * self::FILES_ONLY Mark file as existing only if it is a file (not directory) |
| 222 | + * @return Either array of files and existance flags, or false |
| 223 | + */ |
| 224 | + function fileExistsBatch( $files, $flags = 0 ) { |
| 225 | + if ( !file_exists( $this->directory ) || !is_readable( $this->directory ) ) { |
| 226 | + return false; |
| 227 | + } |
| 228 | + $result = array(); |
| 229 | + foreach ( $files as $key => $file ) { |
| 230 | + if ( self::isVirtualUrl( $file ) ) { |
| 231 | + $file = $this->resolveVirtualUrl( $file ); |
| 232 | + } |
| 233 | + if( $flags & self::FILES_ONLY ) { |
| 234 | + $result[$key] = is_file( $file ); |
| 235 | + } else { |
| 236 | + $result[$key] = file_exists( $file ); |
| 237 | + } |
| 238 | + } |
| 239 | + |
| 240 | + return $result; |
| 241 | + } |
| 242 | + |
| 243 | + /** |
217 | 244 | * Take all available measures to prevent web accessibility of new deleted |
218 | 245 | * directories, in case the user has not configured offline storage |
219 | 246 | */ |
Index: trunk/phase3/includes/filerepo/FileRepo.php |
— | — | @@ -6,6 +6,7 @@ |
7 | 7 | * @ingroup FileRepo |
8 | 8 | */ |
9 | 9 | abstract class FileRepo { |
| 10 | + const FILES_ONLY = 1; |
10 | 11 | const DELETE_SOURCE = 1; |
11 | 12 | const FIND_PRIVATE = 1; |
12 | 13 | const FIND_IGNORE_REDIRECT = 2; |
Index: trunk/phase3/includes/Title.php |
— | — | @@ -2664,6 +2664,18 @@ |
2665 | 2665 | return $err; |
2666 | 2666 | } |
2667 | 2667 | |
| 2668 | + // If it is a file, more it first. It is done before all other moving stuff is done because it's hard to revert |
| 2669 | + $dbw = wfGetDB( DB_MASTER ); |
| 2670 | + if( $this->getNamespace() == NS_FILE ) { |
| 2671 | + $file = wfLocalFile( $this ); |
| 2672 | + if( $file->exists() ) { |
| 2673 | + $status = $file->move( $nt ); |
| 2674 | + if( !$status->isOk() ) { |
| 2675 | + return $status->getErrorsArray(); |
| 2676 | + } |
| 2677 | + } |
| 2678 | + } |
| 2679 | + |
2668 | 2680 | $pageid = $this->getArticleID(); |
2669 | 2681 | $protected = $this->isProtected(); |
2670 | 2682 | if( $nt->exists() ) { |
— | — | @@ -2690,7 +2702,6 @@ |
2691 | 2703 | // we can't actually distinguish it from a default here, and it'll |
2692 | 2704 | // be set to the new title even though it really shouldn't. |
2693 | 2705 | // It'll get corrected on the next edit, but resetting cl_timestamp. |
2694 | | - $dbw = wfGetDB( DB_MASTER ); |
2695 | 2706 | $dbw->update( 'categorylinks', |
2696 | 2707 | array( |
2697 | 2708 | 'cl_sortkey' => $nt->getPrefixedText(), |
— | — | @@ -2873,18 +2884,6 @@ |
2874 | 2885 | $redirectSuppressed = true; |
2875 | 2886 | } |
2876 | 2887 | |
2877 | | - # Move an image if this is a file |
2878 | | - if( $this->getNamespace() == NS_FILE ) { |
2879 | | - $file = wfLocalFile( $this ); |
2880 | | - if( $file->exists() ) { |
2881 | | - $status = $file->move( $nt ); |
2882 | | - if( !$status->isOk() ) { |
2883 | | - $dbw->rollback(); |
2884 | | - return $status->getErrorsArray(); |
2885 | | - } |
2886 | | - } |
2887 | | - } |
2888 | | - |
2889 | 2888 | # Log the move |
2890 | 2889 | $log = new LogPage( 'move' ); |
2891 | 2890 | $log->addEntry( 'move_redir', $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) ); |
— | — | @@ -2970,18 +2969,6 @@ |
2971 | 2970 | $redirectSuppressed = true; |
2972 | 2971 | } |
2973 | 2972 | |
2974 | | - # Move an image if this is a file |
2975 | | - if( $this->getNamespace() == NS_FILE ) { |
2976 | | - $file = wfLocalFile( $this ); |
2977 | | - if( $file->exists() ) { |
2978 | | - $status = $file->move( $nt ); |
2979 | | - if( !$status->isOk() ) { |
2980 | | - $dbw->rollback(); |
2981 | | - return $status->getErrorsArray(); |
2982 | | - } |
2983 | | - } |
2984 | | - } |
2985 | | - |
2986 | 2973 | # Log the move |
2987 | 2974 | $log = new LogPage( 'move' ); |
2988 | 2975 | $log->addEntry( 'move', $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) ); |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -308,6 +308,7 @@ |
309 | 309 | * (bug 18190) Proper parsing in MediaWiki:Sharedupload message |
310 | 310 | * (bug 17617) HTML cleanup for ImagePage |
311 | 311 | * (bug 17964) namespaceDupes.php no longer fails on an empty interwiki table |
| 312 | +* Improved error handling for image moving |
312 | 313 | |
313 | 314 | == API changes in 1.15 == |
314 | 315 | * (bug 16858) Revamped list=deletedrevs to make listing deleted contributions |