summaryrefslogtreecommitdiff
path: root/includes/filerepo
diff options
context:
space:
mode:
Diffstat (limited to 'includes/filerepo')
-rw-r--r--includes/filerepo/ArchivedFile.php306
-rw-r--r--includes/filerepo/FSRepo.php2
-rw-r--r--includes/filerepo/File.php75
-rw-r--r--includes/filerepo/FileRepo.php25
-rw-r--r--includes/filerepo/FileRepoStatus.php12
-rw-r--r--includes/filerepo/ICRepo.php204
-rw-r--r--includes/filerepo/LocalFile.php128
-rw-r--r--includes/filerepo/LocalRepo.php48
-rw-r--r--includes/filerepo/NullRepo.php34
-rw-r--r--includes/filerepo/RepoGroup.php47
10 files changed, 679 insertions, 202 deletions
diff --git a/includes/filerepo/ArchivedFile.php b/includes/filerepo/ArchivedFile.php
index bd9ff633..cc70b26d 100644
--- a/includes/filerepo/ArchivedFile.php
+++ b/includes/filerepo/ArchivedFile.php
@@ -5,24 +5,70 @@
*/
class ArchivedFile
{
- /**
- * Returns a file object from the filearchive table
- * @param $title, the corresponding image page title
- * @param $id, the image id, a unique key
- * @param $key, optional storage key
- * @return ResultWrapper
+ /**#@+
+ * @private
*/
+ var $id, # filearchive row ID
+ $title, # image title
+ $name, # image name
+ $group, # FileStore storage group
+ $key, # FileStore sha1 key
+ $size, # file dimensions
+ $bits, # size in bytes
+ $width, # width
+ $height, # height
+ $metadata, # metadata string
+ $mime, # mime type
+ $media_type, # media type
+ $description, # upload description
+ $user, # user ID of uploader
+ $user_text, # user name of uploader
+ $timestamp, # time of upload
+ $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx)
+ $deleted; # Bitfield akin to rev_deleted
+
+ /**#@-*/
+
function ArchivedFile( $title, $id=0, $key='' ) {
- if( !is_object( $title ) ) {
+ if( !is_object($title) ) {
throw new MWException( 'ArchivedFile constructor given bogus title.' );
}
- $conds = ($id) ? "fa_id = $id" : "fa_storage_key = '$key'";
- if( $title->getNamespace() == NS_IMAGE ) {
+ $this->id = -1;
+ $this->title = $title;
+ $this->name = $title->getDBkey();
+ $this->group = '';
+ $this->key = '';
+ $this->size = 0;
+ $this->bits = 0;
+ $this->width = 0;
+ $this->height = 0;
+ $this->metadata = '';
+ $this->mime = "unknown/unknown";
+ $this->media_type = '';
+ $this->description = '';
+ $this->user = 0;
+ $this->user_text = '';
+ $this->timestamp = NULL;
+ $this->deleted = 0;
+ $this->dataLoaded = false;
+ }
+
+ /**
+ * Loads a file object from the filearchive table
+ * @return ResultWrapper
+ */
+ public function load() {
+ if ( $this->dataLoaded ) {
+ return true;
+ }
+ $conds = ($this->id) ? "fa_id = {$this->id}" : "fa_storage_key = '{$this->key}'";
+ if( $this->title->getNamespace() == NS_IMAGE ) {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'filearchive',
array(
'fa_id',
'fa_name',
+ 'fa_archive_name',
'fa_storage_key',
'fa_storage_group',
'fa_size',
@@ -39,7 +85,7 @@ class ArchivedFile
'fa_timestamp',
'fa_deleted' ),
array(
- 'fa_name' => $title->getDbKey(),
+ 'fa_name' => $this->title->getDBkey(),
$conds ),
__METHOD__,
array( 'ORDER BY' => 'fa_timestamp DESC' ) );
@@ -52,36 +98,229 @@ class ArchivedFile
$row = $ret->fetchObject();
// initialize fields for filestore image object
- $this->mId = intval($row->fa_id);
- $this->mName = $row->fa_name;
- $this->mGroup = $row->fa_storage_group;
- $this->mKey = $row->fa_storage_key;
- $this->mSize = $row->fa_size;
- $this->mBits = $row->fa_bits;
- $this->mWidth = $row->fa_width;
- $this->mHeight = $row->fa_height;
- $this->mMetaData = $row->fa_metadata;
- $this->mMime = "$row->fa_major_mime/$row->fa_minor_mime";
- $this->mType = $row->fa_media_type;
- $this->mDescription = $row->fa_description;
- $this->mUser = $row->fa_user;
- $this->mUserText = $row->fa_user_text;
- $this->mTimestamp = $row->fa_timestamp;
- $this->mDeleted = $row->fa_deleted;
+ $this->id = intval($row->fa_id);
+ $this->name = $row->fa_name;
+ $this->archive_name = $row->fa_archive_name;
+ $this->group = $row->fa_storage_group;
+ $this->key = $row->fa_storage_key;
+ $this->size = $row->fa_size;
+ $this->bits = $row->fa_bits;
+ $this->width = $row->fa_width;
+ $this->height = $row->fa_height;
+ $this->metadata = $row->fa_metadata;
+ $this->mime = "$row->fa_major_mime/$row->fa_minor_mime";
+ $this->media_type = $row->fa_media_type;
+ $this->description = $row->fa_description;
+ $this->user = $row->fa_user;
+ $this->user_text = $row->fa_user_text;
+ $this->timestamp = $row->fa_timestamp;
+ $this->deleted = $row->fa_deleted;
} else {
throw new MWException( 'This title does not correspond to an image page.' );
return;
}
+ $this->dataLoaded = true;
+
return true;
}
/**
+ * Loads a file object from the filearchive table
+ * @return ResultWrapper
+ */
+ public static function newFromRow( $row ) {
+ $file = new ArchivedFile( Title::makeTitle( NS_IMAGE, $row->fa_name ) );
+
+ $file->id = intval($row->fa_id);
+ $file->name = $row->fa_name;
+ $file->archive_name = $row->fa_archive_name;
+ $file->group = $row->fa_storage_group;
+ $file->key = $row->fa_storage_key;
+ $file->size = $row->fa_size;
+ $file->bits = $row->fa_bits;
+ $file->width = $row->fa_width;
+ $file->height = $row->fa_height;
+ $file->metadata = $row->fa_metadata;
+ $file->mime = "$row->fa_major_mime/$row->fa_minor_mime";
+ $file->media_type = $row->fa_media_type;
+ $file->description = $row->fa_description;
+ $file->user = $row->fa_user;
+ $file->user_text = $row->fa_user_text;
+ $file->timestamp = $row->fa_timestamp;
+ $file->deleted = $row->fa_deleted;
+
+ return $file;
+ }
+
+ /**
+ * Return the associated title object
+ * @public
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * Return the file name
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ public function getID() {
+ $this->load();
+ return $this->id;
+ }
+
+ /**
+ * Return the FileStore key
+ */
+ public function getKey() {
+ $this->load();
+ return $this->key;
+ }
+
+ /**
+ * Return the FileStore storage group
+ */
+ public function getGroup() {
+ return $file->group;
+ }
+
+ /**
+ * Return the width of the image
+ */
+ public function getWidth() {
+ $this->load();
+ return $this->width;
+ }
+
+ /**
+ * Return the height of the image
+ */
+ public function getHeight() {
+ $this->load();
+ return $this->height;
+ }
+
+ /**
+ * Get handler-specific metadata
+ */
+ public function getMetadata() {
+ $this->load();
+ return $this->metadata;
+ }
+
+ /**
+ * Return the size of the image file, in bytes
+ * @public
+ */
+ public function getSize() {
+ $this->load();
+ return $this->size;
+ }
+
+ /**
+ * Return the bits of the image file, in bytes
+ * @public
+ */
+ public function getBits() {
+ $this->load();
+ return $this->bits;
+ }
+
+ /**
+ * Returns the mime type of the file.
+ */
+ public function getMimeType() {
+ $this->load();
+ return $this->mime;
+ }
+
+ /**
+ * Return the type of the media in the file.
+ * Use the value returned by this function with the MEDIATYPE_xxx constants.
+ */
+ public function getMediaType() {
+ $this->load();
+ return $this->media_type;
+ }
+
+ /**
+ * Return upload timestamp.
+ */
+ public function getTimestamp() {
+ $this->load();
+ return $this->timestamp;
+ }
+
+ /**
+ * Return the user ID of the uploader.
+ */
+ public function getUser() {
+ $this->load();
+ if( $this->isDeleted( File::DELETED_USER ) ) {
+ return 0;
+ } else {
+ return $this->user;
+ }
+ }
+
+ /**
+ * Return the user name of the uploader.
+ */
+ public function getUserText() {
+ $this->load();
+ if( $this->isDeleted( File::DELETED_USER ) ) {
+ return 0;
+ } else {
+ return $this->user_text;
+ }
+ }
+
+ /**
+ * Return upload description.
+ */
+ public function getDescription() {
+ $this->load();
+ if( $this->isDeleted( File::DELETED_COMMENT ) ) {
+ return 0;
+ } else {
+ return $this->description;
+ }
+ }
+
+ /**
+ * Return the user ID of the uploader.
+ */
+ public function getRawUser() {
+ $this->load();
+ return $this->user;
+ }
+
+ /**
+ * Return the user name of the uploader.
+ */
+ public function getRawUserText() {
+ $this->load();
+ return $this->user_text;
+ }
+
+ /**
+ * Return upload description.
+ */
+ public function getRawDescription() {
+ $this->load();
+ return $this->description;
+ }
+
+ /**
* int $field one of DELETED_* bitfield constants
* for file or revision rows
* @return bool
*/
- function isDeleted( $field ) {
- return ($this->mDeleted & $field) == $field;
+ public function isDeleted( $field ) {
+ return ($this->deleted & $field) == $field;
}
/**
@@ -90,19 +329,16 @@ class ArchivedFile
* @param int $field
* @return bool
*/
- function userCan( $field ) {
- if( isset($this->mDeleted) && ($this->mDeleted & $field) == $field ) {
- // images
+ public function userCan( $field ) {
+ if( ($this->deleted & $field) == $field ) {
global $wgUser;
- $permission = ( $this->mDeleted & File::DELETED_RESTRICTED ) == File::DELETED_RESTRICTED
+ $permission = ( $this->deleted & File::DELETED_RESTRICTED ) == File::DELETED_RESTRICTED
? 'hiderevision'
: 'deleterevision';
- wfDebug( "Checking for $permission due to $field match on $this->mDeleted\n" );
+ wfDebug( "Checking for $permission due to $field match on $this->deleted\n" );
return $wgUser->isAllowed( $permission );
} else {
return true;
}
}
}
-
-
diff --git a/includes/filerepo/FSRepo.php b/includes/filerepo/FSRepo.php
index 84ec9a27..86887d09 100644
--- a/includes/filerepo/FSRepo.php
+++ b/includes/filerepo/FSRepo.php
@@ -422,7 +422,7 @@ class FSRepo extends FileRepo {
$status->error( 'filerenameerror', $srcPath, $archivePath );
$good = false;
} else {
- chmod( $archivePath, 0644 );
+ @chmod( $archivePath, 0644 );
}
}
if ( $good ) {
diff --git a/includes/filerepo/File.php b/includes/filerepo/File.php
index 21b7a865..5172ad0f 100644
--- a/includes/filerepo/File.php
+++ b/includes/filerepo/File.php
@@ -46,7 +46,7 @@ abstract class File {
/**
* The following member variables are not lazy-initialised
*/
- var $repo, $title, $lastError;
+ var $repo, $title, $lastError, $redirected;
/**
* Call this constructor from child classes
@@ -135,20 +135,28 @@ abstract class File {
/**
* Return the associated title object
- * @public
*/
- function getTitle() { return $this->title; }
+ public function getTitle() { return $this->title; }
/**
* Return the URL of the file
- * @public
*/
- function getUrl() {
+ public function getUrl() {
if ( !isset( $this->url ) ) {
$this->url = $this->repo->getZoneUrl( 'public' ) . '/' . $this->getUrlRel();
}
return $this->url;
}
+
+ /**
+ * Return a fully-qualified URL to the file.
+ * Upload URL paths _may or may not_ be fully qualified, so
+ * we check. Local paths are assumed to belong on $wgServer.
+ * @return string
+ */
+ public function getFullUrl() {
+ return wfExpandUrl( $this->getUrl() );
+ }
function getViewURL() {
if( $this->mustRender()) {
@@ -173,10 +181,8 @@ abstract class File {
* or in hashed paths like /images/3/3c.
*
* May return false if the file is not locally accessible.
- *
- * @public
*/
- function getPath() {
+ public function getPath() {
if ( !isset( $this->path ) ) {
$this->path = $this->repo->getZonePath('public') . '/' . $this->getRel();
}
@@ -185,9 +191,8 @@ abstract class File {
/**
* Alias for getPath()
- * @public
*/
- function getFullPath() {
+ public function getFullPath() {
return $this->getPath();
}
@@ -210,6 +215,14 @@ abstract class File {
public function getHeight( $page = 1 ) { return false; }
/**
+ * Returns ID or name of user who uploaded the file
+ * STUB
+ *
+ * @param $type string 'text' or 'id'
+ */
+ public function getUser( $type='text' ) { return null; }
+
+ /**
* Get the duration of a media file in seconds
*/
public function getLength() {
@@ -487,9 +500,11 @@ abstract class File {
$script = $this->getTransformScript();
if ( $script && !($flags & self::RENDER_NOW) ) {
- // Use a script to transform on client request
+ // Use a script to transform on client request, if possible
$thumb = $this->handler->getScriptedTransform( $this, $script, $params );
- break;
+ if( $thumb ) {
+ break;
+ }
}
$normalisedParams = $params;
@@ -497,7 +512,7 @@ abstract class File {
$thumbName = $this->thumbName( $normalisedParams );
$thumbPath = $this->getThumbPath( $thumbName );
$thumbUrl = $this->getThumbUrl( $thumbName );
-
+
if ( $this->repo->canTransformVia404() && !($flags & self::RENDER_NOW ) ) {
$thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
break;
@@ -585,7 +600,7 @@ abstract class File {
* STUB
* Overridden by LocalFile
*/
- function purgeCache( $archiveFiles = array() ) {}
+ function purgeCache() {}
/**
* Purge the file description page, but don't go after
@@ -618,6 +633,18 @@ abstract class File {
}
/**
+ * Return a fragment of the history of file.
+ *
+ * STUB
+ * @param $limit integer Limit of rows to return
+ * @param $start timestamp Only revisions older than $start will be returned
+ * @param $end timestamp Only revisions newer than $end will be returned
+ */
+ function getHistory($limit = null, $start = null, $end = null) {
+ return false;
+ }
+
+ /**
* Return the history of this file, line by line. Starts with current version,
* then old versions. Should return an object similar to an image/oldimage
* database row.
@@ -887,7 +914,7 @@ abstract class File {
* STUB
* Overridden by LocalFile
*/
- function delete( $reason, $suppress=false ) {
+ function delete( $reason ) {
$this->readOnlyError();
}
@@ -984,6 +1011,14 @@ abstract class File {
}
/**
+ * Get discription of file revision
+ * STUB
+ */
+ function getDescription() {
+ return null;
+ }
+
+ /**
* Get the 14-character timestamp of the file upload, or false if
* it doesn't exist
*/
@@ -1014,7 +1049,7 @@ abstract class File {
}
/**
- * Get an associative array containing information about a file in the local filesystem\
+ * Get an associative array containing information about a file in the local filesystem.
*
* @param string $path Absolute local filesystem path
* @param mixed $ext The file extension, or true to extract it from the filename.
@@ -1121,6 +1156,14 @@ abstract class File {
return '';
}
}
+
+ function getRedirected() {
+ return $this->redirected;
+ }
+
+ function redirectedFrom( $from ) {
+ $this->redirected = $from;
+ }
}
/**
* Aliases for backwards compatibility with 1.6
diff --git a/includes/filerepo/FileRepo.php b/includes/filerepo/FileRepo.php
index cf6d65c2..ee7691a6 100644
--- a/includes/filerepo/FileRepo.php
+++ b/includes/filerepo/FileRepo.php
@@ -82,7 +82,7 @@ abstract class FileRepo {
if ( !$img ) {
return false;
}
- if ( $img->exists() && ( !$time || $img->getTimestamp() <= $time ) ) {
+ if ( $img->exists() && ( !$time || $img->getTimestamp() == $time ) ) {
return $img;
}
# Now try an old version of the file
@@ -90,6 +90,19 @@ abstract class FileRepo {
if ( $img->exists() ) {
return $img;
}
+
+ # Now try redirects
+ $redir = $this->checkRedirect( $title );
+ if( $redir && $redir->getNamespace() == NS_IMAGE) {
+ $img = $this->newFile( $redir );
+ if( !$img ) {
+ return false;
+ }
+ if( $img->exists() ) {
+ $img->redirectedFrom( $title->getText() );
+ return $img;
+ }
+ }
}
/**
@@ -400,5 +413,15 @@ abstract class FileRepo {
* STUB
*/
function cleanupDeletedBatch( $storageKeys ) {}
+
+ /**
+ * Checks if there is a redirect named as $title
+ * STUB
+ *
+ * @param Title $title Title of image
+ */
+ function checkRedirect( $title ) {
+ return false;
+ }
}
diff --git a/includes/filerepo/FileRepoStatus.php b/includes/filerepo/FileRepoStatus.php
index 972b2e46..5dd1dbda 100644
--- a/includes/filerepo/FileRepoStatus.php
+++ b/includes/filerepo/FileRepoStatus.php
@@ -135,22 +135,22 @@ class FileRepoStatus {
}
if ( count( $this->errors ) == 1 ) {
$params = array_map( 'wfEscapeWikiText', $this->cleanParams( $this->errors[0]['params'] ) );
- $s = wfMsgReal( $this->errors[0]['message'], $params );
+ $s = wfMsgReal( $this->errors[0]['message'], $params, true, false, false );
if ( $shortContext ) {
- $s = wfMsg( $shortContext, $s );
+ $s = wfMsgNoTrans( $shortContext, $s );
} elseif ( $longContext ) {
- $s = wfMsg( $longContext, "* $s\n" );
+ $s = wfMsgNoTrans( $longContext, "* $s\n" );
}
} else {
$s = '';
foreach ( $this->errors as $error ) {
$params = array_map( 'wfEscapeWikiText', $this->cleanParams( $error['params'] ) );
- $s .= '* ' . wfMsgReal( $error['message'], $params ) . "\n";
+ $s .= '* ' . wfMsgReal( $error['message'], $params, true, false, false ) . "\n";
}
if ( $longContext ) {
- $s = wfMsg( $longContext, $s );
+ $s = wfMsgNoTrans( $longContext, $s );
} elseif ( $shortContext ) {
- $s = wfMsg( $shortContext, "\n* $s\n" );
+ $s = wfMsgNoTrans( $shortContext, "\n* $s\n" );
}
}
return $s;
diff --git a/includes/filerepo/ICRepo.php b/includes/filerepo/ICRepo.php
index 124fe2b6..ab686f9b 100644
--- a/includes/filerepo/ICRepo.php
+++ b/includes/filerepo/ICRepo.php
@@ -1,24 +1,24 @@
<?php
/**
- * A repository for files accessible via InstantCommons.
+ * A repository for files accessible via InstantCommons.
*/
class ICRepo extends LocalRepo {
- var $directory, $url, $hashLevels, $cache;
+ var $directory, $url, $hashLevels, $cache;
var $fileFactory = array( 'ICFile', 'newFromTitle' );
var $oldFileFactory = false;
function __construct( $info ) {
- parent::__construct( $info );
+ parent::__construct( $info );
// Required settings
$this->directory = $info['directory'];
$this->url = $info['url'];
$this->hashLevels = $info['hashLevels'];
if(isset($info['cache'])){
$this->cache = getcwd().'/images/'.$info['cache'];
- }
- }
+ }
+ }
}
/**
@@ -26,30 +26,30 @@ class ICRepo extends LocalRepo {
*/
class ICFile extends LocalFile{
static function newFromTitle($title,$repo){
- return new self($title, $repo);
+ return new self($title, $repo);
}
-
+
/**
* Returns true if the file comes from the local file repository.
*
* @return bool
*/
- function isLocal() {
- return true;
+ function isLocal() {
+ return true;
}
-
+
function load(){
if (!$this->dataLoaded ) {
if ( !$this->loadFromCache() ) {
if(!$this->loadFromDB()){
$this->loadFromIC();
- }
- $this->saveToCache();
+ }
+ $this->saveToCache();
}
$this->dataLoaded = true;
- }
+ }
}
-
+
/**
* Load file metadata from the DB
*/
@@ -62,15 +62,15 @@ class ICFile extends LocalFile{
$dbr = $this->repo->getSlaveDB();
$row = $dbr->selectRow( 'ic_image', $this->getCacheFields( 'img_' ),
- array( 'img_name' => $this->getName() ), __METHOD__ );
+ array( 'img_name' => $this->getName() ), __METHOD__ );
if ( $row ) {
if (trim($row->img_media_type)==NULL) {
$this->upgradeRow();
$this->upgraded = true;
- }
+ }
$this->loadFromRow( $row );
//This means that these files are local so the repository locations are local
- $this->setUrlPathLocal();
+ $this->setUrlPathLocal();
$this->fileExists = true;
//var_dump($this); exit;
} else {
@@ -78,10 +78,10 @@ class ICFile extends LocalFile{
}
wfProfileOut( __METHOD__ );
-
+
return $this->fileExists;
}
-
+
/**
* Fix assorted version-related problems with the image row by reloading it from the file
*/
@@ -110,106 +110,102 @@ class ICFile extends LocalFile{
$this->saveToCache();
wfProfileOut( __METHOD__ );
}
-
+
function exists(){
$this->load();
return $this->fileExists;
-
}
-
+
/**
* Fetch the file from the repository. Check local ic_images table first. If not available, check remote server
- */
- function loadFromIC(){
- # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
+ */
+ function loadFromIC(){
+ # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
$this->dataLoaded = true;
- $icUrl = $this->repo->directory.'&media='.$this->title->mDbkeyform;
- if($h = @fopen($icUrl, 'rb')){
- $contents = fread($h, 3000);
- $image = $this->api_xml_to_array($contents);
- if($image['fileExists']){
- foreach($image as $property=>$value){
- if($property=="url"){$value=$this->repo->url.$value; }
- $this->$property = $value;
- }
- if($this->curl_file_get_contents($this->repo->url.$image['url'], $this->repo->cache.'/'.$image['name'])){
- //Record the image
- $this->recordDownload("Downloaded with InstantCommons");
-
- //Then cache it
- }else{//set fileExists back to false
- $this->fileExists = false;
- }
- }
+ $icUrl = $this->repo->directory.'&media='.$this->title->mDbkeyform;
+ if($h = @fopen($icUrl, 'rb')){
+ $contents = fread($h, 3000);
+ $image = $this->api_xml_to_array($contents);
+ if($image['fileExists']){
+ foreach($image as $property=>$value){
+ if($property=="url"){$value=$this->repo->url.$value; }
+ $this->$property = $value;
+ }
+ if($this->curl_file_get_contents($this->repo->url.$image['url'], $this->repo->cache.'/'.$image['name'])){
+ //Record the image
+ $this->recordDownload("Downloaded with InstantCommons");
+
+ //Then cache it
+ }else{//set fileExists back to false
+ $this->fileExists = false;
+ }
+ }
}
- }
-
-
- function setUrlPathLocal(){
- global $wgScriptPath;
- $path = $wgScriptPath.'/'.substr($this->repo->cache, strlen($wgScriptPath));
- $this->repo->url = $path;//.'/'.rawurlencode($this->title->mDbkeyform);
+ }
+
+ function setUrlPathLocal(){
+ global $wgScriptPath;
+ $path = $wgScriptPath.'/'.substr($this->repo->cache, strlen($wgScriptPath));
+ $this->repo->url = $path;//.'/'.rawurlencode($this->title->mDbkeyform);
$this->repo->directory = $this->repo->cache;//.'/'.rawurlencode($this->title->mDbkeyform);
-
- }
-
- function getThumbPath( $suffix=false ){
- $path = $this->repo->cache;
- if ( $suffix !== false ) {
+
+ }
+
+ function getThumbPath( $suffix=false ){
+ $path = $this->repo->cache;
+ if ( $suffix !== false ) {
$path .= '/thumb/' . rawurlencode( $suffix );
}
return $path;
- }
- function getThumbUrl( $suffix=false ){
- global $wgScriptPath;
+ }
+ function getThumbUrl( $suffix=false ){
+ global $wgScriptPath;
$path = $wgScriptPath.'/'.substr($this->repo->cache, strlen($wgScriptPath));
- if ( $suffix !== false ) {
+ if ( $suffix !== false ) {
$path .= '/thumb/' . rawurlencode( $suffix );
}
return $path;
- }
-
- /**
- * Convert the InstantCommons Server API XML Response to an associative array
- */
- function api_xml_to_array($xml){
- preg_match("/<instantcommons><image(.*?)<\/instantcommons>/",$xml,$match);
- preg_match_all("/(.*?=\".*?\")/",$match[1], $matches);
- foreach($matches[1] as $match){
- list($key,$value) = split("=",$match);
- $image[trim($key,'<" ')]=trim($value,' "');
- }
- return $image;
- }
-
+ }
+
+ /**
+ * Convert the InstantCommons Server API XML Response to an associative array
+ */
+ function api_xml_to_array($xml){
+ preg_match("/<instantcommons><image(.*?)<\/instantcommons>/",$xml,$match);
+ preg_match_all("/(.*?=\".*?\")/",$match[1], $matches);
+ foreach($matches[1] as $match){
+ list($key,$value) = split("=",$match);
+ $image[trim($key,'<" ')]=trim($value,' "');
+ }
+ return $image;
+ }
+
/**
- * Use cURL to read the content of a URL into a string
- * ref: http://groups-beta.google.com/group/comp.lang.php/browse_thread/thread/8efbbaced3c45e3c/d63c7891cf8e380b?lnk=raot
- * @param string $url - the URL to fetch
- * @param resource $fp - filename to write file contents to
- * @param boolean $bg - call cURL in the background (don't hang page until complete)
- * @param int $timeout - cURL connect timeout
- */
- function curl_file_get_contents($url, $fp, $bg=TRUE, $timeout = 1) {
- {
- # Call curl in the background to download the file
- $cmd = 'curl '.wfEscapeShellArg($url).' -o '.$fp.' &';
- wfDebug('Curl download initiated='.$cmd );
- $success = false;
- $file_contents = array();
- $file_contents['err'] = wfShellExec($cmd, $file_contents['return']);
- if($file_contents['err']==0){//Success
- $success = true;
- }
- }
- return $success;
- }
-
+ * Use cURL to read the content of a URL into a string
+ * ref: http://groups-beta.google.com/group/comp.lang.php/browse_thread/thread/8efbbaced3c45e3c/d63c7891cf8e380b?lnk=raot
+ * @param string $url - the URL to fetch
+ * @param resource $fp - filename to write file contents to
+ * @param boolean $bg - call cURL in the background (don't hang page until complete)
+ * @param int $timeout - cURL connect timeout
+ */
+ function curl_file_get_contents($url, $fp, $bg=TRUE, $timeout = 1) {
+ # Call curl in the background to download the file
+ $cmd = 'curl '.wfEscapeShellArg($url).' -o '.$fp.' &';
+ wfDebug('Curl download initiated='.$cmd );
+ $success = false;
+ $file_contents = array();
+ $file_contents['err'] = wfShellExec($cmd, $file_contents['return']);
+ if($file_contents['err']==0){//Success
+ $success = true;
+ }
+ return $success;
+ }
+
function getMasterDB() {
if ( !isset( $this->dbConn ) ) {
$class = 'Database' . ucfirst( $this->dbType );
- $this->dbConn = new $class( $this->dbServer, $this->dbUser,
- $this->dbPassword, $this->dbName, false, $this->dbFlags,
+ $this->dbConn = new $class( $this->dbServer, $this->dbUser,
+ $this->dbPassword, $this->dbName, false, $this->dbFlags,
$this->tablePrefix );
}
return $this->dbConn;
@@ -219,10 +215,10 @@ class ICFile extends LocalFile{
* Record a file upload in the upload log and the image table
*/
private function recordDownload($comment='', $timestamp = false ){
- global $wgUser;
+ global $wgUser;
$dbw = $this->repo->getMasterDB();
-
+
if ( $timestamp === false ) {
$timestamp = $dbw->timestamp();
}
@@ -252,7 +248,7 @@ class ICFile extends LocalFile{
);
if( $dbw->affectedRows() == 0 ) {
- # Collision, this is an update of a file
+ # Collision, this is an update of a file
# Update the current image row
$dbw->update( 'ic_image',
array( /* SET */
@@ -297,7 +293,7 @@ class ICFile extends LocalFile{
$descTitle->purgeSquid();
}
-
+
# Commit the transaction now, in case something goes wrong later
# The most important thing is that files don't get lost, especially archives
$dbw->immediateCommit();
@@ -308,6 +304,6 @@ class ICFile extends LocalFile{
return true;
}
-
+
}
diff --git a/includes/filerepo/LocalFile.php b/includes/filerepo/LocalFile.php
index 1e5fc449..9b06fe2d 100644
--- a/includes/filerepo/LocalFile.php
+++ b/includes/filerepo/LocalFile.php
@@ -5,7 +5,7 @@
/**
* Bump this number when serialized cache records may be incompatible.
*/
-define( 'MW_FILE_VERSION', 4 );
+define( 'MW_FILE_VERSION', 7 );
/**
* Class to represent a local file in the wiki's own database
@@ -29,24 +29,26 @@ class LocalFile extends File
/**#@+
* @private
*/
- var $fileExists, # does the file file exist on disk? (loadFromXxx)
- $historyLine, # Number of line to return by nextHistoryLine() (constructor)
- $historyRes, # result of the query for the file's history (nextHistoryLine)
- $width, # \
- $height, # |
- $bits, # --- returned by getimagesize (loadFromXxx)
- $attr, # /
- $media_type, # MEDIATYPE_xxx (bitmap, drawing, audio...)
- $mime, # MIME type, determined by MimeMagic::guessMimeType
- $major_mime, # Major mime type
- $minor_mime, # Minor mime type
- $size, # Size in bytes (loadFromXxx)
- $metadata, # Handler-specific metadata
- $timestamp, # Upload timestamp
- $sha1, # SHA-1 base 36 content hash
- $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx)
- $upgraded, # Whether the row was upgraded on load
- $locked; # True if the image row is locked
+ var $fileExists, # does the file file exist on disk? (loadFromXxx)
+ $historyLine, # Number of line to return by nextHistoryLine() (constructor)
+ $historyRes, # result of the query for the file's history (nextHistoryLine)
+ $width, # \
+ $height, # |
+ $bits, # --- returned by getimagesize (loadFromXxx)
+ $attr, # /
+ $media_type, # MEDIATYPE_xxx (bitmap, drawing, audio...)
+ $mime, # MIME type, determined by MimeMagic::guessMimeType
+ $major_mime, # Major mime type
+ $minor_mime, # Minor mime type
+ $size, # Size in bytes (loadFromXxx)
+ $metadata, # Handler-specific metadata
+ $timestamp, # Upload timestamp
+ $sha1, # SHA-1 base 36 content hash
+ $user, $user_text, # User, who uploaded the file
+ $description, # Description of current revision of the file
+ $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx)
+ $upgraded, # Whether the row was upgraded on load
+ $locked; # True if the image row is locked
/**#@-*/
@@ -110,12 +112,9 @@ class LocalFile extends File
wfDebug( "Pulling file metadata from cache key $key\n" );
$this->fileExists = $cachedValues['fileExists'];
if ( $this->fileExists ) {
- unset( $cachedValues['version'] );
- unset( $cachedValues['fileExists'] );
- foreach ( $cachedValues as $name => $value ) {
- $this->$name = $value;
- }
+ $this->setProps( $cachedValues );
}
+ $this->dataLoaded = true;
}
if ( $this->dataLoaded ) {
wfIncrStats( 'image_cache_hit' );
@@ -158,7 +157,7 @@ class LocalFile extends File
function getCacheFields( $prefix = 'img_' ) {
static $fields = array( 'size', 'width', 'height', 'bits', 'media_type',
- 'major_mime', 'minor_mime', 'metadata', 'timestamp', 'sha1' );
+ 'major_mime', 'minor_mime', 'metadata', 'timestamp', 'sha1', 'user', 'user_text', 'description' );
static $results = array();
if ( $prefix == '' ) {
return $fields;
@@ -184,7 +183,7 @@ class LocalFile extends File
# Unconditionally set loaded=true, we don't want the accessors constantly rechecking
$this->dataLoaded = true;
- $dbr = $this->repo->getSlaveDB();
+ $dbr = $this->repo->getMasterDB();
$row = $dbr->selectRow( 'image', $this->getCacheFields( 'img_' ),
array( 'img_name' => $this->getName() ), $fname );
@@ -294,6 +293,9 @@ class LocalFile extends File
$dbw = $this->repo->getMasterDB();
list( $major, $minor ) = self::splitMime( $this->mime );
+ if ( wfReadOnly() ) {
+ return;
+ }
wfDebug(__METHOD__.': upgrading '.$this->getName()." to the current schema\n");
$dbw->update( 'image',
@@ -313,6 +315,13 @@ class LocalFile extends File
wfProfileOut( __METHOD__ );
}
+ /**
+ * Set properties in this object to be equal to those given in the
+ * associative array $info. Only cacheable fields can be set.
+ *
+ * If 'mime' is given, it will be split into major_mime/minor_mime.
+ * If major_mime/minor_mime are given, $this->mime will also be set.
+ */
function setProps( $info ) {
$this->dataLoaded = true;
$fields = $this->getCacheFields( '' );
@@ -378,6 +387,20 @@ class LocalFile extends File
}
/**
+ * Returns ID or name of user who uploaded the file
+ *
+ * @param $type string 'text' or 'id'
+ */
+ function getUser($type='text') {
+ $this->load();
+ if( $type == 'text' ) {
+ return $this->user_text;
+ } elseif( $type == 'id' ) {
+ return $this->user;
+ }
+ }
+
+ /**
* Get handler-specific metadata
*/
function getMetadata() {
@@ -555,6 +578,28 @@ class LocalFile extends File
/** purgeDescription inherited */
/** purgeEverything inherited */
+ function getHistory($limit = null, $start = null, $end = null) {
+ $dbr = $this->repo->getSlaveDB();
+ $conds = $opts = array();
+ $conds[] = "oi_name = " . $dbr->addQuotes( $this->title->getDBKey() );
+ if( $start !== null ) {
+ $conds[] = "oi_timestamp <= " . $dbr->addQuotes( $dbr->timestamp( $start ) );
+ }
+ if( $end !== null ) {
+ $conds[] = "oi_timestamp >= " . $dbr->addQuotes( $dbr->timestamp( $end ) );
+ }
+ if( $limit ) {
+ $opts['LIMIT'] = $limit;
+ }
+ $opts['ORDER BY'] = 'oi_timestamp DESC';
+ $res = $dbr->select('oldimage', '*', $conds, __METHOD__, $opts);
+ $r = array();
+ while( $row = $dbr->fetchObject($res) ) {
+ $r[] = OldLocalFile::newFromRow($row, $this->repo);
+ }
+ return $r;
+ }
+
/**
* Return the history of this file, line by line.
* starts with current version, then old versions.
@@ -566,6 +611,9 @@ class LocalFile extends File
* @public
*/
function nextHistoryLine() {
+ # Polymorphic function name to distinguish foreign and local fetches
+ $fname = get_class( $this ) . '::' . __FUNCTION__;
+
$dbr = $this->repo->getSlaveDB();
if ( $this->historyLine == 0 ) {// called for the first time, return line from cur
@@ -575,7 +623,7 @@ class LocalFile extends File
"'' AS oi_archive_name"
),
array( 'img_name' => $this->title->getDBkey() ),
- __METHOD__
+ $fname
);
if ( 0 == $dbr->numRows( $this->historyRes ) ) {
$dbr->freeResult($this->historyRes);
@@ -586,7 +634,7 @@ class LocalFile extends File
$dbr->freeResult($this->historyRes);
$this->historyRes = $dbr->select( 'oldimage', '*',
array( 'oi_name' => $this->title->getDBkey() ),
- __METHOD__,
+ $fname,
array( 'ORDER BY' => 'oi_timestamp DESC' )
);
}
@@ -678,6 +726,10 @@ class LocalFile extends File
if ( !$props ) {
$props = $this->repo->getFileProps( $this->getVirtualUrl() );
}
+ $props['description'] = $comment;
+ $props['user'] = $wgUser->getID();
+ $props['user_text'] = $wgUser->getName();
+ $props['timestamp'] = wfTimestamp( TS_MW );
$this->setProps( $props );
// Delete thumbnails and refresh the metadata cache
@@ -964,6 +1016,11 @@ class LocalFile extends File
return $html;
}
+ function getDescription() {
+ $this->load();
+ return $this->description;
+ }
+
function getTimestamp() {
$this->load();
return $this->timestamp;
@@ -1188,12 +1245,12 @@ class LocalFileDeleteBatch {
list( $oldRels, $deleteCurrent ) = $this->getOldRels();
if ( $deleteCurrent ) {
+ $concat = $dbw->buildConcat( array( "img_sha1", $encExt ) );
$where = array( 'img_name' => $this->file->getName() );
$dbw->insertSelect( 'filearchive', 'image',
array(
'fa_storage_group' => $encGroup,
- 'fa_storage_key' => "IF(img_sha1='', '', CONCAT(img_sha1,$encExt))",
-
+ 'fa_storage_key' => "CASE WHEN img_sha1='' THEN '' ELSE $concat END",
'fa_deleted_user' => $encUserId,
'fa_deleted_timestamp' => $encTimestamp,
'fa_deleted_reason' => $encReason,
@@ -1217,15 +1274,14 @@ class LocalFileDeleteBatch {
}
if ( count( $oldRels ) ) {
+ $concat = $dbw->buildConcat( array( "oi_sha1", $encExt ) );
$where = array(
'oi_name' => $this->file->getName(),
'oi_archive_name IN (' . $dbw->makeList( array_keys( $oldRels ) ) . ')' );
-
$dbw->insertSelect( 'filearchive', 'oldimage',
array(
'fa_storage_group' => $encGroup,
- 'fa_storage_key' => "IF(oi_sha1='', '', CONCAT(oi_sha1,$encExt))",
-
+ 'fa_storage_key' => "CASE WHEN oi_sha1='' THEN '' ELSE $concat END",
'fa_deleted_user' => $encUserId,
'fa_deleted_timestamp' => $encTimestamp,
'fa_deleted_reason' => $encReason,
@@ -1252,9 +1308,6 @@ class LocalFileDeleteBatch {
function doDBDeletes() {
$dbw = $this->file->repo->getMasterDB();
list( $oldRels, $deleteCurrent ) = $this->getOldRels();
- if ( $deleteCurrent ) {
- $dbw->delete( 'image', array( 'img_name' => $this->file->getName() ), __METHOD__ );
- }
if ( count( $oldRels ) ) {
$dbw->delete( 'oldimage',
array(
@@ -1262,6 +1315,9 @@ class LocalFileDeleteBatch {
'oi_archive_name IN (' . $dbw->makeList( array_keys( $oldRels ) ) . ')'
), __METHOD__ );
}
+ if ( $deleteCurrent ) {
+ $dbw->delete( 'image', array( 'img_name' => $this->file->getName() ), __METHOD__ );
+ }
}
/**
diff --git a/includes/filerepo/LocalRepo.php b/includes/filerepo/LocalRepo.php
index 72f9e9a6..a259bd48 100644
--- a/includes/filerepo/LocalRepo.php
+++ b/includes/filerepo/LocalRepo.php
@@ -62,4 +62,52 @@ class LocalRepo extends FSRepo {
}
return $status;
}
+
+ /**
+ * Function link Title::getArticleID().
+ * We can't say Title object, what database it should use, so we duplicate that function here.
+ */
+ private function getArticleID( $title ) {
+ if( !$title instanceof Title ) {
+ return 0;
+ }
+ $dbr = $this->getSlaveDB();
+ $id = $dbr->selectField(
+ 'page', // Table
+ 'page_id', //Field
+ array( //Conditions
+ 'page_namespace' => $title->getNamespace(),
+ 'page_title' => $title->getDbKey(),
+ ),
+ __METHOD__ //Function name
+ );
+ return $id;
+ }
+
+ function checkRedirect( $title ) {
+ global $wgFileRedirects;
+ if( !$wgFileRedirects ) {
+ return false;
+ }
+
+ if( $title instanceof Title && $title->getNamespace() == NS_MEDIA ) {
+ $title = Title::makeTitle( NS_IMAGE, $title->getText() );
+ }
+
+ $id = $this->getArticleID( $title );
+ if( !$id ) {
+ return false;
+ }
+ $dbr = $this->getSlaveDB();
+ $row = $dbr->selectRow(
+ 'redirect',
+ array( 'rd_title', 'rd_namespace' ),
+ array( 'rd_from' => $id ),
+ __METHOD__
+ );
+ if( !$row ) {
+ return false;
+ }
+ return Title::makeTitle( $row->rd_namespace, $row->rd_title );
+ }
}
diff --git a/includes/filerepo/NullRepo.php b/includes/filerepo/NullRepo.php
new file mode 100644
index 00000000..87bfd3ab
--- /dev/null
+++ b/includes/filerepo/NullRepo.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * File repository with no files, for performance testing
+ */
+
+class NullRepo extends FileRepo {
+ function __construct( $info ) {}
+
+ function storeBatch( $triplets, $flags = 0 ) {
+ return false;
+ }
+
+ function storeTemp( $originalName, $srcPath ) {
+ return false;
+ }
+ function publishBatch( $triplets, $flags = 0 ) {
+ return false;
+ }
+ function deleteBatch( $sourceDestPairs ) {
+ return false;
+ }
+ function getFileProps( $virtualUrl ) {
+ return false;
+ }
+ function newFile( $title, $time = false ) {
+ return false;
+ }
+ function findFile( $title, $time = false ) {
+ return false;
+ }
+}
+
+?>
diff --git a/includes/filerepo/RepoGroup.php b/includes/filerepo/RepoGroup.php
index 23d222af..b0e1d782 100644
--- a/includes/filerepo/RepoGroup.php
+++ b/includes/filerepo/RepoGroup.php
@@ -32,6 +32,13 @@ class RepoGroup {
}
/**
+ * Set the singleton instance to a given object
+ */
+ static function setSingleton( $instance ) {
+ self::$instance = $instance;
+ }
+
+ /**
* Construct a group of file repositories.
* @param array $data Array of repository info arrays.
* Each info array is an associative array with the 'class' member
@@ -47,8 +54,8 @@ class RepoGroup {
* Search repositories for an image.
* You can also use wfGetFile() to do this.
* @param mixed $title Title object or string
- * @param mixed $time The 14-char timestamp before which the file should
- * have been uploaded, or false for the current version
+ * @param mixed $time The 14-char timestamp the file should have
+ * been uploaded, or false for the current version
* @return File object or false if it is not found
*/
function findFile( $title, $time = false ) {
@@ -70,13 +77,34 @@ class RepoGroup {
}
/**
+ * Interface for FileRepo::checkRedirect()
+ */
+ function checkRedirect( $title ) {
+ if ( !$this->reposInitialised ) {
+ $this->initialiseRepos();
+ }
+
+ $redir = $this->localRepo->checkRedirect( $title );
+ if( $redir ) {
+ return $redir;
+ }
+ foreach ( $this->foreignRepos as $repo ) {
+ $redir = $repo->checkRedirect( $title );
+ if ( $redir ) {
+ return $redir;
+ }
+ }
+ return false;
+ }
+
+ /**
* Get the repo instance with a given key.
*/
function getRepo( $index ) {
if ( !$this->reposInitialised ) {
$this->initialiseRepos();
}
- if ( $index == 'local' ) {
+ if ( $index === 'local' ) {
return $this->localRepo;
} elseif ( isset( $this->foreignRepos[$index] ) ) {
return $this->foreignRepos[$index];
@@ -84,6 +112,19 @@ class RepoGroup {
return false;
}
}
+ /**
+ * Get the repo instance by its name
+ */
+ function getRepoByName( $name ) {
+ if ( !$this->reposInitialised ) {
+ $this->initialiseRepos();
+ }
+ foreach ( $this->foreignRepos as $key => $repo ) {
+ if ( $repo->name == $name)
+ return $repo;
+ }
+ return false;
+ }
/**
* Get the local repository, i.e. the one corresponding to the local image