http://www.mediawiki.org/wiki/Special:Code/MediaWiki/88390
Revision: 88390
Author: nelson
Date: 2011-05-18 20:26:10 +0000 (Wed, 18 May 2011)
Log Message:
-----------
Archived files now work. Req'd storeBatch, clearBatch, and fileExistsBatch to
work. Del/Restore next.
Modified Paths:
--------------
trunk/extensions/SwiftMedia/LocalSettings.php
trunk/extensions/SwiftMedia/SwiftMedia.body.php
trunk/extensions/SwiftMedia/SwiftMedia.i18n.php
trunk/extensions/SwiftMedia/wmf/rewrite.py
Added Paths:
-----------
trunk/extensions/SwiftMedia/copyover
Modified: trunk/extensions/SwiftMedia/LocalSettings.php
===================================================================
--- trunk/extensions/SwiftMedia/LocalSettings.php 2011-05-18 20:17:26 UTC
(rev 88389)
+++ trunk/extensions/SwiftMedia/LocalSettings.php 2011-05-18 20:26:10 UTC
(rev 88390)
@@ -149,10 +149,10 @@
'hashLevels' => $wgHashedUploadDirectory ? 2 : 0,
'thumbScriptUrl' => $wgThumbnailScriptPath,
'transformVia404' => !$wgGenerateThumbnailOnParse,
- 'deletedDir' => $wgDeletedDirectory,
- 'deletedHashLevels' => 3
+ #'deletedDir' => $wgDeletedDirectory,
+ #'deletedHashLevels' => 3
);
}
$wgDebugLogFile = "/var/www/debug/abcd";
-
+$wgShowExceptionDetails = true;
Modified: trunk/extensions/SwiftMedia/SwiftMedia.body.php
===================================================================
--- trunk/extensions/SwiftMedia/SwiftMedia.body.php 2011-05-18 20:17:26 UTC
(rev 88389)
+++ trunk/extensions/SwiftMedia/SwiftMedia.body.php 2011-05-18 20:26:10 UTC
(rev 88390)
@@ -551,22 +551,25 @@
$thumbTemp = tempnam( $wgTmpDirectory, 'transform_out_'
);
/* Fetch the image out of Swift */
- $auth = new CF_Authentication($this->swiftuser,
$this->key, NULL, $this->authurl);
- wfDebug( __METHOD__ . "Auth ", $this->swiftuser . ","
. $this->key . "," . $this->authurl . "\n");
+ $auth = new CF_Authentication($this->repo->swiftuser,
$this->repo->key, NULL, $this->repo->authurl);
$auth->authenticate();
$conn = new CF_Connection($auth);
- $container = $conn->get_container($this->container);
+ $container =
$conn->get_container($this->repo->container);
- // how does this return failure??
- $obj = $container->get_object($this->getRel());
- // we need to do a try here
+ // What do we do if the original doesn't exist??
+ try {
+ $obj = $container->get_object($this->getRel());
+ } catch (NoSuchObjectException $e) {
+ $obj = NULL;
+ }
+ // we need to do a try here, but let's see how it fails
first.
wfDebug( __METHOD__ . " writing to " .
$this->temp_path . "\n");
$obj->save_to_filename( $this->temp_path);
$thumb = $this->handler->doTransform( $this,
$thumbTemp, $thumbUrl, $params );
// Store the thumbnail into Swift, but in the thumb
version of the container.
- $container = $conn->get_container($this->container .
"%2Fthumb");
+ $container =
$conn->get_container($this->repo->container . "%2Fthumb");
wfDebug( __METHOD__ . "Creating thumb " .
$this->getRel() . "/" . $thumbName . "\n");
$obj = $container->create_object($this->getRel() . "/"
. $thumbName);
$thumbRel = $obj->load_from_filename($thumbTemp);
@@ -585,34 +588,19 @@
/**
* Fix thumbnail files from 1.4 or before, with extreme prejudice
+ * Upgrading directly from 1.4 to 1.8/SwiftMedia is not supported.
*/
function migrateThumbFile( $thumbName ) {
- $thumbDir = $this->getThumbPath();
- $thumbPath = "$thumbDir/$thumbName";
+ throw new MWException( __METHOD__.': not implemented' );
+ }
+ /**
+ * Get the public root directory of the repository.
+ */
+ function getRootDirectory() {
+ throw new MWException( __METHOD__.': not implemented' );
+ }
- if ( is_dir( $thumbPath ) ) {
- // Directory where file should be
- // This happened occasionally due to broken migration
code in 1.5
- // Rename to broken-*
- for ( $i = 0; $i < 100 ; $i++ ) {
- $broken = $this->repo->getZonePath( 'public' )
. "/broken-$i-$thumbName";
- if ( !file_exists( $broken ) ) {
- rename( $thumbPath, $broken );
- break;
- }
- }
- // Doesn't exist anymore
- clearstatcache();
- }
- if ( is_file( $thumbDir ) ) {
- // File where directory should be
- unlink( $thumbDir );
- // Doesn't exist anymore
- clearstatcache();
- }
- }
-
/** getHandler inherited */
/** iconThumb inherited */
/** getLastError inherited */
@@ -623,23 +611,13 @@
function getThumbnails() {
$this->load();
- $files = array();
- $dir = $this->getThumbPath();
-
- if ( is_dir( $dir ) ) {
- $handle = opendir( $dir );
-
- if ( $handle ) {
- while ( false !== ( $file = readdir( $handle )
) ) {
- if ( $file { 0 } != '.' ) {
- $files[] = $file;
- }
- }
-
- closedir( $handle );
- }
- }
-
+ $dir = $this->getRel();
+ $auth = new CF_Authentication($this->repo->swiftuser,
$this->repo->key, NULL, $this->repo->authurl);
+ $auth->authenticate();
+ $conn = new CF_Connection($auth);
+ $container = $conn->get_container($this->repo->container .
"%2Fthumb");
+ $files = $container->list_objects(0, NULL, $dir);
+ wfDebug( __METHOD__ . var_export($files, true) . "\n");
return $files;
}
@@ -688,23 +666,24 @@
// Delete thumbnails
$files = $this->getThumbnails();
- $dir = $this->getThumbPath();
$urls = array();
+ $auth = new CF_Authentication($this->repo->swiftuser,
$this->repo->key, NULL, $this->repo->authurl);
+ $auth->authenticate();
+ $conn = new CF_Connection($auth);
+ $container = $conn->get_container($this->repo->container .
"%2Fthumb");
foreach ( $files as $file ) {
+ // I have no idea how to implement this given that we
don't have paths in Swift
// Only remove files not in the
$wgExcludeFromThumbnailPurge configuration variable
- $ext = pathinfo( "$dir/$file", PATHINFO_EXTENSION );
- if ( in_array( $ext, $wgExcludeFromThumbnailPurge ) ) {
- continue;
- }
+ // $ext = pathinfo( "$dir/$file", PATHINFO_EXTENSION );
+ //if ( in_array( $ext, $wgExcludeFromThumbnailPurge ) )
{
+ // continue;
+ //}
+ $urls[] = $this->getThumbUrl($file);
# Check that the base file name is part of the thumb
name
# This is a basic sanity check to avoid erasing
unrelated directories
- if ( strpos( $file, $this->getName() ) !== false ) {
- $url = $this->getThumbUrl( $file );
- $urls[] = $url;
- @unlink( "$dir/$file" );
- }
+ $container->delete_object($file);
}
// Purge the squid
@@ -719,7 +698,7 @@
function getHistory( $limit = null, $start = null, $end = null, $inc =
true ) {
$dbr = $this->repo->getSlaveDB();
$tables = array( 'oldimage' );
- $fields = OldLocalFile::selectFields();
+ $fields = OldSwiftFile::selectFields();
$conds = $opts = $join_conds = array();
$eq = $inc ? '=' : '';
$conds[] = "oi_name = " . $dbr->addQuotes(
$this->title->getDBkey() );
@@ -741,7 +720,7 @@
$opts['ORDER BY'] = "oi_timestamp $order";
$opts['USE INDEX'] = array( 'oldimage' => 'oi_name_timestamp' );
- wfRunHooks( 'LocalFile::getHistory', array( &$this, &$tables,
&$fields,
+ wfRunHooks( 'SwiftFile::getHistory', array( &$this, &$tables,
&$fields,
&$conds, &$opts, &$join_conds ) );
$res = $dbr->select( $tables, $fields, $conds, __METHOD__,
$opts, $join_conds );
@@ -751,7 +730,7 @@
if ( $this->repo->oldFileFromRowFactory ) {
$r[] = call_user_func(
$this->repo->oldFileFromRowFactory, $row, $this->repo );
} else {
- $r[] = OldLocalFile::newFromRow( $row,
$this->repo );
+ $r[] = OldSwiftFile::newFromRow( $row,
$this->repo );
}
}
@@ -2040,14 +2019,14 @@
$bits = explode( '!', $oldName, 2 );
if ( count( $bits ) != 2 ) {
- wfDebug( "Invalid old file name: $oldName \n" );
+ wfDebug( "Old file name missing !: '$oldName'
\n" );
continue;
}
list( $timestamp, $filename ) = $bits;
if ( $this->oldName != $filename ) {
- wfDebug( "Invalid old file name: $oldName \n" );
+ wfDebug( "Old file name doesn't match:
'$oldName' \n" );
continue;
}
@@ -2156,7 +2135,7 @@
}
/**
- * Generate triplets for FSRepo::storeBatch().
+ * Generate triplets for storeBatch().
*/
function getMoveTriplets() {
$moves = array_merge( array( $this->cur ), $this->olds );
@@ -2236,10 +2215,10 @@
class SwiftRepo extends LocalRepo {
var $fileFactory = array( 'SwiftFile', 'newFromTitle' );
var $fileFactoryKey = array( 'SwiftFile', 'newFromKey' );
- var $oldFileFactory = array( 'OldLocalFile', 'newFromTitle' );
- var $oldFileFactoryKey = array( 'OldLocalFile', 'newFromKey' );
var $fileFromRowFactory = array( 'SwiftFile', 'newFromRow' );
- var $oldFileFromRowFactory = array( 'OldLocalFile', 'newFromRow' );
+ var $oldFileFactory = array( 'OldSwiftFile', 'newFromTitle' );
+ var $oldFileFactoryKey = array( 'OldSwiftFile', 'newFromKey' );
+ var $oldFileFromRowFactory = array( 'OldSwiftFile', 'newFromRow' );
function __construct( $info ) {
parent::__construct( $info );
@@ -2251,9 +2230,108 @@
$this->container= $info['container'];
}
+ /**
+ * Store a batch of files
+ *
+ * @param $triplets Array: (src,zone,dest) triplets as per store()
+ * @param $flags Integer: bitwise combination of the following flags:
+ * self::DELETE_SOURCE Delete the source file after upload
+ * self::OVERWRITE Overwrite an existing destination file
instead of failing
+ * self::OVERWRITE_SAME Overwrite the file if the destination
exists and has the
+ * same contents as the source
+ */
function storeBatch( $triplets, $flags = 0 ) {
- wfDebug( __METHOD__ . " $triplets\n" );
- return parent::storeBatch( $triplets, $flags);
+ wfDebug( __METHOD__ . ': Storing ' . count( $triplets ) .
+ " triplets; flags: {$flags}\n" );
+
+ // Validate each triplet
+ $status = $this->newGood();
+ foreach ( $triplets as $i => $triplet ) {
+ list( $srcPath, $dstZone, $dstRel ) = $triplet;
+
+ if ( !$this->validateFilename( $dstRel ) ) {
+ throw new MWException( 'Validation error in
$dstRel' );
+ }
+
+ // Check overwriting
+ if (0) { #FIXME
+ if ( !( $flags & self::OVERWRITE ) && file_exists(
$dstPath ) ) {
+ if ( $flags & self::OVERWRITE_SAME ) {
+ $hashSource = sha1_file( $srcPath );
+ $hashDest = sha1_file( $dstPath );
+ if ( $hashSource != $hashDest ) {
+ $status->fatal(
'fileexistserror', $dstPath );
+ }
+ } else {
+ $status->fatal( 'fileexistserror',
$dstPath );
+ }
+ }
+ }
+ }
+
+ // Abort now on failure
+ if ( !$status->ok ) {
+ return $status;
+ }
+
+ // Execute the store operation for each triplet
+ $auth = new CF_Authentication($this->swiftuser, $this->key,
NULL, $this->authurl);
+ $auth->authenticate();
+ $conn = new CF_Connection($auth);
+
+ foreach ( $triplets as $i => $triplet ) {
+ list( $srcPath, $dstZone, $dstRel ) = $triplet;
+
+ if (!self::isVirtualUrl( $srcPath )) {
+ throw new MWException( 'We require a virtual
URL here: $srcPath' );
+ }
+ $src = $this->resolveVirtualUrl( $srcPath );
+ list ($srcContainer, $srcRel) = $src;
+ $dstContainer = $this->getZoneContainer( $dstZone );
+ $good = true;
+
+ // Create the destination object.
+ $dstc = $conn->get_container($dstContainer);
+ $obj = $dstc->create_object($dstRel);
+ $obj->content_type = "text/plain";
+ $obj->write(".");
+
+ // FIXME: not sure if we need to re-open it, but let's
not take any chances.
+ $obj = $dstc->get_object($dstRel);
+ // FIXME: errors are returned as exceptions.
+ $obj->copy("$srcContainer/$srcRel");
+ if (0) { // handle exceptions
+ $status->error( 'filecopyerror', $srcPath,
$dstPath );
+ $good = false;
+ }
+
+ if ( $flags & self::DELETE_SOURCE ) {
+ $dstc = $conn->get_container($srcContainer);
+ // FIXME: handle exceptions.
+ $dstc->delete_object($srcRel);
+ if (0) { // handle exceptions.
+ $status->error( 'filerenameerror',
$srcPath, $dstPath );
+ $good = false;
+ }
+ }
+ if ( !( $flags & self::SKIP_VALIDATION ) ) {
+ // FIXME: Swift will return the MD5 of the data
written.
+ if (0) { // ( $hashDest === false ||
$hashSource !== $hashDest ) {
+ wfDebug( __METHOD__ . ': File copy
validation failed: ' .
+ "$srcPath ($hashSource) to
$dstPath ($hashDest)\n" );
+
+ $status->error( 'filecopyerror',
$srcPath, $dstPath );
+ $good = false;
+ }
+ }
+ if ( $good ) {
+ $status->successCount++;
+ } else {
+ $status->failCount++;
+ }
+ $status->success[$i] = $good;
+ }
+ return $status;
}
function storeTemp( $originalName, $srcPath ) {
@@ -2268,10 +2346,46 @@
wfDebug( __METHOD__ . " $sourceDestPairs\n" );
return parent::deleteBatch( $sourceDestPairs );
}
+
+ function newFromArchiveName( $title, $archiveName ) {
+ return OldSwiftFile::newFromArchiveName( $title, $this,
$archiveName );
+ }
+
+ /**
+ * Checks existence of specified array of files.
+ *
+ * @param $files Array: URLs of files to check
+ * @param $flags Integer: bitwise combination of the following flags:
+ * self::FILES_ONLY Mark file as existing only if it is a file
(not directory)
+ * @return Either array of files and existence flags, or false
+ */
function fileExistsBatch( $files, $flags = 0 ) {
- wfDebug( __METHOD__ . " $files\n" );
- return parent::fileExistsBatch( $files, $flags );
+ // we ONLY support when $flags & self::FILES_ONLY is set!
+ wfDebug( __METHOD__ . var_export($files, true) . " " . $flags.
"\n");
+ $result = array();
+ $auth = new CF_Authentication($this->swiftuser, $this->key,
NULL, $this->authurl);
+ $auth->authenticate();
+ $conn = new CF_Connection($auth);
+ $container = $conn->get_container($this->container);
+
+ foreach ( $files as $key => $file ) {
+ if ( self::isVirtualUrl( $file ) ) {
+ $rvu = $this->resolveVirtualUrl( $file );
+ list ($cont, $rel) = $rvu;
+ $container = $conn->get_container($cont);
+ }
+ try {
+ $obj = $container->get_object($rel);
+ $result[$key] = true;
+ } catch (NoSuchObjectException $e) {
+ $result[$key] = false;
+ }
+ }
+
+ return $result;
}
+
+
function getFileProps( $virtualUrl ) {
wfDebug( __METHOD__ . " $virtualUrl\n" );
return parent::getFileProps( $virtualUrl );
@@ -2280,27 +2394,26 @@
if ( empty($title) ) { return null; }
wfDebug( __METHOD__ . " $title, $time " .
var_export($this->fileFactory, true) ."\n" );
$f = parent::newFile( $title, $time );
- $f->key= $this->key;
- $f->swiftuser= $this->swiftuser;
- $f->authurl= $this->authurl;
- $f->container= $this->container;
return $f;
}
function findFile( $title, $options = array() ) {
- global $wgLocalFileRepo;
- wfDebug( __METHOD__ . " finding $title\n" );
- wfDebug( __METHOD__ . var_export($wgLocalFileRepo, true) .
"\n" );
- return parent::findFile( $title, $options );
+ wfDebug( __METHOD__ . " finding $title" . var_export($options,
true) . "\n" );
+ $found = parent::findFile( $title, $options );
+ wfDebug( __METHOD__ . " found " . var_export($found, true) .
"\n" );
+ return $found;
}
- function swiftcopy($container, $dstRel, $archiveRel ) {
+ function swiftcopy($container, $srcRel, $archiveRel ) {
// Note the assumption that we're not doing cross-container
copies.
// The destination must exist already.
$obj = $container->create_object($archiveRel);
+ $obj->content_type = "text/plain";
$obj->write(".");
- $obj->close();
- $obj = $container->get_object($dstRel);
- $success = $obj->copy($container->name . "/" . $archiveRel);
+ // FIXME: not sure if we need to re-open it, but let's not take
any chances.
+ $obj = $container->get_object($archiveRel);
+ // Errors are returned as exceptions.
+ wfDebug( __METHOD__ . " copying to $archiveRel from " .
$container->name . "/" . $srcRel . "\n");
+ $success = $obj->copy($container->name . "/" . $srcRel);
return $success;
}
@@ -2312,7 +2425,6 @@
*/
function publishBatch( $triplets, $flags = 0 ) {
$auth = new CF_Authentication($this->swiftuser, $this->key,
NULL, $this->authurl);
- wfDebug( __METHOD__ . $this->swiftuser . "," . $this->key .
"," . $this->authurl . "\n");
$auth->authenticate();
$conn = new CF_Connection($auth);
$container = $conn->get_container($this->container);
@@ -2346,8 +2458,6 @@
foreach ( $triplets as $i => $triplet ) {
list( $srcPath, $dstRel, $archiveRel ) = $triplet;
- $dstPath = "{$this->directory}/$dstRel";
- $archivePath = "{$this->directory}/$archiveRel";
// Archive destination file if it exists
try {
@@ -2356,11 +2466,7 @@
$pic = NULL;
}
if( $pic ) {
- // Check if the archive file exists
- // This is a sanity check to avoid data loss.
In UNIX, the rename primitive
- // unlinks the destination file if it exists.
DB-based synchronisation in
- // publishBatch's caller should prevent races.
In Windows there's no
- // problem because the rename primitive fails
if the destination exists.
+ // this shouldn't fail, but we'll catch it
anyway.
try {
$success = $this->swiftcopy($container,
$dstRel, $archiveRel );
} catch (NoSuchObjectException $e) {
@@ -2368,11 +2474,11 @@
}
if( !$success ) {
- $status->error(
'filerenameerror',$dstPath, $archivePath );
+ $status->error(
'filerenameerror',$dstRel, $archiveRel );
$status->failCount++;
continue;
} else {
- wfDebug(__METHOD__.": moved file
$dstPath to $archivePath\n");
+ wfDebug(__METHOD__.": moved file
$dstRel to $archiveRel\n");
}
$status->value[$i] = 'archived';
} else {
@@ -2380,11 +2486,11 @@
}
$good = true;
- // how does this return failure??
+ // FIXME: how does this return failure??
$obj = $container->create_object($dstRel);
- // we need to do a try here
+ // FIXME: we need to do a try here
$obj->load_from_filename( $srcPath, True);
- // $status->error( 'filecopyerror', $srcPath, $dstPath
);
+ // $status->error( 'filecopyerror', $srcPath, $dstRel );
// $good = false;
#if ( $flags & self::DELETE_SOURCE ) {
#delete ( $srcPath );
@@ -2392,9 +2498,7 @@
if ( $good ) {
$status->successCount++;
- wfDebug(__METHOD__.": wrote tempfile $srcPath
to $dstPath\n");
- // Thread-safe override for umask
- $this->chmod( $dstPath );
+ wfDebug(__METHOD__.": wrote tempfile $srcPath
to $dstRel\n");
} else {
$status->failCount++;
}
@@ -2402,14 +2506,95 @@
return $status;
}
+ /**
+ * Deletes a batch of files. Each file can be a (zone, rel) pairs, a
+ * virtual url or a real path. It will try to delete each file, but
+ * ignores any errors that may occur
+ *
+ * @param $pairs array List of files to delete
+ */
+ function cleanupBatch( $files ) {
+ $auth = new CF_Authentication($this->swiftuser, $this->key,
NULL, $this->authurl);
+ $auth->authenticate();
+ $conn = new CF_Connection($auth);
+ foreach ( $files as $file ) {
+ if ( is_array( $file ) ) {
+ // This is a pair, extract it
+ list( $zone, $rel ) = $file;
+ $cont = $this->getZonePath( $zone );
+ } else {
+ if ( self::isVirtualUrl( $file ) ) {
+ // This is a virtual url, resolve it
+ $path = $this->resolveVirtualUrl( $file
);
+ list( $zone, $rel) = $path;
+ $cont = $this->getZonePath( $zone );
+ } else {
+ // FIXME: This is a full file name
+ throw new MWException( __METHOD__.':
$file' );
+ }
+ }
+
+ wfDebug( __METHOD__.": $cont/$rel\n" );
+ $container = $conn->get_container($cont);
+ $container->delete_object($rel);
+ }
+ }
+ /**
+ * Makes no sense in our context -- don't let anybody call it.
+ */
+ function getZonePath( $zone ) {
+ throw new MWException( __METHOD__.': not implemented' );
+ }
+ /**
+ * Get the Swift container corresponding to one of the three basic zones
+ */
+ function getZoneContainer( $zone ) {
+ switch ( $zone ) {
+ case 'public':
+ return $this->container;
+ case 'temp':
+ return $this->container . "%2Ftemp";
+ case 'deleted':
+ return $this->container . "%2Fdeleted";
+ case 'thumb':
+ return $this->container . "%2Fthumb";
+ default:
+ return false;
+ }
+ }
+ /**
+ * Get the ($container, $object) corresponding to a virtual URL
+ */
+ function resolveVirtualUrl( $url ) {
+ if ( substr( $url, 0, 9 ) != 'mwrepo://' ) {
+ throw new MWException( __METHOD__.': unknown protoocl'
);
+ }
+
+ $bits = explode( '/', substr( $url, 9 ), 3 );
+ if ( count( $bits ) != 3 ) {
+ throw new MWException( __METHOD__.": invalid mwrepo
URL: $url" );
+ }
+ list( $repo, $zone, $rel ) = $bits;
+ if ( $repo !== $this->name ) {
+ throw new MWException( __METHOD__.": fetching from a
foreign repo is not supported" );
+ }
+ $container = $this->getZoneContainer( $zone );
+ if ( $container === false) {
+ throw new MWException( __METHOD__.": invalid zone:
$zone" );
+ }
+ return array($container, rawurldecode( $rel ));
+ }
+
+
+
+
}
class Junkyjunk {
var $directory, $deletedDir, $deletedHashLevels, $fileMode;
var $fileFactory = array( 'UnregisteredLocalFile', 'newFromTitle' );
- var $oldFileFactory = false;
var $pathDisclosureProtection = 'simple';
function __construct( $info ) {
@@ -2438,13 +2623,6 @@
}
/**
- * Get the public root directory of the repository.
- */
- function getRootDirectory() {
- return $this->directory;
- }
-
- /**
* Get the public root URL of the repository
*/
function getRootUrl() {
@@ -2459,24 +2637,6 @@
}
/**
- * Get the local directory corresponding to one of the three basic zones
- */
- function getZonePath( $zone ) {
- switch ( $zone ) {
- case 'public':
- return $this->directory;
- case 'temp':
- return "{$this->directory}/temp";
- case 'deleted':
- return $this->deletedDir;
- case 'thumb':
- return $this->thumbDir;
- default:
- return false;
- }
- }
-
- /**
* @see FileRepo::getZoneUrl()
*/
function getZoneUrl( $zone ) {
@@ -2507,185 +2667,7 @@
return $path;
}
- /**
- * Get the local path corresponding to a virtual URL
- */
- function resolveVirtualUrl( $url ) {
- if ( substr( $url, 0, 9 ) != 'mwrepo://' ) {
- throw new MWException( __METHOD__.': unknown protoocl'
);
- }
-
- $bits = explode( '/', substr( $url, 9 ), 3 );
- if ( count( $bits ) != 3 ) {
- throw new MWException( __METHOD__.": invalid mwrepo
URL: $url" );
- }
- list( $repo, $zone, $rel ) = $bits;
- if ( $repo !== $this->name ) {
- throw new MWException( __METHOD__.": fetching from a
foreign repo is not supported" );
- }
- $base = $this->getZonePath( $zone );
- if ( !$base ) {
- throw new MWException( __METHOD__.": invalid zone:
$zone" );
- }
- return $base . '/' . rawurldecode( $rel );
- }
-
- /**
- * Store a batch of files
- *
- * @param $triplets Array: (src,zone,dest) triplets as per store()
- * @param $flags Integer: bitwise combination of the following flags:
- * self::DELETE_SOURCE Delete the source file after upload
- * self::OVERWRITE Overwrite an existing destination file
instead of failing
- * self::OVERWRITE_SAME Overwrite the file if the destination
exists and has the
- * same contents as the source
- */
- function storeBatch( $triplets, $flags = 0 ) {
- wfDebug( __METHOD__ . ': Storing ' . count( $triplets ) .
- " triplets; flags: {$flags}\n" );
-
- // Try creating directories
- if ( !wfMkdirParents( $this->directory ) ) {
- return $this->newFatal( 'upload_directory_missing',
$this->directory );
- }
- if ( !is_writable( $this->directory ) ) {
- return $this->newFatal( 'upload_directory_read_only',
$this->directory );
- }
-
- // Validate each triplet
- $status = $this->newGood();
- foreach ( $triplets as $i => $triplet ) {
- list( $srcPath, $dstZone, $dstRel ) = $triplet;
-
- // Resolve destination path
- $root = $this->getZonePath( $dstZone );
- if ( !$root ) {
- throw new MWException( "Invalid zone: $dstZone"
);
- }
- if ( !$this->validateFilename( $dstRel ) ) {
- throw new MWException( 'Validation error in
$dstRel' );
- }
- $dstPath = "$root/$dstRel";
- $dstDir = dirname( $dstPath );
-
- // Create destination directories for this triplet
- if ( !is_dir( $dstDir ) ) {
- if ( !wfMkdirParents( $dstDir ) ) {
- return $this->newFatal(
'directorycreateerror', $dstDir );
- }
- if ( $dstZone == 'deleted' ) {
- $this->initDeletedDir( $dstDir );
- }
- }
-
- // Resolve source
- if ( self::isVirtualUrl( $srcPath ) ) {
- $srcPath = $triplets[$i][0] =
$this->resolveVirtualUrl( $srcPath );
- }
- if ( !is_file( $srcPath ) ) {
- // Make a list of files that don't exist for
return to the caller
- $status->fatal( 'filenotfound', $srcPath );
- continue;
- }
-
- // Check overwriting
- if ( !( $flags & self::OVERWRITE ) && file_exists(
$dstPath ) ) {
- if ( $flags & self::OVERWRITE_SAME ) {
- $hashSource = sha1_file( $srcPath );
- $hashDest = sha1_file( $dstPath );
- if ( $hashSource != $hashDest ) {
- $status->fatal(
'fileexistserror', $dstPath );
- }
- } else {
- $status->fatal( 'fileexistserror',
$dstPath );
- }
- }
- }
-
- // Windows does not support moving over existing files, so
explicitly delete them
- $deleteDest = wfIsWindows() && ( $flags & self::OVERWRITE );
-
- // Abort now on failure
- if ( !$status->ok ) {
- return $status;
- }
-
- // Execute the store operation for each triplet
- foreach ( $triplets as $i => $triplet ) {
- list( $srcPath, $dstZone, $dstRel ) = $triplet;
- $root = $this->getZonePath( $dstZone );
- $dstPath = "$root/$dstRel";
- $good = true;
-
- if ( $flags & self::DELETE_SOURCE ) {
- if ( $deleteDest ) {
- unlink( $dstPath );
- }
- if ( !rename( $srcPath, $dstPath ) ) {
- $status->error( 'filerenameerror',
$srcPath, $dstPath );
- $good = false;
- }
- } else {
- if ( !copy( $srcPath, $dstPath ) ) {
- $status->error( 'filecopyerror',
$srcPath, $dstPath );
- $good = false;
- }
- if ( !( $flags & self::SKIP_VALIDATION ) ) {
- wfSuppressWarnings();
- $hashSource = sha1_file( $srcPath );
- $hashDest = sha1_file( $dstPath );
- wfRestoreWarnings();
-
- if ( $hashDest === false || $hashSource
!== $hashDest ) {
- wfDebug( __METHOD__ . ': File
copy validation failed: ' .
- "$srcPath ($hashSource)
to $dstPath ($hashDest)\n" );
-
- $status->error(
'filecopyerror', $srcPath, $dstPath );
- $good = false;
- }
- }
- }
- if ( $good ) {
- $this->chmod( $dstPath );
- $status->successCount++;
- } else {
- $status->failCount++;
- }
- $status->success[$i] = $good;
- }
- return $status;
- }
- /**
- * Deletes a batch of files. Each file can be a (zone, rel) pairs, a
- * virtual url or a real path. It will try to delete each file, but
- * ignores any errors that may occur
- *
- * @param $pairs array List of files to delete
- */
- function cleanupBatch( $files ) {
- foreach ( $files as $file ) {
- if ( is_array( $file ) ) {
- // This is a pair, extract it
- list( $zone, $rel ) = $file;
- $root = $this->getZonePath( $zone );
- $path = "$root/$rel";
- } else {
- if ( self::isVirtualUrl( $file ) ) {
- // This is a virtual url, resolve it
- $path = $this->resolveVirtualUrl( $file
);
- } else {
- // This is a full file name
- $path = $file;
- }
- }
-
- wfSuppressWarnings();
- unlink( $path );
- wfRestoreWarnings();
- }
- }
-
function append( $srcPath, $toAppendPath, $flags = 0 ) {
$status = $this->newGood();
@@ -2724,44 +2706,11 @@
}
/**
- * Checks existence of specified array of files.
- *
- * @param $files Array: URLs of files to check
- * @param $flags Integer: bitwise combination of the following flags:
- * self::FILES_ONLY Mark file as existing only if it is a file
(not directory)
- * @return Either array of files and existence flags, or false
- */
- function fileExistsBatch( $files, $flags = 0 ) {
- if ( !file_exists( $this->directory ) || !is_readable(
$this->directory ) ) {
- return false;
- }
- $result = array();
- foreach ( $files as $key => $file ) {
- if ( self::isVirtualUrl( $file ) ) {
- $file = $this->resolveVirtualUrl( $file );
- }
- if( $flags & self::FILES_ONLY ) {
- $result[$key] = is_file( $file );
- } else {
- $result[$key] = file_exists( $file );
- }
- }
-
- return $result;
- }
-
- /**
* Take all available measures to prevent web accessibility of new
deleted
* directories, in case the user has not configured offline storage
*/
protected function initDeletedDir( $dir ) {
- // Add a .htaccess file to the root of the deleted zone
- $root = $this->getZonePath( 'deleted' );
- if ( !file_exists( "$root/.htaccess" ) ) {
- file_put_contents( "$root/.htaccess", "Deny from all\n"
);
- }
- // Seed new directories with a blank index.html, to prevent
crawling
- file_put_contents( "$dir/index.html", '' );
+ return; // we just don't make it public.
}
/**
@@ -2880,98 +2829,219 @@
}
return $status;
}
+}
- /**
- * Get a relative path for a deletion archive key,
- * e.g. s/z/a/ for sza251lrxrc1jad41h5mgilp8nysje52.jpg
- */
- function getDeletedHashPath( $key ) {
- $path = '';
- for ( $i = 0; $i < $this->deletedHashLevels; $i++ ) {
- $path .= $key[$i] . '/';
- }
- return $path;
+/**
+ * Old file in the in the oldimage table
+ *
+ * @file
+ * @ingroup FileRepo
+ */
+
+/**
+ * Class to represent a file in the oldimage table
+ *
+ * @ingroup FileRepo
+ */
+class OldSwiftFile extends SwiftFile {
+ var $requestedTime, $archive_name;
+
+ const CACHE_VERSION = 1;
+ const MAX_CACHE_ROWS = 20;
+
+ static function newFromTitle( $title, $repo, $time = null ) {
+ # The null default value is only here to avoid an E_STRICT
+ if( $time === null )
+ throw new MWException( __METHOD__.' got null for $time
parameter' );
+ return new self( $title, $repo, $time, null );
}
+ static function newFromArchiveName( $title, $repo, $archiveName ) {
+ return new self( $title, $repo, null, $archiveName );
+ }
+
+ static function newFromRow( $row, $repo ) {
+ $title = Title::makeTitle( NS_FILE, $row->oi_name );
+ $file = new self( $title, $repo, null, $row->oi_archive_name );
+ $file->loadFromRow( $row, 'oi_' );
+ return $file;
+ }
+
/**
- * Call a callback function for every file in the repository.
- * Uses the filesystem even in child classes.
+ * @static
+ * @param $sha1
+ * @param $repo LocalRepo
+ * @param bool $timestamp
+ * @return bool|OldLocalFile
*/
- function enumFilesInFS( $callback ) {
- $numDirs = 1 << ( $this->hashLevels * 4 );
- for ( $flatIndex = 0; $flatIndex < $numDirs; $flatIndex++ ) {
- $hexString = sprintf( "%0{$this->hashLevels}x",
$flatIndex );
- $path = $this->directory;
- for ( $hexPos = 0; $hexPos < $this->hashLevels;
$hexPos++ ) {
- $path .= '/' . substr( $hexString, 0, $hexPos +
1 );
- }
- if ( !file_exists( $path ) || !is_dir( $path ) ) {
- continue;
- }
- $dir = opendir( $path );
- while ( false !== ( $name = readdir( $dir ) ) ) {
- call_user_func( $callback, $path . '/' . $name
);
- }
+ static function newFromKey( $sha1, $repo, $timestamp = false ) {
+ $conds = array( 'oi_sha1' => $sha1 );
+ if( $timestamp ) {
+ $conds['oi_timestamp'] = $timestamp;
}
+ $dbr = $repo->getSlaveDB();
+ $row = $dbr->selectRow( 'oldimage', self::selectFields(),
$conds, __METHOD__ );
+ if( $row ) {
+ return self::newFromRow( $row, $repo );
+ } else {
+ return false;
+ }
}
-
+
/**
- * Call a callback function for every file in the repository
- * May use either the database or the filesystem
+ * Fields in the oldimage table
*/
- function enumFiles( $callback ) {
- $this->enumFilesInFS( $callback );
+ static function selectFields() {
+ return array(
+ 'oi_name',
+ 'oi_archive_name',
+ 'oi_size',
+ 'oi_width',
+ 'oi_height',
+ 'oi_metadata',
+ 'oi_bits',
+ 'oi_media_type',
+ 'oi_major_mime',
+ 'oi_minor_mime',
+ 'oi_description',
+ 'oi_user',
+ 'oi_user_text',
+ 'oi_timestamp',
+ 'oi_deleted',
+ 'oi_sha1',
+ );
}
/**
- * Get properties of a file with a given virtual URL
- * The virtual URL must refer to this repo
+ * @param $title Title
+ * @param $repo FileRepo
+ * @param $time String: timestamp or null to load by archive name
+ * @param $archiveName String: archive name or null to load by timestamp
*/
- function getFileProps( $virtualUrl ) {
- $path = $this->resolveVirtualUrl( $virtualUrl );
- return File::getPropsFromPath( $path );
+ function __construct( $title, $repo, $time, $archiveName ) {
+ parent::__construct( $title, $repo );
+ $this->requestedTime = $time;
+ $this->archive_name = $archiveName;
+ if ( is_null( $time ) && is_null( $archiveName ) ) {
+ throw new MWException( __METHOD__.': must specify at
least one of $time or $archiveName' );
+ }
}
- /**
- * Path disclosure protection functions
- *
- * Get a callback function to use for cleaning error message parameters
- */
- function getErrorCleanupFunction() {
- switch ( $this->pathDisclosureProtection ) {
- case 'simple':
- $callback = array( $this, 'simpleClean' );
- break;
- default:
- $callback = parent::getErrorCleanupFunction();
+ function getCacheKey() {
+ return false;
+ }
+
+ function getArchiveName() {
+ if ( !isset( $this->archive_name ) ) {
+ $this->load();
}
- return $callback;
+ return $this->archive_name;
}
- function simpleClean( $param ) {
- if ( !isset( $this->simpleCleanPairs ) ) {
- global $IP;
- $this->simpleCleanPairs = array(
- $this->directory => 'public',
- "{$this->directory}/temp" => 'temp',
- $IP => '$IP',
- dirname( __FILE__ ) =>
'$IP/extensions/WebStore',
- );
- if ( $this->deletedDir ) {
- $this->simpleCleanPairs[$this->deletedDir] =
'deleted';
- }
+ function isOld() {
+ return true;
+ }
+
+ function isVisible() {
+ return $this->exists() && !$this->isDeleted(File::DELETED_FILE);
+ }
+
+ function loadFromDB() {
+ wfProfileIn( __METHOD__ );
+ $this->dataLoaded = true;
+ $dbr = $this->repo->getSlaveDB();
+ $conds = array( 'oi_name' => $this->getName() );
+ if ( is_null( $this->requestedTime ) ) {
+ $conds['oi_archive_name'] = $this->archive_name;
+ } else {
+ $conds[] = 'oi_timestamp = ' . $dbr->addQuotes(
$dbr->timestamp( $this->requestedTime ) );
}
- return strtr( $param, $this->simpleCleanPairs );
+ $row = $dbr->selectRow( 'oldimage', $this->getCacheFields(
'oi_' ),
+ $conds, __METHOD__, array( 'ORDER BY' => 'oi_timestamp
DESC' ) );
+ if ( $row ) {
+ $this->loadFromRow( $row, 'oi_' );
+ } else {
+ $this->fileExists = false;
+ }
+ wfProfileOut( __METHOD__ );
}
+ function getCacheFields( $prefix = 'img_' ) {
+ $fields = parent::getCacheFields( $prefix );
+ $fields[] = $prefix . 'archive_name';
+ $fields[] = $prefix . 'deleted';
+ return $fields;
+ }
+
+ function getRel() {
+ return 'archive/' . $this->getHashPath() .
$this->getArchiveName();
+ }
+
+ function getUrlRel() {
+ return 'archive/' . $this->getHashPath() . rawurlencode(
$this->getArchiveName() );
+ }
+
+ function upgradeRow() {
+ wfProfileIn( __METHOD__ );
+ $this->loadFromFile();
+
+ # Don't destroy file info of missing files
+ if ( !$this->fileExists ) {
+ wfDebug( __METHOD__.": file does not exist, aborting\n"
);
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+
+ $dbw = $this->repo->getMasterDB();
+ list( $major, $minor ) = self::splitMime( $this->mime );
+
+ wfDebug(__METHOD__.': upgrading '.$this->archive_name." to the
current schema\n");
+ $dbw->update( 'oldimage',
+ array(
+ 'oi_width' => $this->width,
+ 'oi_height' => $this->height,
+ 'oi_bits' => $this->bits,
+ 'oi_media_type' => $this->media_type,
+ 'oi_major_mime' => $major,
+ 'oi_minor_mime' => $minor,
+ 'oi_metadata' => $this->metadata,
+ 'oi_sha1' => $this->sha1,
+ ), array(
+ 'oi_name' => $this->getName(),
+ 'oi_archive_name' => $this->archive_name ),
+ __METHOD__
+ );
+ wfProfileOut( __METHOD__ );
+ }
+
/**
- * Chmod a file, supressing the warnings.
- * @param $path String: the path to change
+ * @param $field Integer: one of DELETED_* bitfield constants
+ * for file or revision rows
+ * @return bool
*/
- protected function chmod( $path ) {
- wfSuppressWarnings();
- chmod( $path, $this->fileMode );
- wfRestoreWarnings();
+ function isDeleted( $field ) {
+ $this->load();
+ return ($this->deleted & $field) == $field;
}
+ /**
+ * Returns bitfield value
+ * @return int
+ */
+ function getVisibility() {
+ $this->load();
+ return (int)$this->deleted;
+ }
+
+ /**
+ * Determine if the current user is allowed to view a particular
+ * field of this image file, if it's marked as deleted.
+ *
+ * @param $field Integer
+ * @return bool
+ */
+ function userCan( $field ) {
+ $this->load();
+ return Revision::userCanBitfield( $this->deleted, $field );
+ }
}
Modified: trunk/extensions/SwiftMedia/SwiftMedia.i18n.php
===================================================================
--- trunk/extensions/SwiftMedia/SwiftMedia.i18n.php 2011-05-18 20:17:26 UTC
(rev 88389)
+++ trunk/extensions/SwiftMedia/SwiftMedia.i18n.php 2011-05-18 20:26:10 UTC
(rev 88390)
@@ -1,6 +1,7 @@
<?php
$messages = array();
$messages['en'] = array(
- 'swiftmedia' => 'Openstack\'s Swift is a very large scale reliable
object store. It will serve multiple petabytes of media files.',
+ 'swiftmedia' => 'Openstack\'s Swift is a very large scale reliable
object store. It will serve multiple petabytes of media files. \
+ [[Extension:SwiftMedia]] allows MediaWiki uploads to be stored in
Swift.',
);
Added: trunk/extensions/SwiftMedia/copyover
===================================================================
--- trunk/extensions/SwiftMedia/copyover (rev 0)
+++ trunk/extensions/SwiftMedia/copyover 2011-05-18 20:26:10 UTC (rev
88390)
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+scp [email protected]:/var/www/LocalSettings.php .
+scp [email protected]:/etc/swift/proxy-server.conf .
+scp
[email protected]:/var/www/extensions/SwiftMedia/{SwiftMedia.body.php,SwiftMedia.i18n.php,SwiftMedia.php}
.
+scp
[email protected]:/usr/local/lib/python2.6/dist-packages/wmf/{client.py,__init__.py,rewrite.py}
wmf/
Property changes on: trunk/extensions/SwiftMedia/copyover
___________________________________________________________________
Added: svn:executable
+ *
Modified: trunk/extensions/SwiftMedia/wmf/rewrite.py
===================================================================
--- trunk/extensions/SwiftMedia/wmf/rewrite.py 2011-05-18 20:17:26 UTC (rev
88389)
+++ trunk/extensions/SwiftMedia/wmf/rewrite.py 2011-05-18 20:26:10 UTC (rev
88390)
@@ -110,7 +110,7 @@
req.host = '127.0.0.1'
url = req.url[:]
# Create a path to our object's name.
- req.path_info = "/v1/%s/%s/%s" % (self.account, container, obj)
+ req.path_info = "/v1/%s/%s/%s" % (self.account, container,
urllib2.unquote(obj))
controller = ObjectController()
# do_start_response just remembers what it got called with,
because we may want to generate a different response.
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs