Index: trunk/phase3/maintenance/upgrade1_5.php |
— | — | @@ -33,6 +33,8 @@ |
34 | 34 | $this->upgradePage(); |
35 | 35 | $this->upgradeLinks(); |
36 | 36 | $this->upgradeUser(); |
| 37 | + $this->upgradeImage(); |
| 38 | + #$this->upgradeOldImage(); |
37 | 39 | } |
38 | 40 | |
39 | 41 | |
— | — | @@ -544,96 +546,128 @@ |
545 | 547 | $this->dbw->query( "ALTER TABLE $user_temp RENAME TO $user" ); |
546 | 548 | } |
547 | 549 | |
548 | | - |
549 | | - /** |
550 | | - * Truncate a table. |
551 | | - * @param string $table The table name to be truncated |
552 | | - */ |
553 | | - function clearTable( $table ) { |
554 | | - print "Clearing $table...\n"; |
555 | | - $tableName = $this->db->tableName( $table ); |
556 | | - $this->db->query( 'TRUNCATE $tableName' ); |
| 550 | + function upgradeImage() { |
| 551 | + $fname = 'FiveUpgrade::upgradeImage'; |
| 552 | + $chunksize = 100; |
| 553 | + |
| 554 | + extract( $this->dbw->tableNames( 'image', 'image_temp', 'image_old' ) ); |
| 555 | + $this->log( 'Creating temporary image_temp to merge into...' ); |
| 556 | + $this->dbw->query( <<<END |
| 557 | +CREATE TABLE $image_temp ( |
| 558 | + img_name varchar(255) binary NOT NULL default '', |
| 559 | + img_size int(8) unsigned NOT NULL default '0', |
| 560 | + img_width int(5) NOT NULL default '0', |
| 561 | + img_height int(5) NOT NULL default '0', |
| 562 | + img_metadata mediumblob NOT NULL, |
| 563 | + img_bits int(3) NOT NULL default '0', |
| 564 | + img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL, |
| 565 | + img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown", |
| 566 | + img_minor_mime varchar(32) NOT NULL default "unknown", |
| 567 | + img_description tinyblob NOT NULL default '', |
| 568 | + img_user int(5) unsigned NOT NULL default '0', |
| 569 | + img_user_text varchar(255) binary NOT NULL default '', |
| 570 | + img_timestamp char(14) binary NOT NULL default '', |
| 571 | + |
| 572 | + PRIMARY KEY img_name (img_name), |
| 573 | + INDEX img_size (img_size), |
| 574 | + INDEX img_timestamp (img_timestamp) |
| 575 | +) TYPE=InnoDB |
| 576 | +END |
| 577 | + , $fname); |
| 578 | + |
| 579 | + $numimages = $this->dbw->selectField( 'image', 'count(*)', '', $fname ); |
| 580 | + $result = $this->dbr->select( 'image', |
| 581 | + array( |
| 582 | + 'img_name', |
| 583 | + 'img_size', |
| 584 | + 'img_description', |
| 585 | + 'img_user', |
| 586 | + 'img_user_text', |
| 587 | + 'img_timestamp' ), |
| 588 | + '', |
| 589 | + $fname ); |
| 590 | + $add = array(); |
| 591 | + $this->setChunkScale( $chunksize, $numimages, 'image_temp', $fname ); |
| 592 | + while( $row = $this->dbr->fetchObject( $result ) ) { |
| 593 | + // Fill in the new image info fields |
| 594 | + $info = $this->imageInfo( $row->img_name ); |
| 595 | + |
| 596 | + // Update and convert encoding |
| 597 | + $add[] = array( |
| 598 | + 'img_name' => $this->conv( $row->img_name ), |
| 599 | + 'img_size' => $row->img_size, |
| 600 | + 'img_width' => $info['width'], |
| 601 | + 'img_height' => $info['height'], |
| 602 | + 'img_metadata' => "", // loaded on-demand |
| 603 | + 'img_bits' => $info['bits'], |
| 604 | + 'img_media_type' => $info['media'], |
| 605 | + 'img_major_mime' => $info['major'], |
| 606 | + 'img_minor_mime' => $info['minor'], |
| 607 | + 'img_description' => $this->conv( $row->img_description ), |
| 608 | + 'img_user' => $row->img_user, |
| 609 | + 'img_user_text' => $this->conv( $row->img_user_text ), |
| 610 | + 'img_timestamp' => $row->img_timestamp ); |
| 611 | + |
| 612 | + // If doing UTF8 conversion the file must be renamed |
| 613 | + $this->renameFile( $row->img_name, 'wfImageDir' ); |
| 614 | + } |
| 615 | + $this->lastChunk( $add ); |
| 616 | + |
| 617 | + $this->log( 'Renaming image to image_old and image_temp to image...' ); |
| 618 | + $this->dbw->query( "ALTER TABLE $image RENAME TO $image_old" ); |
| 619 | + $this->dbw->query( "ALTER TABLE $image_temp RENAME TO $image" ); |
| 620 | + |
| 621 | + $this->log( 'done with image table.' ); |
557 | 622 | } |
558 | 623 | |
559 | | - /** |
560 | | - * @param string $table Table to be converted |
561 | | - * @param string $key Primary key, to identify fields in the UPDATE. If NULL, all fields will be used to match. |
562 | | - * @param array $fields List of all fields to grab and convert. If null, will assume you want the $key, and will ask for DISTINCT. |
563 | | - * @param array $timestamp A field which should be updated to the current timestamp on changed records. |
564 | | - * @param callable $callback |
565 | | - * @access private |
566 | | - */ |
567 | | - function convertTable( $table, $key, $fields = null, $timestamp = null, $callback = null ) { |
568 | | - $fname = 'FiveUpgrade::convertTable'; |
569 | | - if( $fields ) { |
570 | | - $distinct = ''; |
| 624 | + function imageInfo( $name ) { |
| 625 | + $filename = wfImageDir( $name ) . '/' . $name; |
| 626 | + $info = array( |
| 627 | + 'width' => 0, |
| 628 | + 'height' => 0, |
| 629 | + 'bits' => 0, |
| 630 | + 'media' => '', |
| 631 | + 'major' => '', |
| 632 | + 'minor' => '' ); |
| 633 | + |
| 634 | + $magic =& wfGetMimeMagic(); |
| 635 | + $mime = $magic->guessMimeType( $filename, true ); |
| 636 | + list( $info['major'], $info['minor'] ) = explode( '/', $mime ); |
| 637 | + |
| 638 | + $info['media'] = $magic->getMediaType( $filename, $mime ); |
| 639 | + |
| 640 | + # Height and width |
| 641 | + $gis = false; |
| 642 | + if( $mime == 'image/svg' ) { |
| 643 | + $gis = wfGetSVGsize( $this->imagePath ); |
| 644 | + } elseif( $magic->isPHPImageType( $mime ) ) { |
| 645 | + $gis = getimagesize( $filename ); |
571 | 646 | } else { |
572 | | - # If working on one key only, there will be multiple rows. |
573 | | - # Use DISTINCT to return only one and save us some trouble. |
574 | | - $fields = array( $key ); |
575 | | - $distinct = 'DISTINCT'; |
| 647 | + $this->log( "Surprising mime type: $mime" ); |
576 | 648 | } |
577 | | - $condition = ''; |
578 | | - foreach( $fields as $field ) { |
579 | | - if( $condition ) $condition .= ' OR '; |
580 | | - $condition .= "$field RLIKE '[\x80-\xff]'"; |
| 649 | + if( $gis ) { |
| 650 | + $info['width' ] = $gis[0]; |
| 651 | + $info['height'] = $gis[1]; |
581 | 652 | } |
582 | | - $res = $this->dbw->selectArray( |
583 | | - $table, |
584 | | - array_merge( $fields, array( $key ) ), |
585 | | - $condition, |
586 | | - $fname, |
587 | | - $distinct ); |
588 | | - print "Converting " . $this->dbw->numResults( $res ) . " rows from $table:\n"; |
589 | | - $n = 0; |
590 | | - while( $s = $this->dbw->fetchObject( $res ) ) { |
591 | | - $set = array(); |
592 | | - foreach( $fields as $field ) { |
593 | | - $set[] = $this->toUtf8( $s->$field ); |
594 | | - } |
595 | | - if( $timestamp ) { |
596 | | - $set[$timestamp] = $this->db->timestamp(); |
597 | | - } |
598 | | - if( $key ) { |
599 | | - $keyCond = array( $key, $s->$key ); |
600 | | - } else { |
601 | | - $keyCond = array(); |
602 | | - foreach( $fields as $field ) { |
603 | | - $keyCond[$field] = $s->$field; |
604 | | - } |
605 | | - } |
606 | | - $this->dbw->updateArray( |
607 | | - $table, |
608 | | - $set, |
609 | | - $keyCond, |
610 | | - $fname ); |
611 | | - if( ++$n % 100 == 0 ) echo "$n\n"; |
612 | | - |
613 | | - if( is_callable( $callback ) ) { |
614 | | - call_user_func( $callback, $s ); |
615 | | - } |
| 653 | + if( isset( $gis['bits'] ) ) { |
| 654 | + $info['bits'] = $gis['bits']; |
616 | 655 | } |
617 | | - echo "$n done.\n"; |
618 | | - $this->dbw->freeResult( $res ); |
| 656 | + |
| 657 | + return $info; |
619 | 658 | } |
620 | 659 | |
| 660 | + |
621 | 661 | /** |
622 | | - * @param object $row |
623 | | - * @access private |
| 662 | + * Truncate a table. |
| 663 | + * @param string $table The table name to be truncated |
624 | 664 | */ |
625 | | - function imageRenameCallback( $row ) { |
626 | | - $this->renameFile( $row->img_name, 'wfImageDir' ); |
| 665 | + function clearTable( $table ) { |
| 666 | + print "Clearing $table...\n"; |
| 667 | + $tableName = $this->db->tableName( $table ); |
| 668 | + $this->db->query( 'TRUNCATE $tableName' ); |
627 | 669 | } |
628 | 670 | |
629 | 671 | /** |
630 | | - * @param object $row |
631 | | - * @access private |
632 | | - */ |
633 | | - function oldimageRenameCallback( $row ) { |
634 | | - $this->renameFile( $row->oi_archive_name, 'wfImageArchiveDir' ); |
635 | | - } |
636 | | - |
637 | | - /** |
638 | 672 | * Rename a given image or archived image file to the converted filename, |
639 | 673 | * leaving a symlink for URL compatibility. |
640 | 674 | * |
— | — | @@ -642,7 +676,7 @@ |
643 | 677 | * @access private |
644 | 678 | */ |
645 | 679 | function renameFile( $oldname, $subdirCallback ) { |
646 | | - $newname = $this->toUtf8( $oldname ); |
| 680 | + $newname = $this->conv( $oldname ); |
647 | 681 | if( $newname == $oldname ) { |
648 | 682 | // No need to rename; another field triggered this row. |
649 | 683 | return; |
— | — | @@ -651,20 +685,49 @@ |
652 | 686 | $oldpath = call_user_func( $subdirCallback, $oldname ) . '/' . $oldname; |
653 | 687 | $newpath = call_user_func( $subdirCallback, $newname ) . '/' . $newname; |
654 | 688 | |
655 | | - echo "Renaming $oldpath to $newpath... "; |
| 689 | + $this->log( "$oldpath -> $newpath" ); |
656 | 690 | if( rename( $oldpath, $newpath ) ) { |
657 | | - echo "ok\n"; |
658 | | - echo "Creating compatibility symlink from $newpath to $oldpath... "; |
659 | | - if( symlink( $newpath, $oldpath ) ) { |
660 | | - echo "ok\n"; |
661 | | - } else { |
662 | | - echo " symlink failed!\n"; |
| 691 | + $relpath = $this->relativize( $newpath, dirname( $oldpath ) ); |
| 692 | + if( !symlink( $relpath, $oldpath ) ) { |
| 693 | + $this->log( "... symlink failed!" ); |
663 | 694 | } |
664 | 695 | } else { |
665 | | - echo " rename failed!\n"; |
| 696 | + $this->log( "... rename failed!" ); |
666 | 697 | } |
667 | 698 | } |
668 | 699 | |
| 700 | + /** |
| 701 | + * Generate a relative path name to the given file. |
| 702 | + * Assumes Unix-style paths, separators, and semantics. |
| 703 | + * |
| 704 | + * @param string $path Absolute destination path including target filename |
| 705 | + * @param string $from Absolute source path, directory only |
| 706 | + * @return string |
| 707 | + * @access private |
| 708 | + * @static |
| 709 | + */ |
| 710 | + function relativize( $path, $from ) { |
| 711 | + $pieces = explode( '/', dirname( $path ) ); |
| 712 | + $against = explode( '/', $from ); |
| 713 | + |
| 714 | + // Trim off common prefix |
| 715 | + while( count( $pieces ) && count( $against ) |
| 716 | + && $pieces[0] == $against[0] ) { |
| 717 | + array_shift( $pieces ); |
| 718 | + array_shift( $against ); |
| 719 | + } |
| 720 | + |
| 721 | + // relative dots to bump us to the parent |
| 722 | + while( count( $against ) ) { |
| 723 | + array_unshift( $pieces, '..' ); |
| 724 | + array_shift( $against ); |
| 725 | + } |
| 726 | + |
| 727 | + array_push( $pieces, basename( $path ) ); |
| 728 | + |
| 729 | + return implode( '/', $pieces ); |
| 730 | + } |
| 731 | + |
669 | 732 | |
670 | 733 | } |
671 | 734 | |