Jpond has uploaded a new change for review. https://gerrit.wikimedia.org/r/51393
Change subject: (bug 45364)Fixed Moving/Rename, synched for Repo Upgrades ...................................................................... (bug 45364)Fixed Moving/Rename, synched for Repo Upgrades This is a major update to incorporate several evolved changes to the core FileRepo modules into the extension and to bring the extension up to current coding and core standards. Specifically: * [https://bugzilla.wikimedia.org/show_bug.cgi?id=45364 Bug Fix for 45364] - Fixed Move/Relocate issue and cleared result array for Title * Synchronized with changes made to the core FileRepo classes and methods. It now reflects fixes and upgrades as of 2013-2-27 * Updated for Documentation (doxygen) * Cleared result array for userCan hook (NSFileRepolockdownUserCan) since Title now takes anything in that array to be an affirmative (user can) Should be backwards compatible, but only tested with HEAD, 1.19 and 1.20 Bug: 45364 Change-Id: I00fad8394ac8fe6061d4b81250c0ae7a42e2b419 --- M NSFileRepo.php M NSFileRepo_body.php M README 3 files changed, 251 insertions(+), 194 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/NSFileRepo refs/changes/93/51393/1 diff --git a/NSFileRepo.php b/NSFileRepo.php index 485a35a..2ae0833 100644 --- a/NSFileRepo.php +++ b/NSFileRepo.php @@ -96,8 +96,10 @@ } elseif( function_exists( 'lockdownUserPermissionsErrors' ) ) { if( $title->getNamespace() == NS_FILE ) { $ntitle = Title::newFromText( $title->mDbkeyform ); - return ( $ntitle->getNamespace() < 100 ) ? - true : lockdownUserPermissionsErrors( $ntitle, $user, $action, $result ); + $ret_val = ( $ntitle->getNamespace() < 100 ) ? + true : lockdownUserPermissionsErrors( $ntitle, $user, $action, $result ); + $result = null; + return $ret_val; } } return true; diff --git a/NSFileRepo_body.php b/NSFileRepo_body.php index 66369c2..fa8d836 100644 --- a/NSFileRepo_body.php +++ b/NSFileRepo_body.php @@ -58,6 +58,44 @@ function getRel() { return $this->getHashPath() . $this->getFileNameStripped( $this->getName() ); } + /** + * Get the path, relative to the thumbnail zone root, of the + * thumbnail directory or a particular file if $suffix is specified + * + * @param $suffix bool|string if not false, the name of a thumbnail file + * + * @return string + */ + function getThumbRel( $suffix = false ) { + $path = $this->getRel(); + if ( $suffix !== false ) { +/* This is the part that changed from LocalFile */ + $path .= '/' . $this->getFileNameStripped( $suffix ); +/* End of changes */ + } + return $path; + } + + /** + * Get the path of an archived file relative to the public zone root + * + * @param $suffix bool|string if not false, the name of an archived thumbnail file + * + * @return string + */ + function getArchiveRel( $suffix = false ) { + $path = 'archive/' . $this->getHashPath(); + if ( $suffix === false ) { + $path = substr( $path, 0, -1 ); + } else { +/* This is the part that changed from LocalFile */ + $path .= '/' . $this->getFileNameStripped( $suffix ); +/* End of changes */ + } + return $path; + } + + /** * Get urlencoded relative path of the file @@ -67,7 +105,13 @@ rawurlencode( $this->getFileNameStripped( $this->getName() ) ); } - /** Get the URL of the thumbnail directory, or a particular file if $suffix is specified */ + /** + * Get the URL of the thumbnail directory, or a particular file if $suffix is specified + * + * @param $suffix bool|string if not false, the name of a thumbnail file + * + * @return string path + */ function getThumbUrl( $suffix = false ) { $path = $this->repo->getZoneUrl('thumb') . '/' . $this->getUrlRel(); if ( $suffix !== false ) { @@ -77,246 +121,202 @@ } - /** Return the file name of a thumbnail with the specified parameters */ - function thumbName( $params ) { + public function thumbName( $params, $flags = 0 ) { + $name = ( $this->repo && !( $flags & self::THUMB_FULL_NAME ) ) +/* This is the part that changed from LocalFile */ + ? $this->repo->nameForThumb( $this->getFileNameStripped( $this->getName() ) ) + : $this->getFileNameStripped( $this->getName() ); +/* End of changes */ + return $this->generateThumbName( $name, $params ); + } + + /** + * Generate a thumbnail file name from a name and specified parameters + * + * @param string $name + * @param array $params Parameters which will be passed to MediaHandler::makeParamString + * + * @return string + */ + public function generateThumbName( $name, $params ) { if ( !$this->getHandler() ) { return null; } $extension = $this->getExtension(); - list( $thumbExt, $thumbMime ) = $this->handler->getThumbType( $extension, $this->getMimeType() ); + list( $thumbExt, $thumbMime ) = $this->handler->getThumbType( + $extension, $this->getMimeType(), $params ); /* This is the part that changed from LocalFile */ - $thumbName = $this->handler->makeParamString( $params ) . '-' . $this->getFileNameStripped( $this->getName() ); + $thumbName = $this->handler->makeParamString( $params ) . '-' . + $this->getFileNameStripped( $this->getName() ); /* End of changes */ if ( $thumbExt != $extension ) { $thumbName .= ".$thumbExt"; } +/* And also need to retain namespace changed from LocalFile */ $bits = explode( ':',$this->getName() ); if ( count($bits) > 1 ) $thumbName = $bits[0] . ":" . $thumbName; +/* End of changes */ return $thumbName; } - - /** Get the path of the thumbnail directory, or a particular file if $suffix is specified */ - function getThumbPath( $suffix = false ) { - $path = $this->repo->getZonePath('thumb') . '/' . $this->getRel(); - if ( $suffix !== false ) { - $path .= '/' . $this->getFileNameStripped( $suffix ); - } - return $path; - } - - /** Get the relative path for an archive file */ - function getArchiveRel( $suffix = false ) { - $path = 'archive/' . $this->getHashPath(); - if ( $suffix === false ) { - $path = substr( $path, 0, -1 ); - } else { - $path .= $this->getFileNameStripped( $suffix ); - } - return $path; - } - - /** Get the URL of the archive directory, or a particular file if $suffix is specified */ + /** + * Get the URL of the archive directory, or a particular file if $suffix is specified + * + * @param $suffix bool|string if not false, the name of an archived file + * + * @return string + */ function getArchiveUrl( $suffix = false ) { - $path = $this->repo->getZoneUrl( 'public' ) . '/archive/' . $this->getHashPath(); + $this->assertRepoDefined(); + $ext = $this->getExtension(); + $path = $this->repo->getZoneUrl( 'public', $ext ) . '/archive/' . $this->getHashPath(); if ( $suffix === false ) { $path = substr( $path, 0, -1 ); } else { +/* This is the part that changed from LocalFile */ $path .= rawurlencode( $this->getFileNameStripped( $suffix ) ); +/* End of changes */ } return $path; } - /** Get the virtual URL for an archive file or directory */ + /** + * Get the public zone virtual URL for an archived version source file + * + * @param $suffix bool|string if not false, the name of a thumbnail file + * + * @return string + */ function getArchiveVirtualUrl( $suffix = false ) { + $this->assertRepoDefined(); $path = $this->repo->getVirtualUrl() . '/public/archive/' . $this->getHashPath(); if ( $suffix === false ) { $path = substr( $path, 0, -1 ); } else { +/* This is the part that changed from LocalFile */ $path .= rawurlencode( $this->getFileNameStripped( $suffix ) ); +/* End of changes */ } return $path; } - /** Get the virtual URL for a thumbnail file or directory */ + /** + * Get the virtual URL for a thumbnail file or directory + * + * @param $suffix bool|string if not false, the name of a thumbnail file + * + * @return string + */ function getThumbVirtualUrl( $suffix = false ) { + $this->assertRepoDefined(); $path = $this->repo->getVirtualUrl() . '/thumb/' . $this->getUrlRel(); if ( $suffix !== false ) { + $path .= '/' . rawurlencode( $suffix ); +/* This is the part that changed from LocalFile */ $path .= '/' . rawurlencode( $this->getFileNameStripped( $suffix ) ); +/* End of changes */ } return $path; } - /** Get the virtual URL for the file itself */ - function getVirtualUrl( $suffix = false ) { - $path = $this->repo->getVirtualUrl() . '/public/' . $this->getUrlRel(); - if ( $suffix !== false ) { - $path .= '/' . rawurlencode( $this->getFileNameStripped( $suffix ) ); - } - return $path; - } - - /** Strip namespace (if any) from file name */ + /** + * Strip namespace (if any) from file name + * + * @param $suffix the name of a thumbnail file + * + * @return string + */ function getFileNameStripped($suffix) { $bits = explode( ':', $suffix ); return $bits[ count( $bits ) -1 ]; } - + /** - * This function overrides the LocalFile because the archive name should not contain the namespace in the - * filename. Otherwise the function would have worked. This only affects reuploads - * - * Move or copy a file to its public location. Returns a FileRepoStatus object. - * On success, the value contains "new" or "archived", to indicate whether the file was new with that name. + * Move or copy a file to a specified location. Returns a FileRepoStatus + * object with the archive name in the "value" member on success. * * The archive name should be passed through to recordUpload for database * registration. * - * @param string $sourcePath Local filesystem path to the source image - * @param integer $flags A bitwise combination of: - * File::DELETE_SOURCE Delete the source file, i.e. move - * rather than copy + * @param $srcPath String: local filesystem path to the source image + * @param $dstRel String: target relative path + * @param $flags Integer: a bitwise combination of: + * File::DELETE_SOURCE Delete the source file, i.e. move rather than copy + * @param $options Array Optional additional parameters * @return FileRepoStatus object. On success, the value member contains the * archive name, or an empty string if it was a new file. */ - function publish( $srcPath, $flags = 0 ) { - $this->lock(); - $dstRel = $this->getRel(); - /* This is the part that changed from LocalFile */ - $archiveName = gmdate( 'YmdHis' ) . '!' . - $this->getFileNameStripped( $this->getName() ); - /* End of changes */ + function publishTo( $srcPath, $dstRel, $flags = 0, array $options = array() ) { + if ( $this->getRepo()->getReadOnlyReason() !== false ) { + return $this->readOnlyFatalStatus(); + } + + $this->lock(); // begin + +/* This is the part that changed from LocalFile */ + $archiveName = wfTimestamp( TS_MW ) . '!'. $this->getFileNameStripped( $this->getName() ); +/* End of changes */ $archiveRel = 'archive/' . $this->getHashPath() . $archiveName; $flags = $flags & File::DELETE_SOURCE ? LocalRepo::DELETE_SOURCE : 0; - $status = $this->repo->publish( $srcPath, $dstRel, $archiveRel, $flags ); + $status = $this->repo->publish( $srcPath, $dstRel, $archiveRel, $flags, $options ); + if ( $status->value == 'new' ) { $status->value = ''; } else { $status->value = $archiveName; } - $this->unlock(); + + $this->unlock(); // done + return $status; } /** - * The only thing changed here is that the array needs to strip the NS from the file name for the has (oldname is already fixed) - * Add the old versions of the image to the batch + * Move file to the new title + * + * Move current, old version and all thumbnails + * to the new filename. Old file is deleted. + * + * Cache purging is done; checks for validity + * and logging are caller's responsibility + * + * @param $target Title New file name + * @return FileRepoStatus object. */ - function addOlds() { - $archiveBase = 'archive'; - $this->olds = array(); - $this->oldCount = 0; - - $result = $this->db->select( 'oldimage', - array( 'oi_archive_name', 'oi_deleted' ), - array( 'oi_name' => $this->oldName ), - __METHOD__ - ); - foreach( $result as $row ) { - $oldName = $row->oi_archive_name; - $bits = explode( '!', $oldName, 2 ); - if( count( $bits ) != 2 ) { - wfDebug( "Invalid old file name: $oldName \n" ); - continue; - } - list( $timestamp, $filename ) = $bits; - if( $this->oldName != $filename ) { - wfDebug( "Invalid old file name: $oldName \n" ); - continue; - } - $this->oldCount++; - // Do we want to add those to oldCount? - if( $row->oi_deleted & File::DELETED_FILE ) { - continue; - } - $this->olds[] = array( - "{$archiveBase}/{$this->oldHash}{$oldName}", - /* This is the part that changed from LocalFile */ - "{$archiveBase}/{$this->newHash}{$timestamp}!" . - $this->getFileNameStripped( $this->newName ) - /* End of changes */ - ); + function move( $target ) { + if ( $this->getRepo()->getReadOnlyReason() !== false ) { + return $this->readOnlyFatalStatus(); } - $this->db->freeResult( $result ); + + wfDebugLog( 'imagemove', "Got request to move {$this->name} to " . $target->getText() ); +/* This is the part that changed from LocalFile */ + $batch = new NSLocalFileMoveBatch( $this, $target ); +/* End of changes */ + + $this->lock(); // begin + $batch->addCurrent(); + $archiveNames = $batch->addOlds(); + $status = $batch->execute(); + $this->unlock(); // done + + wfDebugLog( 'imagemove', "Finished moving {$this->name}" ); + + $this->purgeEverything(); + foreach ( $archiveNames as $archiveName ) { + $this->purgeOldThumbnails( $archiveName ); + } + if ( $status->isOK() ) { + // Now switch the object + $this->title = $target; + // Force regeneration of the name and hashpath + unset( $this->name ); + unset( $this->hashPath ); + // Purge the new image + $this->purgeEverything(); + } + + return $status; } - - /** - * The only thing changed here is to strip NS from the file name - * Delete cached transformed files - */ - function purgeThumbnails( $options = array() ) { - global $wgUseSquid; - // Delete thumbnails - $files = $this->getThumbnails(); - $dir = $this->getThumbPath(); - $urls = array(); - foreach ( $files as $file ) { - # Check that the base file name is part of the thumb name - # This is a basic sanity check to avoid erasing unrelated directories - - /* This is the part that changed from LocalFile */ - if ( strpos( $file, $this->getFileNameStripped($this->getName()) ) !== false ) { - /* End of changes */ - $url = $this->getThumbUrl( $file ); - $urls[] = $url; - wfSuppressWarnings(); - unlink( "$dir/$file" ); - wfRestoreWarnings(); - } - } - - // Purge the squid - if ( $wgUseSquid ) { - SquidUpdate::purge( $urls ); - } - } - - /** - * Replaces hard coded OldLocalFile::newFromRow to use $this->repo->oldFileFromRowFactory configuration - * This may not be necessary in the future if LocalFile is patched to allow configuration - */ - - function getHistory( $limit = null, $start = null, $end = null, $inc = true ) { - $dbr = $this->repo->getSlaveDB(); - $tables = array( 'oldimage' ); - $fields = OldLocalFile::selectFields(); - $conds = $opts = $join_conds = array(); - $eq = $inc ? '=' : ''; - $conds[] = "oi_name = " . $dbr->addQuotes( $this->title->getDBkey() ); - if( $start ) { - $conds[] = "oi_timestamp <$eq " . $dbr->addQuotes( $dbr->timestamp( $start ) ); - } - if( $end ) { - $conds[] = "oi_timestamp >$eq " . $dbr->addQuotes( $dbr->timestamp( $end ) ); - } - if( $limit ) { - $opts['LIMIT'] = $limit; - } - // Search backwards for time > x queries - $order = ( !$start && $end !== null ) ? 'ASC' : 'DESC'; - $opts['ORDER BY'] = "oi_timestamp $order"; - $opts['USE INDEX'] = array( 'oldimage' => 'oi_name_timestamp' ); - - wfRunHooks( 'LocalFile::getHistory', array( &$this, &$tables, &$fields, - &$conds, &$opts, &$join_conds ) ); - - $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $opts, $join_conds ); - $r = array(); - foreach( $res as $row ) { - /* This is the part that changed from LocalFile */ - if ( $this->repo->oldFileFromRowFactory ) { - $r[] = call_user_func( $this->repo->oldFileFromRowFactory, $row, $this->repo ); - } else { - $r[] = OldLocalFile::newFromRow( $row, $this->repo ); - } - /* End of changes */ - } - if( $order == 'ASC' ) { - $r = array_reverse( $r ); // make sure it ends up descending - } - return $r; - } - /** Instantiating this class using "self" @@ -425,3 +425,25 @@ return $file; } } + +/** + * Helper class for file movement + * @ingroup FileAbstraction + */ +class NSLocalFileMoveBatch extends LocalFileMoveBatch { + /** + * @param File $file + * @param Title $target + */ + function __construct( File $file, Title $target ) { + $this->file = $file; + $this->target = $target; + $this->oldHash = $this->file->repo->getHashPath( $this->file->getName() ); + $this->newHash = $this->file->repo->getHashPath( $this->target->getDBkey() ); + $this->oldName = $this->file->getName(); + $this->newName = $this->file->repo->getNameFromTitle( $this->target ); + $this->oldRel = $this->oldHash . $this->file->getFileNameStripped( $this->oldName ); + $this->newRel = $this->newHash . $this->file->getFileNameStripped( $this->newName ); + $this->db = $file->getRepo()->getMasterDb(); + } +} diff --git a/README b/README index 6aa828c..0788181 100644 --- a/README +++ b/README @@ -6,24 +6,23 @@ |type1 = user rights |type2 = |hook1 = userCan -|hook2 = ImgAuthBeforeStream +|hook2 = UploadForm:BeforeProcessing +|hook3 = ImgAuthBeforeStream |username = Jpond |author = Jack D. Pond |description = implements per-namespace group permissions for image and file rights protection |image = -|version = 1.3 -|update = 2010-4-23 -|mediawiki = 1.13, 1.14, 1.15, 1.16, 1.17-SVN +|version = 1.5 +|update = 2013-03-01 +|mediawiki = 1.13, 1.14, 1.15, 1.16, 1.17-SVN, 1.19, 1.20 |license = GNU General Public Licence 2.0 |download = {{WikimediaDownload|NSFileRepo}} |readme = [http://svn.wikimedia.org/svnroot/mediawiki/trunk/extensions/NSFileRepo/README README] |changelog = [http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/NSFileRepo/?view=log log] -|parameters = <!-- configuration parameters, for use in LocalSettings.php --> -|rights = <!-- user rights, for use with $wgGroupPermissions --> +|parameters = $wgWhitelistRead<br/>$wgImgAuthPublicTest<br/>$wgSpecialPageLockdown<br/>$wgActionLockdown<br/>$wgNamespacePermissionLockdown<br/>$wgExtraNamespaces<br/>$wgContentNamespaces<br/>$wgNonincludableNamespaces<br/> +|rights = $wgGroupPermissions |example = }} - -==What can this extension do?== The '''NSFileRepo''' restricts access to upload and read files and images to a given set of user groups associated with protected namespaces. Using this extension (within the security limitations noted above), you can protect not only pages and areas of your wiki, but also any uploaded images or files within those namespaces. @@ -78,13 +77,21 @@ == Release Notes == -=== NSFileRepo 1.4 === +=== NSFileRepo 1.5 (Planned) === +This is a major update to incorporate several evolved changes to the core FileRepo modules into the extension and to bring the extension up to current coding and core standards. Specifically: -=== NSFileRepo 1.4 (Planned) === +* [https://bugzilla.wikimedia.org/show_bug.cgi?id=45364 Bug Fix for 45364] - Fixed Move/Relocate issue and cleared result array for Title +* Synchronized with changes made to the core FileRepo classes and methods. It now reflects fixes and upgrades as of 2013-2-27 +* Updated for Documentation (doxygen) +* Cleared result array for userCan hook (NSFileRepolockdownUserCan) since Title now takes anything in that array to be an affirmative (user can) + +Should be backwards compatible, but only tested with HEAD, 1.19 and 1.20 + +=== NSFileRepo 1.4 === * Bug Fixes - Thumbnails did not display properly in History (and even in uploads of past files). The problem is fixed with 1.16 (and the current trunk version of NSFileRepo), but in versions of MW before 1.16, archived thumbnails still break because LocalFile.php uses hard-coded class of OldLocalFile instead of <nowiki>$repo->oldFileFactory</nowiki> which would instantiate the correct NSLocalFile class. -* Works with all namespaces > 1000 (used to only work with NS >=11 and <1000 +* Works with all namespaces > 1000 (used to only work with NS >=11 and <1000) * Use NS_IMAGE instead of NS_FILE for backward compatibility @@ -105,14 +112,40 @@ * First fully tested version, works with MW 1.13.1, 1.14.1, 1.15.1 with patches * Works with 1.16.0 (trunk) without patches. +=== Tagged Releases === +There were issues with image thumbnails that require modification to both NSFileRepo and the standard FileRepo. These revisions have been tested for MW Rev 1.15.0 and should also work with 1.14.0. It is doubtful they will work before 1.14.0. If someone could test the 1.14.0 patches, I would be glad to assist in any issues you may have. --[[User:Jpond|jdpond]] 15:02, 13 May 2010 (UTC) + +==== Tagged Release 1.14.0 Phase 3 ==== + +This tagged branch '''has not''' been tested. It corrects the Thumbnail issues but requires: + +* Replacement/patching of LocalFile.php +* Replacement/patching of GlobalFunctions.php +* Alternate branched versions of the core NSFileRepo code. + +If you do not mind messed up thumbnails with problems in the following areas, you can stay with the HEAD version: + +* Visual - displays broken impage link for thumbnails, especially in upload history +* Security - without this patch, it loads thumbnails into 'public' area which could (theoretically) be viewed regardless of NS protection + +SVN available [http://svn.wikimedia.org/svnroot/mediawiki/tags/extensions/NSFileRepo/REL1_14_0/phase3 here] + +==== Tagged Release 1.15.0 Phase 3 ==== + +This tagged branch '''has''' been tested. It corrects the thumbnail issues but requires: + +* Replacement/patching of LocalFile.php +* Replacement/patching of GlobalFunctions.php +* Alternate branched versions of the core NSFileRepo code. + +If you do not mind messed up thumbnails with problems in the following areas, you can stay with the HEAD version: + +* Visual - displays broken impage link for thumbnails, especially in upload history +* Security - without this patch, it loads thumbnails into 'public' area which could (theoretically) be viewed regardless of NS protection + +SVN available [http://svn.wikimedia.org/svnroot/mediawiki/tags/extensions/NSFileRepo/REL1_15_0/phase3 here] + ==Download instructions== - -This Extension and the necessary patch/files may be downloaded from one of the following (SVN preferred). The distribution is the same for all versions of MW, 1.13.0 through Current. - - -* [http://wiki.montcopa.org/PublicDownloads/NSFileRepo_REL_1_4.tar Download tar] (May require eol conversion) -* [http://wiki.montcopa.org/PublicDownloads/NSFileRepo_REL_1_4.zip Download zip] -* [http://svn.wikimedia.org/svnroot/mediawiki/trunk/extensions/NSFileRepo SVN] Copy all files and directories into directory: -- To view, visit https://gerrit.wikimedia.org/r/51393 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I00fad8394ac8fe6061d4b81250c0ae7a42e2b419 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/NSFileRepo Gerrit-Branch: master Gerrit-Owner: Jpond <jack.p...@psitex.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits