MediaWiki r34169 - Code Review

Jump to: navigation, search
Repository:MediaWiki
Revision:r34168‎ | r34169 (on ViewVC)‎ | r34170 >
Date:13:09, 3 May 2008
Author:vasilievvv
Status:old
Tags:
Comment:
* (bug 709) Cannot rename/move images and other media files.
Currently in experimental mode, use $wgAllowImageMoving to enable it.
Known issues:
* Doesn't work with rev_deleted
* May also have some security and caching issues.
Modified paths:

Diff [purge]

Index: trunk/phase3/includes/filerepo/LocalFile.php
@@ -910,6 +910,39 @@
911911 /** wasDeleted inherited */
912912
913913 /**
 914+ * Move file to the new title
 915+ *
 916+ * Move current, old version and all thumbnails
 917+ * to the new filename. Old file is deleted.
 918+ *
 919+ * Cache purging is done; checks for validity
 920+ * and logging are caller's responsibility
 921+ *
 922+ * @param $target Title New file name
 923+ * @return FileRepoStatus object.
 924+ */
 925+ function move( $target ) {
 926+ $this->lock();
 927+ $dbw = $this->repo->getMasterDB();
 928+ $batch = new LocalFileMoveBatch( $this, $target, $dbw );
 929+ $batch->addCurrent();
 930+ $batch->addOlds();
 931+ if( !$this->repo->canTransformVia404() ) {
 932+ $batch->addThumbs();
 933+ }
 934+
 935+ $status = $batch->execute();
 936+ $this->purgeEverything();
 937+ $this->unlock();
 938+
 939+ // Now switch the object and repurge
 940+ $this->title = $target;
 941+ unset( $this->name );
 942+ $this->purgeEverything();
 943+ return $status;
 944+ }
 945+
 946+ /**
914947 * Delete all versions of the file.
915948 *
916949 * Moves the files into an archive directory (or deletes them)
@@ -1606,3 +1639,160 @@
16071640 return $status;
16081641 }
16091642 }
 1643+
 1644+#------------------------------------------------------------------------------
 1645+
 1646+/**
 1647+ * Helper class for file movement
 1648+ */
 1649+class LocalFileMoveBatch {
 1650+ var $file, $cur, $olds, $archive, $thumbs, $target, $db;
 1651+
 1652+ function __construct( File $file, Title $target, Database $db ) {
 1653+ $this->file = $file;
 1654+ $this->target = $target;
 1655+ $this->oldHash = $this->file->repo->getHashPath( $this->file->getName() );
 1656+ $this->newHash = $this->file->repo->getHashPath( $this->target->getDbKey() );
 1657+ $this->oldName = $this->file->getName();
 1658+ $this->newName = $this->file->repo->getNameFromTitle( $this->target );
 1659+ $this->oldRel = $this->oldHash . $this->oldName;
 1660+ $this->newRel = $this->newHash . $this->newName;
 1661+ $this->db = $db;
 1662+ }
 1663+
 1664+ function addCurrent() {
 1665+ $this->cur = array( $this->oldRel, $this->newRel );
 1666+ }
 1667+
 1668+ function addThumbs() {
 1669+ $this->thumbs = array();
 1670+ $repo = $this->file->repo;
 1671+ $thumbDirRel = 'thumb/' . $this->oldRel;
 1672+ $thumbDir = $repo->getZonePath( 'public' ) . '/' . $thumbDirRel;
 1673+ $newThumbDirRel = 'thumb/' . $this->newRel;
 1674+ if( !is_dir( $thumbDir ) || !is_readable( $thumbDir ) ) {
 1675+ $this->thumbs = array();
 1676+ return;
 1677+ } else {
 1678+ $files = scandir( $thumbDir );
 1679+ foreach( $files as $file ) {
 1680+ if( $file == '.' || $file == '..' ) continue;
 1681+ if( preg_match( '/^(\d+)px-/', $file, $matches ) ) {
 1682+ list( $unused, $width ) = $matches;
 1683+ $this->thumbs[] = array(
 1684+ $thumbDirRel . '/' . $file,
 1685+ $newThumbDirRel . '/' . $width . 'px-' . $this->newName
 1686+ );
 1687+ } else {
 1688+ wfDebug( 'Strange file in thumbnail directory: ' . $thumbDirRel . '/' . $file );
 1689+ }
 1690+ }
 1691+ }
 1692+ }
 1693+
 1694+ function addOlds() {
 1695+ $archiveBase = 'archive';
 1696+ $this->olds = array();
 1697+
 1698+ $result = $this->db->select( 'oldimage',
 1699+ array( 'oi_archive_name' ),
 1700+ array( 'oi_name' => $this->oldName ),
 1701+ __METHOD__
 1702+ );
 1703+ while( $row = $this->db->fetchObject( $result ) ) {
 1704+ $oldname = $row->oi_archive_name;
 1705+ $bits = explode( '!', $oldname, 2 );
 1706+ if( count( $bits ) != 2 ) {
 1707+ wfDebug( 'Invalid old file name: ' . $oldname );
 1708+ continue;
 1709+ }
 1710+ list( $timestamp, $filename ) = $bits;
 1711+ if( $this->oldName != $filename ) {
 1712+ wfDebug( 'Invalid old file name:' . $oldName );
 1713+ continue;
 1714+ }
 1715+ $this->olds[] = array(
 1716+ "{$archiveBase}/{$this->oldHash}{$oldname}",
 1717+ "{$archiveBase}/{$this->oldHash}{$timestamp}!{$this->newName}"
 1718+ );
 1719+ }
 1720+ $this->db->freeResult( $result );
 1721+ }
 1722+
 1723+ function execute() {
 1724+ $repo = $this->file->repo;
 1725+ $status = $repo->newGood();
 1726+ $triplets = $this->getMoveTriplets();
 1727+
 1728+ $statusDb = $this->doDBUpdates();
 1729+ $statusMove = $repo->storeBatch( $triplets, FSRepo::DELETE_SOURCE );
 1730+ if( !$statusMove->isOk() ) {
 1731+ $this->db->rollback();
 1732+ }
 1733+ $status->merge( $statusDb );
 1734+ $status->merge( $statusMove );
 1735+ return $status;
 1736+ }
 1737+
 1738+ function doDBUpdates() {
 1739+ $repo = $this->file->repo;
 1740+ $status = $repo->newGood();
 1741+ $dbw = $this->db;
 1742+
 1743+ // Update current image
 1744+ $dbw->update(
 1745+ 'image',
 1746+ array( 'img_name' => $this->newName ),
 1747+ array( 'img_name' => $this->oldName ),
 1748+ __METHOD__
 1749+ );
 1750+ if( $dbw->affectedRows() ) {
 1751+ $status->successCount++;
 1752+ } else {
 1753+ $status->failCount++;
 1754+ }
 1755+
 1756+ // Update old images
 1757+ $dbw->update(
 1758+ 'oldimage',
 1759+ array(
 1760+ 'oi_name' => $this->newName,
 1761+ 'oi_archive_name = ' . $dbw->strreplace( 'oi_archive_name', $dbw->addQuotes($this->oldName), $dbw->addQuotes($this->newName) ),
 1762+ ),
 1763+ array( 'oi_name' => $this->oldName ),
 1764+ __METHOD__
 1765+ );
 1766+ $affected = $dbw->affectedRows();
 1767+ $total = count( $this->olds );
 1768+ $status->successCount += $affected;
 1769+ $status->failCount += $total - $affected;
 1770+
 1771+ // Update deleted images
 1772+ $dbw->update(
 1773+ 'filearchive',
 1774+ array(
 1775+ 'fa_name' => $this->newName,
 1776+ 'fa_archive_name = ' . $dbw->strreplace( 'fa_archive_name', $dbw->addQuotes($this->oldName), $dbw->addQuotes($this->newName) ),
 1777+ ),
 1778+ array( 'fa_name' => $this->oldName ),
 1779+ __METHOD__
 1780+ );
 1781+ $affected = $dbw->affectedRows();
 1782+ $total = count( $this->olds );
 1783+ $status->successCount += $affected;
 1784+ $status->failCount += $total - $affected;
 1785+
 1786+ return $status;
 1787+ }
 1788+
 1789+ // Generates triplets for FSRepo::storeBatch()
 1790+ function getMoveTriplets() {
 1791+ $moves = array_merge( array( $this->cur ), $this->olds, $this->thumbs );
 1792+ $triplets = array(); // The format is: (srcUrl,destZone,desrUrl)
 1793+ foreach( $moves as $move ) {
 1794+ $srcUrl = $this->file->repo->getVirtualUrl() . '/public/' . rawurlencode( $move[0] );
 1795+ $triplets[] = array( $srcUrl, 'public', $move[1] );
 1796+ }
 1797+ return $triplets;
 1798+ }
 1799+}
Index: trunk/phase3/includes/filerepo/File.php
@@ -90,6 +90,21 @@
9191 }
9292
9393 /**
 94+ * Checks if file extensions are compatible
 95+ *
 96+ * @param $old File Old file
 97+ * @param $new string New name
 98+ */
 99+ static function checkExtesnionCompatibility( File $old, $new ) {
 100+ $oldMime = $old->getMimeType();
 101+ $n = strrpos( $new, '.' );
 102+ $newExt = self::normalizeExtension(
 103+ $n ? substr( $new, $n + 1 ) : '' );
 104+ $mimeMagic = MimeMagic::singleton();
 105+ return $mimeMagic->isMatchingExtension( $newExt, $oldMime );
 106+ }
 107+
 108+ /**
94109 * Upgrade the database row if there is one
95110 * Called by ImagePage
96111 * STUB
@@ -917,6 +932,22 @@
918933 }
919934
920935 /**
 936+ * Move file to the new title
 937+ *
 938+ * Move current, old version and all thumbnails
 939+ * to the new filename. Old file is deleted.
 940+ *
 941+ * Cache purging is done; checks for validity
 942+ * and logging are caller's responsibility
 943+ *
 944+ * @param $target Title New file name
 945+ * @return FileRepoStatus object.
 946+ */
 947+ function move( $target ) {
 948+ $this->readOnlyError();
 949+ }
 950+
 951+ /**
921952 * Delete all versions of the file.
922953 *
923954 * Moves the files into an archive directory (or deletes them)
Index: trunk/phase3/includes/Title.php
@@ -2388,6 +2388,19 @@
23892389 return 'badarticleerror';
23902390 }
23912391
 2392+ // Image-specific checks
 2393+ if( $this->getNamespace() == NS_IMAGE ) {
 2394+ $file = wfLocalFile( $this );
 2395+ if( $file->exists() ) {
 2396+ if( $nt->getNamespace() != NS_IMAGE ) {
 2397+ return 'imagenocrossnamespace';
 2398+ }
 2399+ if( !File::checkExtesnionCompatibility( $file, $nt->getDbKey() ) ) {
 2400+ return 'imagetypemismatch';
 2401+ }
 2402+ }
 2403+ }
 2404+
23922405 if ( $auth ) {
23932406 global $wgUser;
23942407 $errors = array_merge($this->getUserPermissionsErrors('move', $wgUser),
@@ -2439,12 +2452,15 @@
24402453
24412454 $pageid = $this->getArticleID();
24422455 if( $nt->exists() ) {
2443 - $this->moveOverExistingRedirect( $nt, $reason, $createRedirect );
 2456+ $err = $this->moveOverExistingRedirect( $nt, $reason, $createRedirect );
24442457 $pageCountChange = ($createRedirect ? 0 : -1);
24452458 } else { # Target didn't exist, do normal move.
2446 - $this->moveToNewTitle( $nt, $reason, $createRedirect );
 2459+ $err = $this->moveToNewTitle( $nt, $reason, $createRedirect );
24472460 $pageCountChange = ($createRedirect ? 1 : 0);
24482461 }
 2462+ if( is_string( $err ) ) {
 2463+ return $err;
 2464+ }
24492465 $redirid = $this->getArticleID();
24502466
24512467 // Category memberships include a sort key which may be customized.
@@ -2541,6 +2557,17 @@
25422558 $oldid = $this->getArticleID();
25432559 $dbw = wfGetDB( DB_MASTER );
25442560
 2561+ # Move an image if it is
 2562+ if( $this->getNamespace() == NS_IMAGE ) {
 2563+ $file = wfLocalFile( $this );
 2564+ if( $file->exists() ) {
 2565+ $status = $file->move( $nt );
 2566+ if( !$status->isOk() ) {
 2567+ return $status->getWikiText();
 2568+ }
 2569+ }
 2570+ }
 2571+
25452572 # Delete the old redirect. We don't save it to history since
25462573 # by definition if we've got here it's rather uninteresting.
25472574 # We have to remove it so that the next step doesn't trigger
@@ -2636,6 +2663,17 @@
26372664 $dbw = wfGetDB( DB_MASTER );
26382665 $now = $dbw->timestamp();
26392666
 2667+ # Move an image if it is
 2668+ if( $this->getNamespace() == NS_IMAGE ) {
 2669+ $file = wfLocalFile( $this );
 2670+ if( $file->exists() ) {
 2671+ $status = $file->move( $nt );
 2672+ if( !$status->isOk() ) {
 2673+ return $status->getWikiText();
 2674+ }
 2675+ }
 2676+ }
 2677+
26402678 # Save a null revision in the page's history notifying of the move
26412679 $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true );
26422680 $nullRevId = $nullRevision->insertOn( $dbw );
@@ -2701,6 +2739,15 @@
27022740 $fname = 'Title::isValidMoveTarget';
27032741 $dbw = wfGetDB( DB_MASTER );
27042742
 2743+ # Is it an existsing file?
 2744+ if( $nt->getNamespace() == NS_IMAGE ) {
 2745+ $file = wfLocalFile( $nt );
 2746+ if( $file->exists() ) {
 2747+ wfDebug( __METHOD__ . ": file exists\n" );
 2748+ return false;
 2749+ }
 2750+ }
 2751+
27052752 # Is it a redirect?
27062753 $id = $nt->getArticleID();
27072754 $obj = $dbw->selectRow( array( 'page', 'revision', 'text'),
Index: trunk/phase3/includes/Namespace.php
@@ -51,7 +51,8 @@
5252 * @return bool
5353 */
5454 public static function isMovable( $index ) {
55 - return !( $index < NS_MAIN || $index == NS_IMAGE || $index == NS_CATEGORY );
 55+ global $wgAllowImageMoving;
 56+ return !( $index < NS_MAIN || ($index == NS_IMAGE && !$wgAllowImageMoving) || $index == NS_CATEGORY );
5657 }
5758
5859 /**
Index: trunk/phase3/includes/DefaultSettings.php
@@ -1526,6 +1526,9 @@
15271527 */
15281528 $wgAllowExternalImagesFrom = '';
15291529
 1530+/** Allows to move images and other media files. Experemintal, not sure if it always works */
 1531+$wgAllowImageMoving = false;
 1532+
