$this->upgradePage();
$this->upgradeLinks();
$this->upgradeUser();
+ $this->upgradeImage();
+ #$this->upgradeOldImage();
}
$this->dbw->query( "ALTER TABLE $user_temp RENAME TO $user" );
}
-
- /**
- * Truncate a table.
- * @param string $table The table name to be truncated
- */
- function clearTable( $table ) {
- print "Clearing $table...\n";
- $tableName = $this->db->tableName( $table );
- $this->db->query( 'TRUNCATE $tableName' );
+ function upgradeImage() {
+ $fname = 'FiveUpgrade::upgradeImage';
+ $chunksize = 100;
+
+ extract( $this->dbw->tableNames( 'image', 'image_temp', 'image_old' ) );
+ $this->log( 'Creating temporary image_temp to merge into...' );
+ $this->dbw->query( <<<END
+CREATE TABLE $image_temp (
+ img_name varchar(255) binary NOT NULL default '',
+ img_size int(8) unsigned NOT NULL default '0',
+ img_width int(5) NOT NULL default '0',
+ img_height int(5) NOT NULL default '0',
+ img_metadata mediumblob NOT NULL,
+ img_bits int(3) NOT NULL default '0',
+ img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown",
+ img_minor_mime varchar(32) NOT NULL default "unknown",
+ img_description tinyblob NOT NULL default '',
+ img_user int(5) unsigned NOT NULL default '0',
+ img_user_text varchar(255) binary NOT NULL default '',
+ img_timestamp char(14) binary NOT NULL default '',
+
+ PRIMARY KEY img_name (img_name),
+ INDEX img_size (img_size),
+ INDEX img_timestamp (img_timestamp)
+) TYPE=InnoDB
+END
+ , $fname);
+
+ $numimages = $this->dbw->selectField( 'image', 'count(*)', '', $fname );
+ $result = $this->dbr->select( 'image',
+ array(
+ 'img_name',
+ 'img_size',
+ 'img_description',
+ 'img_user',
+ 'img_user_text',
+ 'img_timestamp' ),
+ '',
+ $fname );
+ $add = array();
+ $this->setChunkScale( $chunksize, $numimages, 'image_temp', $fname );
+ while( $row = $this->dbr->fetchObject( $result ) ) {
+ // Fill in the new image info fields
+ $info = $this->imageInfo( $row->img_name );
+
+ // Update and convert encoding
+ $add[] = array(
+ 'img_name' => $this->conv( $row->img_name ),
+ 'img_size' => $row->img_size,
+ 'img_width' => $info['width'],
+ 'img_height' => $info['height'],
+ 'img_metadata' => "", // loaded on-demand
+ 'img_bits' => $info['bits'],
+ 'img_media_type' => $info['media'],
+ 'img_major_mime' => $info['major'],
+ 'img_minor_mime' => $info['minor'],
+ 'img_description' => $this->conv( $row->img_description ),
+ 'img_user' => $row->img_user,
+ 'img_user_text' => $this->conv( $row->img_user_text ),
+ 'img_timestamp' => $row->img_timestamp );
+
+ // If doing UTF8 conversion the file must be renamed
+ $this->renameFile( $row->img_name, 'wfImageDir' );
+ }
+ $this->lastChunk( $add );
+
+ $this->log( 'Renaming image to image_old and image_temp to image...' );
+ $this->dbw->query( "ALTER TABLE $image RENAME TO $image_old" );
+ $this->dbw->query( "ALTER TABLE $image_temp RENAME TO $image" );
+
+ $this->log( 'done with image table.' );
}
- /**
- * @param string $table Table to be converted
- * @param string $key Primary key, to identify fields in the UPDATE. If NULL, all fields will be used to match.
- * @param array $fields List of all fields to grab and convert. If null, will assume you want the $key, and will ask for DISTINCT.
- * @param array $timestamp A field which should be updated to the current timestamp on changed records.
- * @param callable $callback
- * @access private
- */
- function convertTable( $table, $key, $fields = null, $timestamp = null, $callback = null ) {
- $fname = 'FiveUpgrade::convertTable';
- if( $fields ) {
- $distinct = '';
+ function imageInfo( $name ) {
+ $filename = wfImageDir( $name ) . '/' . $name;
+ $info = array(
+ 'width' => 0,
+ 'height' => 0,
+ 'bits' => 0,
+ 'media' => '',
+ 'major' => '',
+ 'minor' => '' );
+
+ $magic =& wfGetMimeMagic();
+ $mime = $magic->guessMimeType( $filename, true );
+ list( $info['major'], $info['minor'] ) = explode( '/', $mime );
+
+ $info['media'] = $magic->getMediaType( $filename, $mime );
+
+ # Height and width
+ $gis = false;
+ if( $mime == 'image/svg' ) {
+ $gis = wfGetSVGsize( $this->imagePath );
+ } elseif( $magic->isPHPImageType( $mime ) ) {
+ $gis = getimagesize( $filename );
} else {
- # If working on one key only, there will be multiple rows.
- # Use DISTINCT to return only one and save us some trouble.
- $fields = array( $key );
- $distinct = 'DISTINCT';
+ $this->log( "Surprising mime type: $mime" );
}
- $condition = '';
- foreach( $fields as $field ) {
- if( $condition ) $condition .= ' OR ';
- $condition .= "$field RLIKE '[\x80-\xff]'";
+ if( $gis ) {
+ $info['width' ] = $gis[0];
+ $info['height'] = $gis[1];
}
- $res = $this->dbw->selectArray(
- $table,
- array_merge( $fields, array( $key ) ),
- $condition,
- $fname,
- $distinct );
- print "Converting " . $this->dbw->numResults( $res ) . " rows from $table:\n";
- $n = 0;
- while( $s = $this->dbw->fetchObject( $res ) ) {
- $set = array();
- foreach( $fields as $field ) {
- $set[] = $this->toUtf8( $s->$field );
- }
- if( $timestamp ) {
- $set[$timestamp] = $this->db->timestamp();
- }
- if( $key ) {
- $keyCond = array( $key, $s->$key );
- } else {
- $keyCond = array();
- foreach( $fields as $field ) {
- $keyCond[$field] = $s->$field;
- }
- }
- $this->dbw->updateArray(
- $table,
- $set,
- $keyCond,
- $fname );
- if( ++$n % 100 == 0 ) echo "$n\n";
-
- if( is_callable( $callback ) ) {
- call_user_func( $callback, $s );
- }
+ if( isset( $gis['bits'] ) ) {
+ $info['bits'] = $gis['bits'];
}
- echo "$n done.\n";
- $this->dbw->freeResult( $res );
- }
-
- /**
- * @param object $row
- * @access private
- */
- function imageRenameCallback( $row ) {
- $this->renameFile( $row->img_name, 'wfImageDir' );
+
+ return $info;
}
+
/**
- * @param object $row
- * @access private
+ * Truncate a table.
+ * @param string $table The table name to be truncated
*/
- function oldimageRenameCallback( $row ) {
- $this->renameFile( $row->oi_archive_name, 'wfImageArchiveDir' );
+ function clearTable( $table ) {
+ print "Clearing $table...\n";
+ $tableName = $this->db->tableName( $table );
+ $this->db->query( 'TRUNCATE $tableName' );
}
/**
* @access private
*/
function renameFile( $oldname, $subdirCallback ) {
- $newname = $this->toUtf8( $oldname );
+ $newname = $this->conv( $oldname );
if( $newname == $oldname ) {
// No need to rename; another field triggered this row.
return;
$oldpath = call_user_func( $subdirCallback, $oldname ) . '/' . $oldname;
$newpath = call_user_func( $subdirCallback, $newname ) . '/' . $newname;
- echo "Renaming $oldpath to $newpath... ";
+ $this->log( "$oldpath -> $newpath" );
if( rename( $oldpath, $newpath ) ) {
- echo "ok\n";
- echo "Creating compatibility symlink from $newpath to $oldpath... ";
- if( symlink( $newpath, $oldpath ) ) {
- echo "ok\n";
- } else {
- echo " symlink failed!\n";
+ $relpath = $this->relativize( $newpath, dirname( $oldpath ) );
+ if( !symlink( $relpath, $oldpath ) ) {
+ $this->log( "... symlink failed!" );
}
} else {
- echo " rename failed!\n";
+ $this->log( "... rename failed!" );
+ }
+ }
+
+ /**
+ * Generate a relative path name to the given file.
+ * Assumes Unix-style paths, separators, and semantics.
+ *
+ * @param string $path Absolute destination path including target filename
+ * @param string $from Absolute source path, directory only
+ * @return string
+ * @access private
+ * @static
+ */
+ function relativize( $path, $from ) {
+ $pieces = explode( '/', dirname( $path ) );
+ $against = explode( '/', $from );
+
+ // Trim off common prefix
+ while( count( $pieces ) && count( $against )
+ && $pieces[0] == $against[0] ) {
+ array_shift( $pieces );
+ array_shift( $against );
}
+
+ // relative dots to bump us to the parent
+ while( count( $against ) ) {
+ array_unshift( $pieces, '..' );
+ array_shift( $against );
+ }
+
+ array_push( $pieces, basename( $path ) );
+
+ return implode( '/', $pieces );
}