diff options
Diffstat (limited to 'includes/filerepo')
-rw-r--r-- | includes/filerepo/ArchivedFile.php | 306 | ||||
-rw-r--r-- | includes/filerepo/FSRepo.php | 2 | ||||
-rw-r--r-- | includes/filerepo/File.php | 75 | ||||
-rw-r--r-- | includes/filerepo/FileRepo.php | 25 | ||||
-rw-r--r-- | includes/filerepo/FileRepoStatus.php | 12 | ||||
-rw-r--r-- | includes/filerepo/ICRepo.php | 204 | ||||
-rw-r--r-- | includes/filerepo/LocalFile.php | 128 | ||||
-rw-r--r-- | includes/filerepo/LocalRepo.php | 48 | ||||
-rw-r--r-- | includes/filerepo/NullRepo.php | 34 | ||||
-rw-r--r-- | includes/filerepo/RepoGroup.php | 47 |
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 |