15301533 /** Disable database-intensive features */
15311534 $wgMiserMode = false;
15321535 /** Disable all query pages if miser mode is on, not just some */
Index: trunk/phase3/includes/Database.php
@@ -1656,6 +1656,18 @@
16571657 }
16581658
16591659 /**
 1660+ * Returns a comand for str_replace function in SQL query.
 1661+ * Uses REPLACE() in MySQL
 1662+ *
 1663+ * @param string $orig String or column to modify
 1664+ * @param string $old String or column to seek
 1665+ * @param string $new String or column to replace with
 1666+ */
 1667+ function strreplace( $orig, $old, $new ) {
 1668+ return "REPLACE({$orig}, {$old}, {$new})";
 1669+ }
 1670+
 1671+ /**
16601672 * Determines if the last failure was due to a deadlock
16611673 */
16621674 function wasDeadlock() {
Index: trunk/phase3/RELEASE-NOTES
@@ -96,6 +96,7 @@
9797 and local one
9898 * Update documentation links in auto-generated LocalSettings.php
9999 * (bug 13584) The new hook SkinTemplateToolboxEnd was added.
 100+* (bug 709) Cannot rename/move images and other media files [EXPERIMENTAL]
100101
101102 === Bug fixes in 1.13 ===
102103

Status & tagging log

  • 15:26, 12 September 2011 Meno25 (talk | contribs) changed the status of r34169 [removed: ok added: old]