2007-05-30 21:02:32 +00:00
|
|
|
<?php
|
2010-09-04 18:13:18 +00:00
|
|
|
/**
|
|
|
|
|
* Local repository that stores files in the local filesystem and registers them
|
|
|
|
|
* in the wiki's own database.
|
|
|
|
|
*
|
|
|
|
|
* @file
|
|
|
|
|
* @ingroup FileRepo
|
|
|
|
|
*/
|
|
|
|
|
|
2007-05-30 21:02:32 +00:00
|
|
|
/**
|
|
|
|
|
* A repository that stores files in the local filesystem and registers them
|
|
|
|
|
* in the wiki's own database. This is the most commonly used repository class.
|
2011-12-20 03:52:06 +00:00
|
|
|
*
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @ingroup FileRepo
|
2007-05-30 21:02:32 +00:00
|
|
|
*/
|
2011-12-20 03:52:06 +00:00
|
|
|
class LocalRepo extends FileRepo {
|
2012-02-08 09:28:14 +00:00
|
|
|
var $fileFactory = array( 'LocalFile' , 'newFromTitle' );
|
|
|
|
|
var $fileFactoryKey = array( 'LocalFile' , 'newFromKey' );
|
|
|
|
|
var $fileFromRowFactory = array( 'LocalFile' , 'newFromRow' );
|
|
|
|
|
var $oldFileFactory = array( 'OldLocalFile', 'newFromTitle' );
|
|
|
|
|
var $oldFileFactoryKey = array( 'OldLocalFile', 'newFromKey' );
|
|
|
|
|
var $oldFileFromRowFactory = array( 'OldLocalFile', 'newFromRow' );
|
2007-05-30 21:02:32 +00:00
|
|
|
|
2011-03-07 14:59:41 +00:00
|
|
|
/**
|
|
|
|
|
* @throws MWException
|
2011-12-20 03:52:06 +00:00
|
|
|
* @param $row
|
2011-03-07 14:59:41 +00:00
|
|
|
* @return File
|
|
|
|
|
*/
|
2007-05-30 21:02:32 +00:00
|
|
|
function newFileFromRow( $row ) {
|
|
|
|
|
if ( isset( $row->img_name ) ) {
|
2008-05-30 13:16:08 +00:00
|
|
|
return call_user_func( $this->fileFromRowFactory, $row, $this );
|
2007-05-30 21:02:32 +00:00
|
|
|
} elseif ( isset( $row->oi_name ) ) {
|
2008-05-30 13:16:08 +00:00
|
|
|
return call_user_func( $this->oldFileFromRowFactory, $row, $this );
|
2007-05-30 21:02:32 +00:00
|
|
|
} else {
|
|
|
|
|
throw new MWException( __METHOD__.': invalid row' );
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2011-05-28 18:59:42 +00:00
|
|
|
/**
|
|
|
|
|
* @param $title
|
|
|
|
|
* @param $archiveName
|
|
|
|
|
* @return OldLocalFile
|
|
|
|
|
*/
|
2007-05-31 00:35:07 +00:00
|
|
|
function newFromArchiveName( $title, $archiveName ) {
|
|
|
|
|
return OldLocalFile::newFromArchiveName( $title, $this, $archiveName );
|
|
|
|
|
}
|
2007-07-22 14:45:12 +00:00
|
|
|
|
|
|
|
|
/**
|
2008-04-14 07:45:50 +00:00
|
|
|
* Delete files in the deleted directory if they are not referenced in the
|
|
|
|
|
* filearchive table. This needs to be done in the repo because it needs to
|
|
|
|
|
* interleave database locks with file operations, which is potentially a
|
2007-07-22 14:45:12 +00:00
|
|
|
* remote operation.
|
2011-05-29 14:24:27 +00:00
|
|
|
*
|
|
|
|
|
* @param $storageKeys array
|
|
|
|
|
*
|
2007-07-22 14:45:12 +00:00
|
|
|
* @return FileRepoStatus
|
|
|
|
|
*/
|
|
|
|
|
function cleanupDeletedBatch( $storageKeys ) {
|
2011-12-20 03:52:06 +00:00
|
|
|
$backend = $this->backend; // convenience
|
2007-07-22 14:45:12 +00:00
|
|
|
$root = $this->getZonePath( 'deleted' );
|
|
|
|
|
$dbw = $this->getMasterDB();
|
|
|
|
|
$status = $this->newGood();
|
2011-05-29 14:24:27 +00:00
|
|
|
$storageKeys = array_unique( $storageKeys );
|
2007-07-22 14:45:12 +00:00
|
|
|
foreach ( $storageKeys as $key ) {
|
|
|
|
|
$hashPath = $this->getDeletedHashPath( $key );
|
|
|
|
|
$path = "$root/$hashPath$key";
|
2012-02-24 17:00:52 +00:00
|
|
|
$dbw->begin( __METHOD__ );
|
2011-10-15 17:07:53 +00:00
|
|
|
// Check for usage in deleted/hidden files and pre-emptively
|
|
|
|
|
// lock the key to avoid any future use until we are finished.
|
|
|
|
|
$deleted = $this->deletedFileHasKey( $key, 'lock' );
|
|
|
|
|
$hidden = $this->hiddenFileHasKey( $key, 'lock' );
|
|
|
|
|
if ( !$deleted && !$hidden ) { // not in use now
|
2007-07-23 17:22:09 +00:00
|
|
|
wfDebug( __METHOD__ . ": deleting $key\n" );
|
2011-12-20 03:52:06 +00:00
|
|
|
$op = array( 'op' => 'delete', 'src' => $path );
|
|
|
|
|
if ( !$backend->doOperation( $op )->isOK() ) {
|
2007-07-23 17:22:09 +00:00
|
|
|
$status->error( 'undelete-cleanup-error', $path );
|
|
|
|
|
$status->failCount++;
|
|
|
|
|
}
|
2007-07-22 14:45:12 +00:00
|
|
|
} else {
|
2007-07-23 17:22:09 +00:00
|
|
|
wfDebug( __METHOD__ . ": $key still in use\n" );
|
2007-07-22 14:45:12 +00:00
|
|
|
$status->successCount++;
|
|
|
|
|
}
|
2012-02-24 17:00:52 +00:00
|
|
|
$dbw->commit( __METHOD__ );
|
2007-07-22 14:45:12 +00:00
|
|
|
}
|
|
|
|
|
return $status;
|
|
|
|
|
}
|
2011-03-06 20:05:41 +00:00
|
|
|
|
2011-10-15 17:07:53 +00:00
|
|
|
/**
|
|
|
|
|
* Check if a deleted (filearchive) file has this sha1 key
|
2011-12-20 03:52:06 +00:00
|
|
|
*
|
2011-10-15 17:07:53 +00:00
|
|
|
* @param $key String File storage key (base-36 sha1 key with file extension)
|
|
|
|
|
* @param $lock String|null Use "lock" to lock the row via FOR UPDATE
|
|
|
|
|
* @return bool File with this key is in use
|
|
|
|
|
*/
|
|
|
|
|
protected function deletedFileHasKey( $key, $lock = null ) {
|
|
|
|
|
$options = ( $lock === 'lock' ) ? array( 'FOR UPDATE' ) : array();
|
|
|
|
|
|
|
|
|
|
$dbw = $this->getMasterDB();
|
|
|
|
|
return (bool)$dbw->selectField( 'filearchive', '1',
|
|
|
|
|
array( 'fa_storage_group' => 'deleted', 'fa_storage_key' => $key ),
|
|
|
|
|
__METHOD__, $options
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if a hidden (revision delete) file has this sha1 key
|
2011-12-20 03:52:06 +00:00
|
|
|
*
|
2011-10-15 17:07:53 +00:00
|
|
|
* @param $key String File storage key (base-36 sha1 key with file extension)
|
|
|
|
|
* @param $lock String|null Use "lock" to lock the row via FOR UPDATE
|
|
|
|
|
* @return bool File with this key is in use
|
|
|
|
|
*/
|
|
|
|
|
protected function hiddenFileHasKey( $key, $lock = null ) {
|
|
|
|
|
$options = ( $lock === 'lock' ) ? array( 'FOR UPDATE' ) : array();
|
|
|
|
|
|
|
|
|
|
$sha1 = self::getHashFromKey( $key );
|
|
|
|
|
$ext = File::normalizeExtension( substr( $key, strcspn( $key, '.' ) + 1 ) );
|
|
|
|
|
|
|
|
|
|
$dbw = $this->getMasterDB();
|
|
|
|
|
return (bool)$dbw->selectField( 'oldimage', '1',
|
|
|
|
|
array( 'oi_sha1' => $sha1,
|
|
|
|
|
'oi_archive_name ' . $dbw->buildLike( $dbw->anyString(), ".$ext" ),
|
|
|
|
|
$dbw->bitAnd( 'oi_deleted', File::DELETED_FILE ) => File::DELETED_FILE ),
|
2012-01-01 23:31:20 +00:00
|
|
|
__METHOD__, $options
|
2011-10-15 17:07:53 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-06 20:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* Gets the SHA1 hash from a storage key
|
|
|
|
|
*
|
|
|
|
|
* @param string $key
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public static function getHashFromKey( $key ) {
|
|
|
|
|
return strtok( $key, '.' );
|
|
|
|
|
}
|
2009-04-20 03:06:49 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks if there is a redirect named as $title
|
|
|
|
|
*
|
2010-03-26 21:55:13 +00:00
|
|
|
* @param $title Title of file
|
2011-11-17 03:14:05 +00:00
|
|
|
* @return bool
|
2009-04-20 03:06:49 +00:00
|
|
|
*/
|
2011-11-04 23:49:03 +00:00
|
|
|
function checkRedirect( Title $title ) {
|
2009-04-20 03:06:49 +00:00
|
|
|
global $wgMemc;
|
|
|
|
|
|
2011-11-04 23:49:03 +00:00
|
|
|
$title = File::normalizeTitle( $title, 'exception' );
|
2009-04-20 03:06:49 +00:00
|
|
|
|
2009-10-06 20:23:12 +00:00
|
|
|
$memcKey = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
|
2009-06-17 07:31:00 +00:00
|
|
|
if ( $memcKey === false ) {
|
2009-10-06 20:23:12 +00:00
|
|
|
$memcKey = $this->getLocalCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
|
2009-06-17 07:31:00 +00:00
|
|
|
$expiry = 300; // no invalidation, 5 minutes
|
|
|
|
|
} else {
|
|
|
|
|
$expiry = 86400; // has invalidation, 1 day
|
|
|
|
|
}
|
2009-04-20 03:06:49 +00:00
|
|
|
$cachedValue = $wgMemc->get( $memcKey );
|
2009-06-17 07:31:00 +00:00
|
|
|
if ( $cachedValue === ' ' || $cachedValue === '' ) {
|
|
|
|
|
// Does not exist
|
2009-04-20 03:06:49 +00:00
|
|
|
return false;
|
2009-06-17 07:31:00 +00:00
|
|
|
} elseif ( strval( $cachedValue ) !== '' ) {
|
2009-10-06 20:23:12 +00:00
|
|
|
return Title::newFromText( $cachedValue, NS_FILE );
|
2009-06-17 07:31:00 +00:00
|
|
|
} // else $cachedValue is false or null: cache miss
|
2009-04-20 03:06:49 +00:00
|
|
|
|
|
|
|
|
$id = $this->getArticleID( $title );
|
|
|
|
|
if( !$id ) {
|
2009-06-17 07:31:00 +00:00
|
|
|
$wgMemc->set( $memcKey, " ", $expiry );
|
2009-04-20 03:06:49 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$dbr = $this->getSlaveDB();
|
|
|
|
|
$row = $dbr->selectRow(
|
|
|
|
|
'redirect',
|
|
|
|
|
array( 'rd_title', 'rd_namespace' ),
|
|
|
|
|
array( 'rd_from' => $id ),
|
|
|
|
|
__METHOD__
|
|
|
|
|
);
|
|
|
|
|
|
2009-10-06 20:23:12 +00:00
|
|
|
if( $row && $row->rd_namespace == NS_FILE ) {
|
2009-06-17 07:31:00 +00:00
|
|
|
$targetTitle = Title::makeTitle( $row->rd_namespace, $row->rd_title );
|
2009-10-06 20:23:12 +00:00
|
|
|
$wgMemc->set( $memcKey, $targetTitle->getDBkey(), $expiry );
|
2009-06-17 07:31:00 +00:00
|
|
|
return $targetTitle;
|
|
|
|
|
} else {
|
|
|
|
|
$wgMemc->set( $memcKey, '', $expiry );
|
2009-04-20 03:06:49 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-01-16 18:27:43 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Function link Title::getArticleID().
|
|
|
|
|
* We can't say Title object, what database it should use, so we duplicate that function here.
|
2011-12-20 03:52:06 +00:00
|
|
|
*
|
2011-02-18 23:56:08 +00:00
|
|
|
* @param $title Title
|
2012-02-09 21:33:27 +00:00
|
|
|
* @return bool|int|mixed
|
2008-01-16 18:27:43 +00:00
|
|
|
*/
|
2008-04-06 10:18:47 +00:00
|
|
|
protected function getArticleID( $title ) {
|
2008-01-16 18:27:43 +00:00
|
|
|
if( !$title instanceof Title ) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
$dbr = $this->getSlaveDB();
|
|
|
|
|
$id = $dbr->selectField(
|
2011-12-20 03:52:06 +00:00
|
|
|
'page', // Table
|
|
|
|
|
'page_id', //Field
|
|
|
|
|
array( //Conditions
|
2008-01-16 18:27:43 +00:00
|
|
|
'page_namespace' => $title->getNamespace(),
|
2009-05-24 08:29:10 +00:00
|
|
|
'page_title' => $title->getDBkey(),
|
2008-01-16 18:27:43 +00:00
|
|
|
),
|
2011-12-20 03:52:06 +00:00
|
|
|
__METHOD__ //Function name
|
2008-01-16 18:27:43 +00:00
|
|
|
);
|
|
|
|
|
return $id;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-17 07:31:00 +00:00
|
|
|
/**
|
2012-02-01 20:53:38 +00:00
|
|
|
* Get an array or iterator of file objects for files that have a given
|
2009-06-17 07:31:00 +00:00
|
|
|
* SHA-1 content hash.
|
2011-12-20 03:52:06 +00:00
|
|
|
*
|
2012-02-01 20:53:38 +00:00
|
|
|
* @param $hash String a sha1 hash to look for
|
2011-11-17 03:14:05 +00:00
|
|
|
* @return Array
|
2009-06-17 07:31:00 +00:00
|
|
|
*/
|
2008-05-14 15:11:48 +00:00
|
|
|
function findBySha1( $hash ) {
|
|
|
|
|
$dbr = $this->getSlaveDB();
|
|
|
|
|
$res = $dbr->select(
|
|
|
|
|
'image',
|
|
|
|
|
LocalFile::selectFields(),
|
|
|
|
|
array( 'img_sha1' => $hash )
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$result = array();
|
2010-10-13 23:11:40 +00:00
|
|
|
foreach ( $res as $row ) {
|
2008-05-14 15:11:48 +00:00
|
|
|
$result[] = $this->newFileFromRow( $row );
|
2010-10-13 23:11:40 +00:00
|
|
|
}
|
2010-11-06 11:30:21 +00:00
|
|
|
$res->free();
|
2010-10-13 23:11:40 +00:00
|
|
|
|
2008-05-14 15:11:48 +00:00
|
|
|
return $result;
|
|
|
|
|
}
|
2009-06-17 07:31:00 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a connection to the slave DB
|
2012-02-09 21:33:27 +00:00
|
|
|
* @return DatabaseBase
|
2009-06-17 07:31:00 +00:00
|
|
|
*/
|
|
|
|
|
function getSlaveDB() {
|
|
|
|
|
return wfGetDB( DB_SLAVE );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a connection to the master DB
|
2012-02-09 21:33:27 +00:00
|
|
|
* @return DatabaseBase
|
2009-06-17 07:31:00 +00:00
|
|
|
*/
|
|
|
|
|
function getMasterDB() {
|
|
|
|
|
return wfGetDB( DB_MASTER );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a key on the primary cache for this repository.
|
|
|
|
|
* Returns false if the repository's cache is not accessible at this site.
|
|
|
|
|
* The parameters are the parts of the key, as for wfMemcKey().
|
2011-12-20 03:52:06 +00:00
|
|
|
*
|
2011-11-17 03:14:05 +00:00
|
|
|
* @return string
|
2009-06-17 07:31:00 +00:00
|
|
|
*/
|
|
|
|
|
function getSharedCacheKey( /*...*/ ) {
|
|
|
|
|
$args = func_get_args();
|
|
|
|
|
return call_user_func_array( 'wfMemcKey', $args );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Invalidates image redirect cache related to that image
|
|
|
|
|
*
|
2010-03-26 21:55:13 +00:00
|
|
|
* @param $title Title of page
|
2012-01-12 19:41:18 +00:00
|
|
|
* @return void
|
2010-03-26 21:55:13 +00:00
|
|
|
*/
|
2011-11-04 23:33:53 +00:00
|
|
|
function invalidateImageRedirect( Title $title ) {
|
2009-06-17 07:31:00 +00:00
|
|
|
global $wgMemc;
|
2009-10-06 20:23:12 +00:00
|
|
|
$memcKey = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
|
2009-06-17 07:31:00 +00:00
|
|
|
if ( $memcKey ) {
|
|
|
|
|
$wgMemc->delete( $memcKey );
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-05-30 21:02:32 +00:00
|
|
|
}
|
2009-06-17 07:31:00 +00:00
|
|
|
|