Index: trunk/phase3/maintenance/language/messages.inc |
— | — | @@ -1362,6 +1362,7 @@ |
1363 | 1363 | 'backend-fail-closetemp', |
1364 | 1364 | 'backend-fail-read', |
1365 | 1365 | 'backend-fail-create', |
| 1366 | + 'backend-fail-readonly' |
1366 | 1367 | ), |
1367 | 1368 | |
1368 | 1369 | 'lockmanager-errors' => array( |
Index: trunk/phase3/tests/phpunit/includes/filerepo/FileBackendTest.php |
— | — | @@ -433,6 +433,32 @@ |
434 | 434 | } |
435 | 435 | |
436 | 436 | /** |
| 437 | + * @dataProvider provider_testGetFileContents |
| 438 | + */ |
| 439 | + public function testGetFileContents( $src, $content ) { |
| 440 | + $this->pathsToPrune[] = $src; |
| 441 | + |
| 442 | + $status = $this->backend->doOperation( |
| 443 | + array( 'op' => 'create', 'content' => $content, 'dst' => $src ) ); |
| 444 | + $this->assertEquals( true, $status->isOK(), "Creation of file at $src succeeded." ); |
| 445 | + |
| 446 | + $newContents = $this->backend->getFileContents( array( 'src' => $src ) ); |
| 447 | + $this->assertNotEquals( false, $newContents, "Read of file at $src succeeded." ); |
| 448 | + |
| 449 | + $this->assertEquals( $content, $newContents, "Contents read match data at $src." ); |
| 450 | + } |
| 451 | + |
| 452 | + function provider_testGetFileContents() { |
| 453 | + $cases = array(); |
| 454 | + |
| 455 | + $base = $this->singleBasePath(); |
| 456 | + $cases[] = array( "$base/cont1/b/z/some_file.txt", "some file contents" ); |
| 457 | + $cases[] = array( "$base/cont1/b/some-other_file.txt", "more file contents" ); |
| 458 | + |
| 459 | + return $cases; |
| 460 | + } |
| 461 | + |
| 462 | + /** |
437 | 463 | * @dataProvider provider_testGetLocalCopy |
438 | 464 | */ |
439 | 465 | public function testGetLocalCopy( $src, $content ) { |
— | — | @@ -460,7 +486,7 @@ |
461 | 487 | } |
462 | 488 | |
463 | 489 | /** |
464 | | - * @dataProvider provider_testGetReference |
| 490 | + * @dataProvider provider_testGetLocalReference |
465 | 491 | */ |
466 | 492 | public function testGetLocalReference( $src, $content ) { |
467 | 493 | $this->pathsToPrune[] = $src; |
— | — | @@ -476,7 +502,7 @@ |
477 | 503 | $this->assertNotEquals( false, $contents, "Local copy of $src exists." ); |
478 | 504 | } |
479 | 505 | |
480 | | - function provider_testGetReference() { |
| 506 | + function provider_testGetLocalReference() { |
481 | 507 | $cases = array(); |
482 | 508 | |
483 | 509 | $base = $this->singleBasePath(); |
Index: trunk/phase3/includes/filerepo/backend/FileBackendMultiWrite.php |
— | — | @@ -210,6 +210,21 @@ |
211 | 211 | } |
212 | 212 | |
213 | 213 | /** |
| 214 | + * @see FileBackendBase::getFileContents() |
| 215 | + */ |
| 216 | + function getFileContents( array $params ) { |
| 217 | + # Hit all backends in case of failed operations (out of sync) |
| 218 | + foreach ( $this->backends as $backend ) { |
| 219 | + $realParams = $this->substOpPaths( $params, $backend ); |
| 220 | + $data = $backend->getFileContents( $realParams ); |
| 221 | + if ( $data !== false ) { |
| 222 | + return $data; |
| 223 | + } |
| 224 | + } |
| 225 | + return false; |
| 226 | + } |
| 227 | + |
| 228 | + /** |
214 | 229 | * @see FileBackendBase::getFileSha1Base36() |
215 | 230 | */ |
216 | 231 | function getFileSha1Base36( array $params ) { |
Index: trunk/phase3/includes/filerepo/backend/FileBackend.php |
— | — | @@ -28,6 +28,7 @@ |
29 | 29 | abstract class FileBackendBase { |
30 | 30 | protected $name; // unique backend name |
31 | 31 | protected $wikiId; // unique wiki name |
| 32 | + protected $readOnly; // string |
32 | 33 | /** @var LockManager */ |
33 | 34 | protected $lockManager; |
34 | 35 | |
— | — | @@ -36,9 +37,11 @@ |
37 | 38 | * This should only be called from within FileBackendGroup. |
38 | 39 | * |
39 | 40 | * $config includes: |
40 | | - * 'name' : The name of this backend |
| 41 | + * 'name' : The unique name of this backend |
41 | 42 | * 'wikiId' : Prefix to container names that is unique to this wiki |
42 | | - * 'lockManager' : Registered name of the file lock manager to use |
| 43 | + * 'lockManager' : Registered name of a file lock manager to use |
| 44 | + * 'readOnly' : Write operations are disallowed if this is a non-empty string. |
| 45 | + * It should be an explanation for the backend being read-only. |
43 | 46 | * |
44 | 47 | * @param $config Array |
45 | 48 | */ |
— | — | @@ -48,6 +51,9 @@ |
49 | 52 | ? $config['wikiId'] |
50 | 53 | : wfWikiID(); |
51 | 54 | $this->lockManager = LockManagerGroup::singleton()->get( $config['lockManager'] ); |
| 55 | + $this->readOnly = isset( $config['readOnly'] ) |
| 56 | + ? (string)$config['readOnly'] |
| 57 | + : ''; |
52 | 58 | } |
53 | 59 | |
54 | 60 | /** |
— | — | @@ -149,6 +155,9 @@ |
150 | 156 | * @return Status |
151 | 157 | */ |
152 | 158 | final public function doOperations( array $ops, array $opts = array() ) { |
| 159 | + if ( $this->readOnly != '' ) { |
| 160 | + return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly ); |
| 161 | + } |
153 | 162 | if ( empty( $opts['ignoreErrors'] ) ) { // sanity |
154 | 163 | unset( $opts['nonLocking'] ); |
155 | 164 | unset( $opts['allowStale'] ); |
— | — | @@ -320,30 +329,43 @@ |
321 | 330 | abstract public function fileExists( array $params ); |
322 | 331 | |
323 | 332 | /** |
324 | | - * Get a SHA-1 hash of the file at a storage path in the backend. |
| 333 | + * Get the last-modified timestamp of the file at a storage path. |
325 | 334 | * |
326 | 335 | * $params include: |
327 | 336 | * src : source storage path |
328 | 337 | * latest : use the latest available data |
329 | 338 | * |
330 | 339 | * @param $params Array |
331 | | - * @return string|false Hash string or false on failure |
| 340 | + * @return string|false TS_MW timestamp or false on failure |
332 | 341 | */ |
333 | | - abstract public function getFileSha1Base36( array $params ); |
| 342 | + abstract public function getFileTimestamp( array $params ); |
334 | 343 | |
335 | 344 | /** |
336 | | - * Get the last-modified timestamp of the file at a storage path. |
| 345 | + * Get the contents of a file at a storage path in the backend. |
| 346 | + * This should be avoided for potentially large files. |
337 | 347 | * |
338 | 348 | * $params include: |
339 | 349 | * src : source storage path |
340 | 350 | * latest : use the latest available data |
341 | 351 | * |
342 | 352 | * @param $params Array |
343 | | - * @return string|false TS_MW timestamp or false on failure |
| 353 | + * @return string|false Returns false on failure |
344 | 354 | */ |
345 | | - abstract public function getFileTimestamp( array $params ); |
| 355 | + abstract public function getFileContents( array $params ); |
346 | 356 | |
347 | 357 | /** |
| 358 | + * Get a SHA-1 hash of the file at a storage path in the backend. |
| 359 | + * |
| 360 | + * $params include: |
| 361 | + * src : source storage path |
| 362 | + * latest : use the latest available data |
| 363 | + * |
| 364 | + * @param $params Array |
| 365 | + * @return string|false Hash string or false on failure |
| 366 | + */ |
| 367 | + abstract public function getFileSha1Base36( array $params ); |
| 368 | + |
| 369 | + /** |
348 | 370 | * Get the properties of the file at a storage path in the backend. |
349 | 371 | * Returns FSFile::placeholderProps() on failure. |
350 | 372 | * |
— | — | @@ -754,6 +776,20 @@ |
755 | 777 | } |
756 | 778 | |
757 | 779 | /** |
| 780 | + * @see FileBackendBase::getFileContents() |
| 781 | + */ |
| 782 | + public function getFileContents( array $params ) { |
| 783 | + $tmpFile = $this->getLocalReference( $params ); |
| 784 | + if ( !$tmpFile ) { |
| 785 | + return false; |
| 786 | + } |
| 787 | + wfSuppressWarnings(); |
| 788 | + $data = file_get_contents( $tmpFile->getPath() ); |
| 789 | + wfRestoreWarnings(); |
| 790 | + return $data; |
| 791 | + } |
| 792 | + |
| 793 | + /** |
758 | 794 | * @see FileBackendBase::getFileSha1Base36() |
759 | 795 | */ |
760 | 796 | public function getFileSha1Base36( array $params ) { |
Index: trunk/phase3/languages/messages/MessagesEn.php |
— | — | @@ -2253,6 +2253,7 @@ |
2254 | 2254 | 'backend-fail-closetemp' => 'Could not close temporary file.', |
2255 | 2255 | 'backend-fail-read' => 'Could not read file $1.', |
2256 | 2256 | 'backend-fail-create' => 'Could not create file $1.', |
| 2257 | +'backend-fail-readonly' => 'The backend "$1" is currently read-only. The reason given is: "$2"', |
2257 | 2258 | |
2258 | 2259 | # Lock manager |
2259 | 2260 | 'lockmanager-notlocked' => 'Could not unlock "$1"; it is not locked.', |