* Added FileBackendBase::getFileContents() function with a default FileBackend version.
authorAaron Schulz <aaron@users.mediawiki.org>
Wed, 4 Jan 2012 02:15:07 +0000 (02:15 +0000)
committerAaron Schulz <aaron@users.mediawiki.org>
Wed, 4 Jan 2012 02:15:07 +0000 (02:15 +0000)
* Added read-only mode to FileBackendBase config.
* Moved FileBackendBase::getFileTimestamp() up slightly.

includes/filerepo/backend/FileBackend.php
includes/filerepo/backend/FileBackendMultiWrite.php
languages/messages/MessagesEn.php
maintenance/language/messages.inc
tests/phpunit/includes/filerepo/FileBackendTest.php

index 07f7ed7..71bc5e2 100644 (file)
@@ -28,6 +28,7 @@
 abstract class FileBackendBase {
        protected $name; // unique backend name
        protected $wikiId; // unique wiki name
+       protected $readOnly; // string
        /** @var LockManager */
        protected $lockManager;
 
@@ -36,9 +37,11 @@ abstract class FileBackendBase {
         * This should only be called from within FileBackendGroup.
         * 
         * $config includes:
-        *     'name'        : The name of this backend
+        *     'name'        : The unique name of this backend
         *     'wikiId'      : Prefix to container names that is unique to this wiki
-        *     'lockManager' : Registered name of the file lock manager to use
+        *     'lockManager' : Registered name of a file lock manager to use
+        *     'readOnly'    : Write operations are disallowed if this is a non-empty string.
+        *                     It should be an explanation for the backend being read-only.
         * 
         * @param $config Array
         */
@@ -48,6 +51,9 @@ abstract class FileBackendBase {
                        ? $config['wikiId']
                        : wfWikiID();
                $this->lockManager = LockManagerGroup::singleton()->get( $config['lockManager'] );
+               $this->readOnly = isset( $config['readOnly'] )
+                       ? (string)$config['readOnly']
+                       : '';
        }
 
        /**
@@ -149,6 +155,9 @@ abstract class FileBackendBase {
         * @return Status
         */
        final public function doOperations( array $ops, array $opts = array() ) {
+               if ( $this->readOnly != '' ) {
+                       return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
+               }
                if ( empty( $opts['ignoreErrors'] ) ) { // sanity
                        unset( $opts['nonLocking'] );
                        unset( $opts['allowStale'] );
@@ -320,28 +329,41 @@ abstract class FileBackendBase {
        abstract public function fileExists( array $params );
 
        /**
-        * Get a SHA-1 hash of the file at a storage path in the backend.
+        * Get the last-modified timestamp of the file at a storage path.
         * 
         * $params include:
         *     src    : source storage path
         *     latest : use the latest available data
         * 
         * @param $params Array
-        * @return string|false Hash string or false on failure
+        * @return string|false TS_MW timestamp or false on failure
         */
-       abstract public function getFileSha1Base36( array $params );
+       abstract public function getFileTimestamp( array $params );
 
        /**
-        * Get the last-modified timestamp of the file at a storage path.
+        * Get the contents of a file at a storage path in the backend.
+        * This should be avoided for potentially large files.
         * 
         * $params include:
         *     src    : source storage path
         *     latest : use the latest available data
         * 
         * @param $params Array
-        * @return string|false TS_MW timestamp or false on failure
+        * @return string|false Returns false on failure
         */
-       abstract public function getFileTimestamp( array $params );
+       abstract public function getFileContents( array $params );
+
+       /**
+        * Get a SHA-1 hash of the file at a storage path in the backend.
+        * 
+        * $params include:
+        *     src    : source storage path
+        *     latest : use the latest available data
+        * 
+        * @param $params Array
+        * @return string|false Hash string or false on failure
+        */
+       abstract public function getFileSha1Base36( array $params );
 
        /**
         * Get the properties of the file at a storage path in the backend.
@@ -753,6 +775,20 @@ abstract class FileBackend extends FileBackendBase {
                return Status::newGood();
        }
 
+       /**
+        * @see FileBackendBase::getFileContents()
+        */
+       public function getFileContents( array $params ) {
+               $tmpFile = $this->getLocalReference( $params );
+               if ( !$tmpFile ) {
+                       return false;
+               }
+               wfSuppressWarnings();
+               $data = file_get_contents( $tmpFile->getPath() );
+               wfRestoreWarnings();
+               return $data;
+       }
+
        /**
         * @see FileBackendBase::getFileSha1Base36()
         */
index 114424c..241858e 100644 (file)
@@ -209,6 +209,21 @@ class FileBackendMultiWrite extends FileBackendBase {
                return $this->backends[$this->masterIndex]->getFileTimestamp( $realParams );
        }
 
+       /**
+        * @see FileBackendBase::getFileContents()
+        */
+       function getFileContents( array $params ) {
+               # Hit all backends in case of failed operations (out of sync)
+               foreach ( $this->backends as $backend ) {
+                       $realParams = $this->substOpPaths( $params, $backend );
+                       $data = $backend->getFileContents( $realParams );
+                       if ( $data !== false ) {
+                               return $data;
+                       }
+               }
+               return false;
+       }
+
        /**
         * @see FileBackendBase::getFileSha1Base36()
         */
index f21b6fc..32aef6f 100644 (file)
@@ -2253,6 +2253,7 @@ If the problem persists, contact an [[Special:ListUsers/sysop|administrator]].',
 'backend-fail-closetemp'     => 'Could not close temporary file.',
 'backend-fail-read'          => 'Could not read file $1.',
 'backend-fail-create'        => 'Could not create file $1.',
+'backend-fail-readonly'      => 'The backend "$1" is currently read-only. The reason given is: "$2"',
 
 # Lock manager
 'lockmanager-notlocked'        => 'Could not unlock "$1"; it is not locked.',
index 3133d77..4ff4c61 100644 (file)
@@ -1362,6 +1362,7 @@ $wgMessageStructure = array(
                'backend-fail-closetemp',
                'backend-fail-read',
                'backend-fail-create',
+               'backend-fail-readonly'
        ),
 
        'lockmanager-errors' => array(
index aea98ce..c1cc55f 100644 (file)
@@ -432,6 +432,32 @@ class FileBackendTest extends MediaWikiTestCase {
                return $cases;
        }
 
+       /**
+        * @dataProvider provider_testGetFileContents
+        */
+       public function testGetFileContents( $src, $content ) {
+               $this->pathsToPrune[] = $src;
+
+               $status = $this->backend->doOperation(
+                       array( 'op' => 'create', 'content' => $content, 'dst' => $src ) );
+               $this->assertEquals( true, $status->isOK(), "Creation of file at $src succeeded." );
+
+               $newContents = $this->backend->getFileContents( array( 'src' => $src ) );
+               $this->assertNotEquals( false, $newContents, "Read of file at $src succeeded." );
+
+               $this->assertEquals( $content, $newContents, "Contents read match data at $src." );
+       }
+
+       function provider_testGetFileContents() {
+               $cases = array();
+
+               $base = $this->singleBasePath();
+               $cases[] = array( "$base/cont1/b/z/some_file.txt", "some file contents" );
+               $cases[] = array( "$base/cont1/b/some-other_file.txt", "more file contents" );
+
+               return $cases;
+       }
+
        /**
         * @dataProvider provider_testGetLocalCopy
         */
@@ -460,7 +486,7 @@ class FileBackendTest extends MediaWikiTestCase {
        }
 
        /**
-        * @dataProvider provider_testGetReference
+        * @dataProvider provider_testGetLocalReference
         */
        public function testGetLocalReference( $src, $content ) {
                $this->pathsToPrune[] = $src;
@@ -476,7 +502,7 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->assertNotEquals( false, $contents, "Local copy of $src exists." );
        }
 
-       function provider_testGetReference() {
+       function provider_testGetLocalReference() {
                $cases = array();
 
                $base = $this->singleBasePath();