Index: branches/FileBackend/phase3/includes/filerepo/ForeignAPIRepo.php |
— | — | @@ -40,12 +40,14 @@ |
41 | 41 | protected $mFileExists = array(); |
42 | 42 | |
43 | 43 | function __construct( $info ) { |
| 44 | + global $wgLocalFileRepo, $wgUploadDirectory; |
| 45 | + if ( !isset( $info['directory'] ) ) { // b/c |
| 46 | + $info['directory'] = $wgUploadDirectory; |
| 47 | + } |
44 | 48 | parent::__construct( $info ); |
45 | | - global $wgUploadDirectory; |
46 | 49 | |
47 | 50 | // http://commons.wikimedia.org/w/api.php |
48 | 51 | $this->mApiBase = isset( $info['apibase'] ) ? $info['apibase'] : null; |
49 | | - $this->directory = isset( $info['directory'] ) ? $info['directory'] : $wgUploadDirectory; |
50 | 52 | |
51 | 53 | if( isset( $info['apiThumbCacheExpiry'] ) ) { |
52 | 54 | $this->apiThumbCacheExpiry = $info['apiThumbCacheExpiry']; |
— | — | @@ -59,17 +61,11 @@ |
60 | 62 | } |
61 | 63 | // If we can cache thumbs we can guess sane defaults for these |
62 | 64 | if( $this->canCacheThumbs() && !$this->url ) { |
63 | | - global $wgLocalFileRepo; |
64 | 65 | $this->url = $wgLocalFileRepo['url']; |
65 | 66 | } |
66 | 67 | if( $this->canCacheThumbs() && !$this->thumbUrl ) { |
67 | 68 | $this->thumbUrl = $this->url . '/thumb'; |
68 | 69 | } |
69 | | - if ( isset( $info['thumbDir'] ) ) { |
70 | | - $this->thumbDir = $info['thumbDir']; |
71 | | - } else { |
72 | | - $this->thumbDir = "{$this->directory}/thumb"; |
73 | | - } |
74 | 70 | } |
75 | 71 | |
76 | 72 | /** |
— | — | @@ -280,9 +276,9 @@ |
281 | 277 | $localFilename = $localPath . "/" . $fileName; |
282 | 278 | $localUrl = $this->getZoneUrl( 'thumb' ) . "/" . $this->getHashPath( $name ) . rawurlencode( $name ) . "/" . rawurlencode( $fileName ); |
283 | 279 | |
284 | | - if( $this->repo->fileExists( $localFilename ) && isset( $metadata['timestamp'] ) ) { |
| 280 | + if( $this->fileExists( $localFilename ) && isset( $metadata['timestamp'] ) ) { |
285 | 281 | wfDebug( __METHOD__ . " Thumbnail was already downloaded before\n" ); |
286 | | - $modified = $this->repo->getFileTimestamp( $localFilename ); |
| 282 | + $modified = $this->getFileTimestamp( $localFilename ); |
287 | 283 | $remoteModified = strtotime( $metadata['timestamp'] ); |
288 | 284 | $current = time(); |
289 | 285 | $diff = abs( $modified - $current ); |
— | — | @@ -302,7 +298,7 @@ |
303 | 299 | |
304 | 300 | # @todo FIXME: Delete old thumbs that aren't being used. Maintenance script? |
305 | 301 | wfSuppressWarnings(); |
306 | | - $backend = $this->repo->getBackend(); |
| 302 | + $backend = $this->getBackend(); |
307 | 303 | $op = array( 'op' => 'create', 'dest' => $localFilename, 'content' => $thumb ); |
308 | 304 | if( !$backend->doOperation( $op )->isOK() ) { |
309 | 305 | wfRestoreWarnings(); |
— | — | @@ -331,17 +327,14 @@ |
332 | 328 | } |
333 | 329 | |
334 | 330 | /** |
335 | | - * Get the local directory corresponding to one of the three basic zones |
| 331 | + * Get the local directory corresponding to one of the basic zones |
336 | 332 | */ |
337 | 333 | function getZonePath( $zone ) { |
338 | | - switch ( $zone ) { |
339 | | - case 'public': |
340 | | - return $this->directory; |
341 | | - case 'thumb': |
342 | | - return $this->thumbDir; |
343 | | - default: |
344 | | - return false; |
| 334 | + $supported = array( 'public', 'thumb' ); |
| 335 | + if ( in_array( $zone, $supported ) ) { |
| 336 | + return parent::getZonePath( $zone ); |
345 | 337 | } |
| 338 | + return false; |
346 | 339 | } |
347 | 340 | |
348 | 341 | /** |
Index: branches/FileBackend/phase3/includes/filerepo/file/FSFile.php |
— | — | @@ -38,7 +38,7 @@ |
39 | 39 | * @return bool |
40 | 40 | */ |
41 | 41 | public function exists() { |
42 | | - return ( file_exists( $this->path ) && !is_dir( $this->path ) ); |
| 42 | + return is_file( $this->path ); |
43 | 43 | } |
44 | 44 | |
45 | 45 | /** |
— | — | @@ -47,7 +47,9 @@ |
48 | 48 | * @return string|false TS_MW timestamp or false on failure |
49 | 49 | */ |
50 | 50 | public function getTimestamp() { |
| 51 | + wfSuppressWarnings(); |
51 | 52 | $timestamp = filemtime( $this->path ); |
| 53 | + wfRestoreWarnings(); |
52 | 54 | if ( $timestamp !== false ) { |
53 | 55 | $timestamp = wfTimestamp( TS_MW, $timestamp ); |
54 | 56 | } |
Index: branches/FileBackend/phase3/includes/filerepo/backend/FileOp.php |
— | — | @@ -14,7 +14,7 @@ |
15 | 15 | abstract class FileOp { |
16 | 16 | /** $var Array */ |
17 | 17 | protected $params; |
18 | | - /** $var FileBackend */ |
| 18 | + /** $var FileBackendBase */ |
19 | 19 | protected $backend; |
20 | 20 | |
21 | 21 | protected $state; |
— | — | @@ -32,7 +32,7 @@ |
33 | 33 | * @params $backend FileBackend |
34 | 34 | * @params $params Array |
35 | 35 | */ |
36 | | - final public function __construct( FileBackend $backend, array $params ) { |
| 36 | + final public function __construct( FileBackendBase $backend, array $params ) { |
37 | 37 | $this->backend = $backend; |
38 | 38 | $this->params = $params; |
39 | 39 | $this->state = self::STATE_NEW; |
— | — | @@ -157,7 +157,8 @@ |
158 | 158 | protected function checkAndBackupDest() { |
159 | 159 | $status = Status::newGood(); |
160 | 160 | // Check if a file already exists at the destination |
161 | | - if ( !$this->backend->fileExists( $this->params['dest'] ) ) { |
| 161 | + $params = array( 'source' => $this->params['dest'] ); |
| 162 | + if ( !$this->backend->fileExists( $params ) ) { |
162 | 163 | return $status; // nothing to do |
163 | 164 | } |
164 | 165 | |
Index: branches/FileBackend/phase3/includes/filerepo/backend/FSFileBackend.php |
— | — | @@ -375,7 +375,7 @@ |
376 | 376 | if ( $source === null ) { |
377 | 377 | return false; // invalid storage path |
378 | 378 | } |
379 | | - return file_exists( $source ) && is_file( $source ); |
| 379 | + return is_file( $source ); |
380 | 380 | } |
381 | 381 | |
382 | 382 | function getHashType() { |
— | — | @@ -519,14 +519,18 @@ |
520 | 520 | * @param $directory string |
521 | 521 | */ |
522 | 522 | public function __construct( $directory ) { |
523 | | - $this->directory = (string)$directory; |
| 523 | + // Removing any trailing slash |
| 524 | + if ( substr( $this->directory, -1 ) === '/' ) { |
| 525 | + $this->directory = substr( $this->directory, 0, -1 ); |
| 526 | + } |
| 527 | + $this->directory = realpath( $directory ); |
524 | 528 | } |
525 | 529 | |
526 | 530 | private function load() { |
527 | 531 | if ( !$this->loaded ) { |
528 | 532 | $this->loaded = true; |
529 | 533 | // If we get an invalid directory then the result is an empty list |
530 | | - if ( file_exists( $this->directory ) && is_dir( $this->directory ) ) { |
| 534 | + if ( is_dir( $this->directory ) ) { |
531 | 535 | $handle = opendir( $this->directory ); |
532 | 536 | if ( $handle ) { |
533 | 537 | $this->pushDirectory( $this->directory, $handle ); |
— | — | @@ -565,36 +569,49 @@ |
566 | 570 | } |
567 | 571 | |
568 | 572 | function nextFile() { |
569 | | - $set = $this->currentDirectory(); |
570 | | - if ( !$set ) { |
| 573 | + if ( !$this->currentDirectory() ) { |
571 | 574 | return false; // nothing else to scan |
572 | 575 | } |
573 | | - list( $dir, $handle ) = $set; |
| 576 | + # Next file under the current directory (and subdirectories). |
| 577 | + # This may advance the current directory down to a descendent. |
| 578 | + # The current directory is set to the parent if nothing is found. |
| 579 | + $nextFile = $this->nextFileBelowCurrent(); |
| 580 | + if ( $nextFile !== false ) { |
| 581 | + return $nextFile; |
| 582 | + } else { |
| 583 | + # Scan the higher directories |
| 584 | + return $this->nextFile(); |
| 585 | + } |
| 586 | + } |
| 587 | + |
| 588 | + function nextFileBelowCurrent() { |
| 589 | + list( $dir, $handle ) = $this->currentDirectory(); |
574 | 590 | while ( false !== ( $file = readdir( $handle ) ) ) { |
575 | 591 | // Exclude '.' and '..' as well .svn or .lock type files |
576 | 592 | if ( $file[0] !== '.' ) { |
| 593 | + $path = "{$dir}/{$file}"; |
577 | 594 | // If the first thing we find is a file, then return it. |
578 | 595 | // If the first thing we find is a directory, then return |
579 | 596 | // the first file that it contains (via recursion). |
580 | 597 | // We exclude symlink dirs in order to avoid cycles. |
581 | | - if ( is_dir( "{$dir}/{$file}" ) && !is_link( "{$dir}/{$file}" ) ) { |
582 | | - $subHandle = opendir( "$dir/$file" ); |
| 598 | + if ( is_dir( $path ) && !is_link( $path ) ) { |
| 599 | + $subHandle = opendir( $path ); |
583 | 600 | if ( $subHandle ) { |
584 | | - $this->pushDirectory( "{$dir}/{$file}", $subHandle ); |
585 | | - $nextFile = $this->nextFile(); |
| 601 | + $this->pushDirectory( $path, $subHandle ); |
| 602 | + $nextFile = $this->nextFileBelowCurrent(); |
586 | 603 | if ( $nextFile !== false ) { |
587 | 604 | return $nextFile; // found the next one! |
588 | 605 | } |
589 | 606 | } |
590 | | - } elseif ( is_file( "{$dir}/{$file}" ) ) { |
591 | | - return "{$dir}/{$file}"; // found the next one! |
| 607 | + } elseif ( is_file( $path ) ) { |
| 608 | + return $path; // found the next one! |
592 | 609 | } |
593 | 610 | } |
594 | 611 | } |
595 | | - # If we didn't find anything in this directory, |
596 | | - # then back out and scan the other higher directories |
| 612 | + # If we didn't find anything else in this directory, |
| 613 | + # then back out so we scan the other higher directories |
597 | 614 | $this->popDirectory(); |
598 | | - return $this->nextFile(); |
| 615 | + return false; |
599 | 616 | } |
600 | 617 | |
601 | 618 | private function currentDirectory() { |
Index: branches/FileBackend/phase3/includes/filerepo/backend/FileBackend.php |
— | — | @@ -84,7 +84,7 @@ |
85 | 85 | * @return Status |
86 | 86 | */ |
87 | 87 | final public function doOperation( $op ) { |
88 | | - return $this->doOperation( $op ); |
| 88 | + return $this->doOperations( array( $op ) ); |
89 | 89 | } |
90 | 90 | |
91 | 91 | /** |
— | — | @@ -400,7 +400,7 @@ |
401 | 401 | unset( $params['op'] ); // don't need this |
402 | 402 | unset( $params['ignoreErrors'] ); // don't need this |
403 | 403 | // Append the FileOp class |
404 | | - $performOps[] = new $class( $params ); |
| 404 | + $performOps[] = new $class( $this, $params ); |
405 | 405 | } else { |
406 | 406 | throw new MWException( "Operation `$opName` is not supported." ); |
407 | 407 | } |
Index: branches/FileBackend/phase3/includes/filerepo/FileRepo.php |
— | — | @@ -43,7 +43,9 @@ |
44 | 44 | function __construct( $info ) { |
45 | 45 | // Required settings |
46 | 46 | $this->name = $info['name']; |
47 | | - $this->url = $info['url']; |
| 47 | + $this->url = isset( $info['url'] ) |
| 48 | + ? $info['url'] |
| 49 | + : false; // a subclass will need to set the URL (e.g. ForeignAPIRepo) |
48 | 50 | |
49 | 51 | // Optional settings that can have no value |
50 | 52 | $optionalSettings = array( |
— | — | @@ -306,7 +308,10 @@ |
307 | 309 | return null; |
308 | 310 | } |
309 | 311 | $backendName = $this->backend->getName(); |
310 | | - return "mwstore://$backendName/{$container}/{$base}"; |
| 312 | + if ( $base != '' ) { // may not be set |
| 313 | + $base = "/{$base}"; |
| 314 | + } |
| 315 | + return "mwstore://$backendName/{$container}{$base}"; |
311 | 316 | } |
312 | 317 | |
313 | 318 | /** |
Index: branches/FileBackend/phase3/includes/AutoLoader.php |
— | — | @@ -489,6 +489,7 @@ |
490 | 490 | 'FileBackend' => 'includes/filerepo/backend/FileBackend.php', |
491 | 491 | 'FileBackendMultiWrite' => 'includes/filerepo/backend/FileBackendMultiWrite.php', |
492 | 492 | 'FSFileBackend' => 'includes/filerepo/backend/FSFileBackend.php', |
| 493 | + 'FileIterator' => 'includes/filerepo/backend/FSFileBackend.php', |
493 | 494 | 'FileLockManager' => 'includes/filerepo/backend/FileLockManager.php', |
494 | 495 | 'FSFileLockManager' => 'includes/filerepo/backend/FileLockManager.php', |
495 | 496 | 'DBFileLockManager' => 'includes/filerepo/backend/FileLockManager.php', |