Index: branches/filerepo-work/phase3/maintenance/importImages.php |
— | — | @@ -42,56 +42,40 @@ |
43 | 43 | $license = isset( $options['license'] ) ? $options['license'] : ''; |
44 | 44 | |
45 | 45 | # Batch "upload" operation |
| 46 | + global $wgUploadDirectory; |
46 | 47 | foreach( $files as $file ) { |
47 | | - |
48 | 48 | $base = wfBaseName( $file ); |
49 | 49 | |
50 | 50 | # Validate a title |
51 | 51 | $title = Title::makeTitleSafe( NS_IMAGE, $base ); |
52 | | - if( is_object( $title ) ) { |
| 52 | + if( !is_object( $title ) ) { |
| 53 | + echo( "{$base} could not be imported; a valid title cannot be produced\n" ); |
| 54 | + continue; |
| 55 | + } |
53 | 56 | |
54 | | - # Check existence |
55 | | - $image = new Image( $title ); |
56 | | - if( !$image->exists() ) { |
| 57 | + # Check existence |
| 58 | + $image = wfLocalFile( $title ); |
| 59 | + if( $image->exists() ) { |
| 60 | + echo( "{$base} could not be imported; a file with this name exists in the wiki\n" ); |
| 61 | + continue; |
| 62 | + } |
57 | 63 | |
58 | | - global $wgUploadDirectory; |
| 64 | + # Stash the file |
| 65 | + echo( "Saving {$base}..." ); |
59 | 66 | |
60 | | - # copy() doesn't create paths so if the hash path doesn't exist, we |
61 | | - # have to create it |
62 | | - makeHashPath( wfGetHashPath( $image->name ) ); |
| 67 | + $archive = $image->publish( $file ); |
| 68 | + if ( WikiError::isError( $archive ) ) { |
| 69 | + echo( "failed.\n" ); |
| 70 | + continue; |
| 71 | + } |
| 72 | + echo( "importing..." ); |
63 | 73 | |
64 | | - # Stash the file |
65 | | - echo( "Saving {$base}..." ); |
66 | | - |
67 | | - if( copy( $file, $image->getFullPath() ) ) { |
68 | | - |
69 | | - echo( "importing..." ); |
70 | | - |
71 | | - # Grab the metadata |
72 | | - $image->loadFromFile(); |
73 | | - |
74 | | - # Record the upload |
75 | | - if( $image->recordUpload( '', $comment, $license ) ) { |
76 | | - |
77 | | - # We're done! |
78 | | - echo( "done.\n" ); |
79 | | - |
80 | | - } else { |
81 | | - echo( "failed.\n" ); |
82 | | - } |
83 | | - |
84 | | - } else { |
85 | | - echo( "failed.\n" ); |
86 | | - } |
87 | | - |
88 | | - } else { |
89 | | - echo( "{$base} could not be imported; a file with this name exists in the wiki\n" ); |
90 | | - } |
91 | | - |
| 74 | + if ( $image->recordUpload( $archive, $comment, $license ) ) { |
| 75 | + # We're done! |
| 76 | + echo( "done.\n" ); |
92 | 77 | } else { |
93 | | - echo( "{$base} could not be imported; a valid title cannot be produced\n" ); |
| 78 | + echo( "failed.\n" ); |
94 | 79 | } |
95 | | - |
96 | 80 | } |
97 | 81 | |
98 | 82 | } else { |
Index: branches/filerepo-work/phase3/maintenance/importImages.inc.php |
— | — | @@ -47,20 +47,4 @@ |
48 | 48 | return array( $fname, $ext ); |
49 | 49 | } |
50 | 50 | |
51 | | -/** |
52 | | - * Given an image hash, check that the structure exists to save the image file |
53 | | - * and create it if it doesn't |
54 | | - * |
55 | | - * @param $hash Part of an image hash, e.g. /f/fd/ |
56 | | - */ |
57 | | -function makeHashPath( $hash ) { |
58 | | - global $wgUploadDirectory; |
59 | | - $parts = explode( '/', substr( $hash, 1, strlen( $hash ) - 2 ) ); |
60 | | - if( !is_dir( $wgUploadDirectory . '/' . $parts[0] ) ) |
61 | | - mkdir( $wgUploadDirectory . '/' . $parts[0] ); |
62 | | - if( !is_dir( $wgUploadDirectory . '/' . $hash ) ) |
63 | | - mkdir( $wgUploadDirectory . '/' . $hash ); |
64 | | -} |
65 | | - |
66 | | - |
67 | | -?> |
\ No newline at end of file |
| 51 | +?> |
Index: branches/filerepo-work/phase3/maintenance/cleanupImages.php |
— | — | @@ -89,7 +89,10 @@ |
90 | 90 | } |
91 | 91 | |
92 | 92 | function filePath( $name ) { |
93 | | - return wfImageDir( $name ) . "/$name"; |
| 93 | + if ( !isset( $this->repo ) ) { |
| 94 | + $this->repo = RepoGroup::singleton()->getLocalRepo(); |
| 95 | + } |
| 96 | + return $this->repo->getRootDirectory() . '/' . $this->repo->getHashPath( $name ) . $name; |
94 | 97 | } |
95 | 98 | |
96 | 99 | function pokeFile( $orig, $new ) { |
Index: branches/filerepo-work/phase3/maintenance/FiveUpgrade.inc |
— | — | @@ -693,10 +693,7 @@ |
694 | 694 | return $copy; |
695 | 695 | } |
696 | 696 | |
697 | | - function imageInfo( $name, $subdirCallback='wfImageDir', $basename = null ) { |
698 | | - if( is_null( $basename ) ) $basename = $name; |
699 | | - $dir = call_user_func( $subdirCallback, $basename ); |
700 | | - $filename = $dir . '/' . $name; |
| 697 | + function imageInfo( $filename ) { |
701 | 698 | $info = array( |
702 | 699 | 'width' => 0, |
703 | 700 | 'height' => 0, |
— | — | @@ -711,20 +708,13 @@ |
712 | 709 | |
713 | 710 | $info['media'] = $magic->getMediaType( $filename, $mime ); |
714 | 711 | |
715 | | - # Height and width |
716 | | - $gis = false; |
717 | | - if( $mime == 'image/svg' ) { |
718 | | - $gis = wfGetSVGsize( $filename ); |
719 | | - } elseif( $magic->isPHPImageType( $mime ) ) { |
720 | | - $gis = getimagesize( $filename ); |
721 | | - } else { |
722 | | - $this->log( "Surprising mime type: $mime" ); |
723 | | - } |
724 | | - if( $gis ) { |
725 | | - $info['width' ] = $gis[0]; |
726 | | - $info['height'] = $gis[1]; |
727 | | - } |
728 | | - if( isset( $gis['bits'] ) ) { |
| 712 | + $image = UnregisteredLocalFile::newFromPath( $filename, $mime ); |
| 713 | + |
| 714 | + $info['width'] = $image->getWidth(); |
| 715 | + $info['height'] = $image->getHeight(); |
| 716 | + |
| 717 | + $gis = $image->getImageSize(); |
| 718 | + if ( isset( $gis['bits'] ) ) { |
729 | 719 | $info['bits'] = $gis['bits']; |
730 | 720 | } |
731 | 721 | |
— | — | @@ -896,7 +886,7 @@ |
897 | 887 | } |
898 | 888 | |
899 | 889 | function upgradeLogging() { |
900 | | - $tabledef = <<<END |
| 890 | + $tabledef = <<<ENDS |
901 | 891 | CREATE TABLE $1 ( |
902 | 892 | -- Symbolic keys for the general log type and the action type |
903 | 893 | -- within the log. The output format will be controlled by the |
— | — | @@ -926,7 +916,7 @@ |
927 | 917 | KEY page_time (log_namespace, log_title, log_timestamp) |
928 | 918 | |
929 | 919 | ) TYPE=InnoDB |
930 | | -END; |
| 920 | +ENDS; |
931 | 921 | $fields = array( |
932 | 922 | 'log_type' => MW_UPGRADE_COPY, |
933 | 923 | 'log_action' => MW_UPGRADE_COPY, |
— | — | @@ -940,7 +930,7 @@ |
941 | 931 | } |
942 | 932 | |
943 | 933 | function upgradeArchive() { |
944 | | - $tabledef = <<<END |
| 934 | + $tabledef = <<<ENDS |
945 | 935 | CREATE TABLE $1 ( |
946 | 936 | ar_namespace int NOT NULL default '0', |
947 | 937 | ar_title varchar(255) binary NOT NULL default '', |
— | — | @@ -960,7 +950,7 @@ |
961 | 951 | KEY name_title_timestamp (ar_namespace,ar_title,ar_timestamp) |
962 | 952 | |
963 | 953 | ) TYPE=InnoDB |
964 | | -END; |
| 954 | +ENDS; |
965 | 955 | $fields = array( |
966 | 956 | 'ar_namespace' => MW_UPGRADE_COPY, |
967 | 957 | 'ar_title' => MW_UPGRADE_ENCODE, |
— | — | @@ -979,7 +969,7 @@ |
980 | 970 | function upgradeImagelinks() { |
981 | 971 | global $wgUseLatin1; |
982 | 972 | if( $wgUseLatin1 ) { |
983 | | - $tabledef = <<<END |
| 973 | + $tabledef = <<<ENDS |
984 | 974 | CREATE TABLE $1 ( |
985 | 975 | -- Key to page_id of the page containing the image / media link. |
986 | 976 | il_from int(8) unsigned NOT NULL default '0', |
— | — | @@ -993,7 +983,7 @@ |
994 | 984 | KEY (il_to) |
995 | 985 | |
996 | 986 | ) TYPE=InnoDB |
997 | | -END; |
| 987 | +ENDS; |
998 | 988 | $fields = array( |
999 | 989 | 'il_from' => MW_UPGRADE_COPY, |
1000 | 990 | 'il_to' => MW_UPGRADE_ENCODE ); |
— | — | @@ -1004,7 +994,7 @@ |
1005 | 995 | function upgradeCategorylinks() { |
1006 | 996 | global $wgUseLatin1; |
1007 | 997 | if( $wgUseLatin1 ) { |
1008 | | - $tabledef = <<<END |
| 998 | + $tabledef = <<<ENDS |
1009 | 999 | CREATE TABLE $1 ( |
1010 | 1000 | cl_from int(8) unsigned NOT NULL default '0', |
1011 | 1001 | cl_to varchar(255) binary NOT NULL default '', |
— | — | @@ -1015,7 +1005,7 @@ |
1016 | 1006 | KEY cl_sortkey(cl_to,cl_sortkey), |
1017 | 1007 | KEY cl_timestamp(cl_to,cl_timestamp) |
1018 | 1008 | ) TYPE=InnoDB |
1019 | | -END; |
| 1009 | +ENDS; |
1020 | 1010 | $fields = array( |
1021 | 1011 | 'cl_from' => MW_UPGRADE_COPY, |
1022 | 1012 | 'cl_to' => MW_UPGRADE_ENCODE, |
— | — | @@ -1028,7 +1018,7 @@ |
1029 | 1019 | function upgradeIpblocks() { |
1030 | 1020 | global $wgUseLatin1; |
1031 | 1021 | if( $wgUseLatin1 ) { |
1032 | | - $tabledef = <<<END |
| 1022 | + $tabledef = <<<ENDS |
1033 | 1023 | CREATE TABLE $1 ( |
1034 | 1024 | ipb_id int(8) NOT NULL auto_increment, |
1035 | 1025 | ipb_address varchar(40) binary NOT NULL default '', |
— | — | @@ -1044,7 +1034,7 @@ |
1045 | 1035 | INDEX ipb_user (ipb_user) |
1046 | 1036 | |
1047 | 1037 | ) TYPE=InnoDB |
1048 | | -END; |
| 1038 | +ENDS; |
1049 | 1039 | $fields = array( |
1050 | 1040 | 'ipb_id' => MW_UPGRADE_COPY, |
1051 | 1041 | 'ipb_address' => MW_UPGRADE_COPY, |
— | — | @@ -1060,7 +1050,7 @@ |
1061 | 1051 | |
1062 | 1052 | function upgradeRecentchanges() { |
1063 | 1053 | // There's a format change in the namespace field |
1064 | | - $tabledef = <<<END |
| 1054 | + $tabledef = <<<ENDS |
1065 | 1055 | CREATE TABLE $1 ( |
1066 | 1056 | rc_id int(8) NOT NULL auto_increment, |
1067 | 1057 | rc_timestamp varchar(14) binary NOT NULL default '', |
— | — | @@ -1098,7 +1088,7 @@ |
1099 | 1089 | INDEX rc_ip (rc_ip) |
1100 | 1090 | |
1101 | 1091 | ) TYPE=InnoDB |
1102 | | -END; |
| 1092 | +ENDS; |
1103 | 1093 | $fields = array( |
1104 | 1094 | 'rc_id' => MW_UPGRADE_COPY, |
1105 | 1095 | 'rc_timestamp' => MW_UPGRADE_COPY, |
— | — | @@ -1124,7 +1114,7 @@ |
1125 | 1115 | |
1126 | 1116 | function upgradeQuerycache() { |
1127 | 1117 | // There's a format change in the namespace field |
1128 | | - $tabledef = <<<END |
| 1118 | + $tabledef = <<<ENDS |
1129 | 1119 | CREATE TABLE $1 ( |
1130 | 1120 | -- A key name, generally the base name of of the special page. |
1131 | 1121 | qc_type char(32) NOT NULL, |
— | — | @@ -1139,7 +1129,7 @@ |
1140 | 1130 | KEY (qc_type,qc_value) |
1141 | 1131 | |
1142 | 1132 | ) TYPE=InnoDB |
1143 | | -END; |
| 1133 | +ENDS; |
1144 | 1134 | $fields = array( |
1145 | 1135 | 'qc_type' => MW_UPGRADE_COPY, |
1146 | 1136 | 'qc_value' => MW_UPGRADE_COPY, |
Index: branches/filerepo-work/phase3/maintenance/rebuildImages.php |
— | — | @@ -40,8 +40,18 @@ |
41 | 41 | |
42 | 42 | $this->maxLag = 10; # if slaves are lagged more than 10 secs, wait |
43 | 43 | $this->dryrun = $dryrun; |
| 44 | + if ( $dryrun ) { |
| 45 | + $GLOBALS['wgReadOnly'] = 'Dry run mode, image upgrades are suppressed'; |
| 46 | + } |
44 | 47 | } |
45 | 48 | |
| 49 | + function getRepo() { |
| 50 | + if ( !isset( $this->repo ) ) { |
| 51 | + $this->repo = RepoGroup::singleton()->getLocalRepo(); |
| 52 | + } |
| 53 | + return $this->repo; |
| 54 | + } |
| 55 | + |
46 | 56 | function build() { |
47 | 57 | $this->buildImage(); |
48 | 58 | $this->buildOldImage(); |
— | — | @@ -94,13 +104,7 @@ |
95 | 105 | |
96 | 106 | while( $row = $this->dbr->fetchObject( $result ) ) { |
97 | 107 | $update = call_user_func( $callback, $row ); |
98 | | - if( is_array( $update ) ) { |
99 | | - if( !$this->dryrun ) { |
100 | | - $this->dbw->update( $table, |
101 | | - $update, |
102 | | - array( $key => $row->$key ), |
103 | | - $fname ); |
104 | | - } |
| 108 | + if( $update ) { |
105 | 109 | $this->progress( 1 ); |
106 | 110 | } else { |
107 | 111 | $this->progress( 0 ); |
— | — | @@ -116,97 +120,43 @@ |
117 | 121 | } |
118 | 122 | |
119 | 123 | function imageCallback( $row ) { |
120 | | - if( $row->img_width ) { |
121 | | - // Already processed |
122 | | - return null; |
123 | | - } |
124 | | - |
125 | | - // Fill in the new image info fields |
126 | | - $info = $this->imageInfo( $row->img_name ); |
127 | | - |
128 | | - global $wgMemc; |
129 | | - $key = wfMemcKey( "Image", md5( $row->img_name ) ); |
130 | | - $wgMemc->delete( $key ); |
131 | | - |
132 | | - return array( |
133 | | - 'img_width' => $info['width'], |
134 | | - 'img_height' => $info['height'], |
135 | | - 'img_bits' => $info['bits'], |
136 | | - 'img_media_type' => $info['media'], |
137 | | - 'img_major_mime' => $info['major'], |
138 | | - 'img_minor_mime' => $info['minor'] ); |
| 124 | + // Create a File object from the row |
| 125 | + // This will also upgrade it |
| 126 | + $file = $this->getRepo()->newFileFromRow( $row ); |
| 127 | + return $file->getUpgraded(); |
139 | 128 | } |
140 | 129 | |
141 | | - |
142 | 130 | function buildOldImage() { |
143 | 131 | $this->buildTable( 'oldimage', 'oi_archive_name', |
144 | 132 | array( &$this, 'oldimageCallback' ) ); |
145 | 133 | } |
146 | 134 | |
147 | 135 | function oldimageCallback( $row ) { |
148 | | - if( $row->oi_width ) { |
149 | | - return null; |
| 136 | + // Create a File object from the row |
| 137 | + // This will also upgrade it |
| 138 | + if ( $row->oi_archive_name == '' ) { |
| 139 | + $this->log( "Empty oi_archive_name for oi_name={$row->oi_name}" ); |
| 140 | + return false; |
150 | 141 | } |
151 | | - |
152 | | - // Fill in the new image info fields |
153 | | - $info = $this->imageInfo( $row->oi_archive_name, 'wfImageArchiveDir', $row->oi_name ); |
154 | | - return array( |
155 | | - 'oi_width' => $info['width' ], |
156 | | - 'oi_height' => $info['height'], |
157 | | - 'oi_bits' => $info['bits' ] ); |
| 142 | + $file = $this->getRepo()->newFileFromRow( $row ); |
| 143 | + return $file->getUpgraded(); |
158 | 144 | } |
159 | 145 | |
160 | 146 | function crawlMissing() { |
161 | | - global $wgUploadDirectory, $wgHashedUploadDirectory; |
162 | | - if( $wgHashedUploadDirectory ) { |
163 | | - for( $i = 0; $i < 16; $i++ ) { |
164 | | - for( $j = 0; $j < 16; $j++ ) { |
165 | | - $dir = sprintf( '%s%s%01x%s%02x', |
166 | | - $wgUploadDirectory, |
167 | | - DIRECTORY_SEPARATOR, |
168 | | - $i, |
169 | | - DIRECTORY_SEPARATOR, |
170 | | - $i * 16 + $j ); |
171 | | - $this->crawlDirectory( $dir ); |
172 | | - } |
173 | | - } |
174 | | - } else { |
175 | | - $this->crawlDirectory( $wgUploadDirectory ); |
176 | | - } |
| 147 | + $repo = RepoGroup::singleton()->getLocalRepo(); |
| 148 | + $repo->enumFilesInFS( array( $this, 'checkMissingImage' ) ); |
177 | 149 | } |
178 | 150 | |
179 | | - function crawlDirectory( $dir ) { |
180 | | - if( !file_exists( $dir ) ) { |
181 | | - return $this->log( "no directory, skipping $dir" ); |
| 151 | + function checkMissingImage( $fullpath ) { |
| 152 | + $fname = 'ImageBuilder::checkMissingImage'; |
| 153 | + $filename = wfBaseName( $fullpath ); |
| 154 | + if( is_dir( $fullpath ) ) { |
| 155 | + return; |
182 | 156 | } |
183 | | - if( !is_dir( $dir ) ) { |
184 | | - return $this->log( "not a directory?! skipping $dir" ); |
| 157 | + if( is_link( $fullpath ) ) { |
| 158 | + $this->log( "skipping symlink at $fullpath" ); |
| 159 | + return; |
185 | 160 | } |
186 | | - if( !is_readable( $dir ) ) { |
187 | | - return $this->log( "dir not readable, skipping $dir" ); |
188 | | - } |
189 | | - $source = opendir( $dir ); |
190 | | - if( $source === false ) { |
191 | | - return $this->log( "couldn't open dir, skipping $dir" ); |
192 | | - } |
193 | | - |
194 | | - $this->log( "crawling $dir" ); |
195 | | - while( false !== ( $filename = readdir( $source ) ) ) { |
196 | | - $fullpath = $dir . DIRECTORY_SEPARATOR . $filename; |
197 | | - if( is_dir( $fullpath ) ) { |
198 | | - continue; |
199 | | - } |
200 | | - if( is_link( $fullpath ) ) { |
201 | | - $this->log( "skipping symlink at $fullpath" ); |
202 | | - continue; |
203 | | - } |
204 | | - $this->checkMissingImage( $filename, $fullpath ); |
205 | | - } |
206 | | - closedir( $source ); |
207 | | - } |
208 | | - |
209 | | - function checkMissingImage( $filename, $fullpath ) { |
210 | | - $fname = 'ImageBuilder::checkMissingImage'; |
211 | 161 | $row = $this->dbw->selectRow( 'image', |
212 | 162 | array( 'img_name' ), |
213 | 163 | array( 'img_name' => $filename ), |
— | — | @@ -224,7 +174,7 @@ |
225 | 175 | $fname = 'ImageBuilder::addMissingImage'; |
226 | 176 | |
227 | 177 | $size = filesize( $fullpath ); |
228 | | - $info = $this->imageInfo( $filename ); |
| 178 | + $info = $this->imageInfo( $fullpath ); |
229 | 179 | $timestamp = $this->dbw->timestamp( filemtime( $fullpath ) ); |
230 | 180 | |
231 | 181 | global $wgContLang; |
— | — | @@ -242,23 +192,14 @@ |
243 | 193 | $this->log( "Empty filename for $fullpath" ); |
244 | 194 | return; |
245 | 195 | } |
246 | | - |
247 | | - $fields = array( |
248 | | - 'img_name' => $filename, |
249 | | - 'img_size' => $size, |
250 | | - 'img_width' => $info['width'], |
251 | | - 'img_height' => $info['height'], |
252 | | - 'img_metadata' => '', // filled in on-demand |
253 | | - 'img_bits' => $info['bits'], |
254 | | - 'img_media_type' => $info['media'], |
255 | | - 'img_major_mime' => $info['major'], |
256 | | - 'img_minor_mime' => $info['minor'], |
257 | | - 'img_description' => '(recovered file, missing upload log entry)', |
258 | | - 'img_user' => 0, |
259 | | - 'img_user_text' => 'Conversion script', |
260 | | - 'img_timestamp' => $timestamp ); |
261 | | - if( !$this->dryrun ) { |
262 | | - $this->dbw->insert( 'image', $fields, $fname ); |
| 196 | + if ( !$this->dryrun ) { |
| 197 | + $file = wfLocalFile( $filename ); |
| 198 | + if ( !$file->recordUpload( '', '(recovered file, missing upload log entry)', '', '', '', |
| 199 | + false, $timestamp ) ) |
| 200 | + { |
| 201 | + $this->log( "Error uploading file $fullpath" ); |
| 202 | + return; |
| 203 | + } |
263 | 204 | } |
264 | 205 | $this->log( $fullpath ); |
265 | 206 | } |
Index: branches/filerepo-work/phase3/includes/filerepo/OldLocalFile.php |
— | — | @@ -12,12 +12,33 @@ |
13 | 13 | const MAX_CACHE_ROWS = 20;
|
14 | 14 |
|
15 | 15 | function newFromTitle( $title, $repo, $time ) {
|
16 | | - return new OldLocalFile( $title, $repo, $time );
|
| 16 | + return new self( $title, $repo, $time, null );
|
17 | 17 | }
|
18 | 18 |
|
19 | | - function __construct( $title, $repo, $time ) {
|
| 19 | + function newFromArchiveName( $title, $repo, $archiveName ) {
|
| 20 | + return new self( $title, $repo, null, $archiveName );
|
| 21 | + }
|
| 22 | +
|
| 23 | + function newFromRow( $row, $repo ) {
|
| 24 | + $title = Title::makeTitle( NS_IMAGE, $row->oi_name );
|
| 25 | + $file = new self( $title, $repo, null, $row->oi_archive_name );
|
| 26 | + $file->loadFromRow( $row, 'oi_' );
|
| 27 | + return $file;
|
| 28 | + }
|
| 29 | +
|
| 30 | + /**
|
| 31 | + * @param Title $title
|
| 32 | + * @param FileRepo $repo
|
| 33 | + * @param string $time Timestamp or null to load by archive name
|
| 34 | + * @param string $archiveName Archive name or null to load by timestamp
|
| 35 | + */
|
| 36 | + function __construct( $title, $repo, $time, $archiveName ) {
|
20 | 37 | parent::__construct( $title, $repo );
|
21 | 38 | $this->requestedTime = $time;
|
| 39 | + $this->archive_name = $archiveName;
|
| 40 | + if ( is_null( $time ) && is_null( $archiveName ) ) {
|
| 41 | + throw new MWException( __METHOD__.': must specify at least one of $time or $archiveName' );
|
| 42 | + }
|
22 | 43 | }
|
23 | 44 |
|
24 | 45 | function getCacheKey() {
|
— | — | @@ -26,7 +47,9 @@ |
27 | 48 | }
|
28 | 49 |
|
29 | 50 | function getArchiveName() {
|
30 | | - $this->load();
|
| 51 | + if ( !isset( $this->archive_name ) ) {
|
| 52 | + $this->load();
|
| 53 | + }
|
31 | 54 | return $this->archive_name;
|
32 | 55 | }
|
33 | 56 |
|
— | — | @@ -48,22 +71,32 @@ |
49 | 72 | $oldImages = $wgMemc->get( $key );
|
50 | 73 |
|
51 | 74 | if ( isset( $oldImages['version'] ) && $oldImages['version'] == MW_OLDFILE_VERSION ) {
|
52 | | - unset( $oldImages['version'];
|
| 75 | + unset( $oldImages['version'] );
|
53 | 76 | $more = isset( $oldImages['more'] );
|
54 | 77 | unset( $oldImages['more'] );
|
55 | | - krsort( $oldImages );
|
56 | 78 | $found = false;
|
57 | | - foreach ( $oldImages as $timestamp => $info ) {
|
58 | | - if ( $timestamp <= $this->desiredTimestamp ) {
|
59 | | - $found = true;
|
60 | | - break;
|
| 79 | + if ( is_null( $this->requestedTime ) ) {
|
| 80 | + foreach ( $oldImages as $timestamp => $info ) {
|
| 81 | + if ( $info['archive_name'] == $this->archive_name ) {
|
| 82 | + $found = true;
|
| 83 | + break;
|
| 84 | + }
|
61 | 85 | }
|
| 86 | + } else {
|
| 87 | + krsort( $oldImages );
|
| 88 | + foreach ( $oldImages as $timestamp => $info ) {
|
| 89 | + if ( $timestamp <= $this->requestedTime ) {
|
| 90 | + $found = true;
|
| 91 | + break;
|
| 92 | + }
|
| 93 | + }
|
62 | 94 | }
|
63 | 95 | if ( $found ) {
|
64 | 96 | wfDebug( "Pulling file metadata from cache key {$key}[{$timestamp}]\n" );
|
65 | | - $this->loadFromRow( (object)$cachedValues ) );
|
66 | | - $this->fileExists = true;
|
67 | 97 | $this->dataLoaded = true;
|
| 98 | + foreach ( $cachedValues as $name => $value ) {
|
| 99 | + $this->$name = $value;
|
| 100 | + }
|
68 | 101 | } elseif ( $more ) {
|
69 | 102 | wfDebug( "Cache key was truncated, oldimage row might be found in the database\n" );
|
70 | 103 | } else {
|
— | — | @@ -122,16 +155,16 @@ |
123 | 156 | function loadFromDB() {
|
124 | 157 | wfProfileIn( __METHOD__ );
|
125 | 158 | $dbr = $this->repo->getSlaveDB();
|
| 159 | + $conds = array( 'oi_name' => $this->getName() );
|
| 160 | + if ( is_null( $this->requestedTimestamp ) ) {
|
| 161 | + $conds['oi_archive_name'] = $this->archive_name;
|
| 162 | + } else {
|
| 163 | + $conds[] = 'oi_timestamp <= ' . $dbr->addQuotes( $this->requestedTimestamp );
|
| 164 | + }
|
126 | 165 | $row = $dbr->selectRow( 'oldimage', $this->getCacheFields( 'oi_' ),
|
127 | | - array(
|
128 | | - 'oi_name' => $this->getName(),
|
129 | | - 'oi_timestamp <= ' . $this->requestedTimestamp
|
130 | | - ), __METHOD__, array( 'ORDER BY' => 'oi_timestamp DESC' ) );
|
| 166 | + $conds, __METHOD__, array( 'ORDER BY' => 'oi_timestamp DESC' ) );
|
131 | 167 | if ( $row ) {
|
132 | | - $this->decodeRow( $row, 'oi_' );
|
133 | 168 | $this->loadFromRow( $row, 'oi_' );
|
134 | | - // Check for rows from a previous schema, quietly upgrade them
|
135 | | - $this->maybeUpgradeRow();
|
136 | 169 | } else {
|
137 | 170 | $this->fileExists = false;
|
138 | 171 | }
|
— | — | @@ -141,14 +174,19 @@ |
142 | 175 | function getCacheFields( $prefix = 'img_' ) {
|
143 | 176 | $fields = parent::getCacheFields( $prefix );
|
144 | 177 | $fields[] = $prefix . 'archive_name';
|
| 178 | +
|
| 179 | + // XXX: Temporary hack before schema update
|
| 180 | + $fields = array_diff( $fields, array(
|
| 181 | + 'oi_media_type', 'oi_major_mime', 'oi_minor_mime', 'oi_metadata' ) );
|
| 182 | + return $fields;
|
145 | 183 | }
|
146 | 184 |
|
147 | 185 | function getRel() {
|
148 | | - return 'archive/' . $this->getHashPath() . '/' . $this->archive_name;
|
| 186 | + return 'archive/' . $this->getHashPath() . $this->getArchiveName();
|
149 | 187 | }
|
150 | 188 |
|
151 | 189 | function getUrlRel() {
|
152 | | - return 'archive/' . $this->getHashPath() . '/' . urlencode( $this->archive_name );
|
| 190 | + return 'archive/' . $this->getHashPath() . urlencode( $this->getArchiveName() );
|
153 | 191 | }
|
154 | 192 |
|
155 | 193 | function upgradeRow() {
|
— | — | @@ -165,15 +203,19 @@ |
166 | 204 | 'oi_width' => $this->width,
|
167 | 205 | 'oi_height' => $this->height,
|
168 | 206 | 'oi_bits' => $this->bits,
|
169 | | - 'oi_media_type' => $this->media_type,
|
170 | | - 'oi_major_mime' => $major,
|
171 | | - 'oi_minor_mime' => $minor,
|
172 | | - 'oi_metadata' => $this->metadata,
|
| 207 | + #'oi_media_type' => $this->media_type,
|
| 208 | + #'oi_major_mime' => $major,
|
| 209 | + #'oi_minor_mime' => $minor,
|
| 210 | + #'oi_metadata' => $this->metadata,
|
173 | 211 | ), array( 'oi_name' => $this->getName(), 'oi_timestamp' => $this->requestedTime ),
|
174 | 212 | __METHOD__
|
175 | 213 | );
|
176 | 214 | wfProfileOut( __METHOD__ );
|
177 | 215 | }
|
| 216 | +
|
| 217 | + // XXX: Temporary hack before schema update
|
| 218 | + function maybeUpgradeRow() {}
|
| 219 | +
|
178 | 220 | }
|
179 | 221 |
|
180 | 222 |
|
Index: branches/filerepo-work/phase3/includes/filerepo/LocalFile.php |
— | — | @@ -34,7 +34,8 @@ |
35 | 35 | $size, # Size in bytes (loadFromXxx) |
36 | 36 | $metadata, # Metadata |
37 | 37 | $timestamp, # Upload timestamp |
38 | | - $dataLoaded; # Whether or not all this has been loaded from the database (loadFromXxx) |
| 38 | + $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx) |
| 39 | + $upgraded; # Whether the row was upgraded on load |
39 | 40 | |
40 | 41 | /**#@-*/ |
41 | 42 | |
— | — | @@ -42,6 +43,13 @@ |
43 | 44 | return new self( $title, $repo ); |
44 | 45 | } |
45 | 46 | |
| 47 | + function newFromRow( $row, $repo ) { |
| 48 | + $title = Title::makeTitle( NS_IMAGE, $row->img_name ); |
| 49 | + $file = new self( $title, $repo ); |
| 50 | + $file->loadFromRow( $row ); |
| 51 | + return $file; |
| 52 | + } |
| 53 | + |
46 | 54 | function __construct( $title, $repo ) { |
47 | 55 | if( !is_object( $title ) ) { |
48 | 56 | throw new MWException( __CLASS__.' constructor given bogus title.' ); |
— | — | @@ -80,7 +88,9 @@ |
81 | 89 | if ( $this->fileExists ) { |
82 | 90 | unset( $cachedValues['version'] ); |
83 | 91 | unset( $cachedValues['fileExists'] ); |
84 | | - $this->loadFromRow( $cachedValues, '' ); |
| 92 | + foreach ( $cachedValues as $name => $value ) { |
| 93 | + $this->$name = $value; |
| 94 | + } |
85 | 95 | } |
86 | 96 | } |
87 | 97 | if ( $this->dataLoaded ) { |
— | — | @@ -196,60 +206,63 @@ |
197 | 207 | function loadFromDB() { |
198 | 208 | wfProfileIn( __METHOD__ ); |
199 | 209 | |
| 210 | + # Unconditionally set loaded=true, we don't want the accessors constantly rechecking |
| 211 | + $this->dataLoaded = true; |
| 212 | + |
200 | 213 | $dbr = $this->repo->getSlaveDB(); |
201 | 214 | |
202 | 215 | $row = $dbr->selectRow( 'image', $this->getCacheFields( 'img_' ), |
203 | 216 | array( 'img_name' => $this->getName() ), __METHOD__ ); |
204 | 217 | if ( $row ) { |
205 | | - $this->fileExists = true; |
206 | | - $this->decodeRow( $row ); |
207 | 218 | $this->loadFromRow( $row ); |
208 | | - // Check for rows from a previous schema, quietly upgrade them |
209 | | - $this->maybeUpgradeRow(); |
210 | 219 | } else { |
211 | 220 | $this->fileExists = false; |
212 | 221 | } |
213 | 222 | |
214 | | - # Unconditionally set loaded=true, we don't want the accessors constantly rechecking |
215 | | - $this->dataLoaded = true; |
216 | 223 | wfProfileOut( __METHOD__ ); |
217 | 224 | } |
218 | 225 | |
219 | | - function decodeRow( &$row, $prefix = 'img_' ) { |
220 | | - $tsName = $prefix . 'timestamp'; |
221 | | - $row->$tsName = wfTimestamp( TS_MW, $row->$tsName ); |
222 | | - } |
223 | | - |
224 | | - function encodeRow( &$row, $db, $prefix = 'img_' ) { |
225 | | - $tsName = $prefix . 'timestamp'; |
226 | | - $row->$tsName = $db->timestamp( $row->$tsName ); |
227 | | - } |
228 | | - |
229 | | - /* |
230 | | - * Load file metadata from a DB result row |
| 226 | + /** |
| 227 | + * Decode a row from the database (either object or array) to an array |
| 228 | + * with timestamps and MIME types decoded, and the field prefix removed. |
231 | 229 | */ |
232 | | - function loadFromRow( $row, $prefix = 'img_' ) { |
| 230 | + function decodeRow( $row, $prefix = 'img_' ) { |
233 | 231 | $array = (array)$row; |
234 | 232 | $prefixLength = strlen( $prefix ); |
235 | 233 | // Sanity check prefix once |
236 | 234 | if ( substr( key( $array ), 0, $prefixLength ) !== $prefix ) { |
237 | 235 | throw new MWException( __METHOD__. ': incorrect $prefix parameter' ); |
238 | | - } |
| 236 | + } |
| 237 | + $decoded = array(); |
239 | 238 | foreach ( $array as $name => $value ) { |
240 | 239 | $deprefixedName = substr( $name, $prefixLength ); |
241 | | - $this->$deprefixedName = $value; |
| 240 | + $decoded[substr( $name, $prefixLength )] = $value; |
242 | 241 | } |
243 | | - if ( !$this->major_mime ) { |
244 | | - $this->mime = "unknown/unknown"; |
| 242 | + $decoded['timestamp'] = wfTimestamp( TS_MW, $decoded['timestamp'] ); |
| 243 | + if ( empty( $decoded['major_mime'] ) ) { |
| 244 | + $decoded['mime'] = "unknown/unknown"; |
245 | 245 | } else { |
246 | | - if (!$this->minor_mime) { |
247 | | - $this->minor_mime = "unknown"; |
| 246 | + if (!$decoded['minor_mime']) { |
| 247 | + $decoded['minor_mime'] = "unknown"; |
248 | 248 | } |
249 | | - $this->mime = $this->major_mime.'/'.$this->minor_mime; |
| 249 | + $decoded['mime'] = $decoded['major_mime'].'/'.$decoded['minor_mime']; |
250 | 250 | } |
251 | | - $this->dataLoaded = true; |
| 251 | + return $decoded; |
252 | 252 | } |
253 | 253 | |
| 254 | + /* |
| 255 | + * Load file metadata from a DB result row |
| 256 | + */ |
| 257 | + function loadFromRow( $row, $prefix = 'img_' ) { |
| 258 | + $array = $this->decodeRow( $row, $prefix ); |
| 259 | + foreach ( $array as $name => $value ) { |
| 260 | + $this->$name = $value; |
| 261 | + } |
| 262 | + $this->fileExists = true; |
| 263 | + // Check for rows from a previous schema, quietly upgrade them |
| 264 | + $this->maybeUpgradeRow(); |
| 265 | + } |
| 266 | + |
254 | 267 | /** |
255 | 268 | * Load file metadata from cache or DB, unless already loaded |
256 | 269 | */ |
— | — | @@ -267,16 +280,25 @@ |
268 | 281 | * Upgrade a row if it needs it |
269 | 282 | */ |
270 | 283 | function maybeUpgradeRow() { |
| 284 | + if ( wfReadOnly() ) { |
| 285 | + return; |
| 286 | + } |
271 | 287 | if ( is_null($this->media_type) || $this->mime == 'image/svg' ) { |
272 | 288 | $this->upgradeRow(); |
| 289 | + $this->upgraded = true; |
273 | 290 | } else { |
274 | 291 | $handler = $this->getHandler(); |
275 | 292 | if ( $handler && !$handler->isMetadataValid( $this, $this->metadata ) ) { |
276 | 293 | $this->upgradeRow(); |
| 294 | + $this->upgraded = true; |
277 | 295 | } |
278 | 296 | } |
279 | 297 | } |
280 | 298 | |
| 299 | + function getUpgraded() { |
| 300 | + return $this->upgraded; |
| 301 | + } |
| 302 | + |
281 | 303 | /** |
282 | 304 | * Fix assorted version-related problems with the image row by reloading it from the file |
283 | 305 | */ |
— | — | @@ -302,6 +324,7 @@ |
303 | 325 | ), array( 'img_name' => $this->getName() ), |
304 | 326 | __METHOD__ |
305 | 327 | ); |
| 328 | + $this->saveToCache(); |
306 | 329 | wfProfileOut( __METHOD__ ); |
307 | 330 | } |
308 | 331 | |
— | — | @@ -590,7 +613,9 @@ |
591 | 614 | /** |
592 | 615 | * Record a file upload in the upload log and the image table |
593 | 616 | */ |
594 | | - function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', $watch = false ) { |
| 617 | + function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', |
| 618 | + $watch = false, $timestamp = false ) |
| 619 | + { |
595 | 620 | global $wgUser, $wgUseCopyrightUpload; |
596 | 621 | |
597 | 622 | $dbw = $this->repo->getMasterDB(); |
— | — | @@ -622,7 +647,9 @@ |
623 | 648 | } |
624 | 649 | } |
625 | 650 | |
626 | | - $now = $dbw->timestamp(); |
| 651 | + if ( $timestamp === false ) { |
| 652 | + $timestamp = $dbw->timestamp(); |
| 653 | + } |
627 | 654 | |
628 | 655 | #split mime type |
629 | 656 | if (strpos($this->mime,'/')!==false) { |
— | — | @@ -646,7 +673,7 @@ |
647 | 674 | 'img_media_type' => $this->media_type, |
648 | 675 | 'img_major_mime' => $major, |
649 | 676 | 'img_minor_mime' => $minor, |
650 | | - 'img_timestamp' => $now, |
| 677 | + 'img_timestamp' => $timestamp, |
651 | 678 | 'img_description' => $desc, |
652 | 679 | 'img_user' => $wgUser->getID(), |
653 | 680 | 'img_user_text' => $wgUser->getName(), |
— | — | @@ -684,7 +711,7 @@ |
685 | 712 | 'img_media_type' => $this->media_type, |
686 | 713 | 'img_major_mime' => $major, |
687 | 714 | 'img_minor_mime' => $minor, |
688 | | - 'img_timestamp' => $now, |
| 715 | + 'img_timestamp' => $timestamp, |
689 | 716 | 'img_description' => $desc, |
690 | 717 | 'img_user' => $wgUser->getID(), |
691 | 718 | 'img_user_text' => $wgUser->getName(), |
Index: branches/filerepo-work/phase3/includes/filerepo/UnregisteredLocalFile.php |
— | — | @@ -76,7 +76,7 @@ |
77 | 77 | if ( !$this->getHandler() ) { |
78 | 78 | return false; |
79 | 79 | } |
80 | | - return $this->handler->getImageSize( $this, $this->getImagePath() ); |
| 80 | + return $this->handler->getImageSize( $this, $this->getPath() ); |
81 | 81 | } |
82 | 82 | |
83 | 83 | function getMetadata() { |
— | — | @@ -84,7 +84,7 @@ |
85 | 85 | if ( !$this->getHandler() ) { |
86 | 86 | $this->metadata = false; |
87 | 87 | } else { |
88 | | - $this->metadata = $this->handler->getMetadata( $this, $this->getImagePath() ); |
| 88 | + $this->metadata = $this->handler->getMetadata( $this, $this->getPath() ); |
89 | 89 | } |
90 | 90 | } |
91 | 91 | return $this->metadata; |
Index: branches/filerepo-work/phase3/includes/filerepo/FSRepo.php |
— | — | @@ -333,6 +333,36 @@ |
334 | 334 | } |
335 | 335 | } |
336 | 336 | } |
| 337 | + |
| 338 | + /** |
| 339 | + * Call a callback function for every file in the repository. |
| 340 | + * Uses the filesystem even in child classes. |
| 341 | + */ |
| 342 | + function enumFilesInFS( $callback ) { |
| 343 | + $numDirs = 1 << ( $this->hashLevels * 4 ); |
| 344 | + for ( $flatIndex = 0; $flatIndex < $numDirs; $flatIndex++ ) { |
| 345 | + $hexString = sprintf( "%0{$this->hashLevels}x", $flatIndex ); |
| 346 | + $path = $this->directory; |
| 347 | + for ( $hexPos = 0; $hexPos < $this->hashLevels; $hexPos++ ) { |
| 348 | + $path .= '/' . substr( $hexString, 0, $hexPos + 1 ); |
| 349 | + } |
| 350 | + if ( !file_exists( $path ) || !is_dir( $path ) ) { |
| 351 | + continue; |
| 352 | + } |
| 353 | + $dir = opendir( $path ); |
| 354 | + while ( false !== ( $name = readdir( $dir ) ) ) { |
| 355 | + call_user_func( $callback, $path . '/' . $name ); |
| 356 | + } |
| 357 | + } |
| 358 | + } |
| 359 | + |
| 360 | + /** |
| 361 | + * Call a callaback function for every file in the repository |
| 362 | + * May use either the database or the filesystem |
| 363 | + */ |
| 364 | + function enumFiles( $callback ) { |
| 365 | + $this->enumFilesInFS( $callback ); |
| 366 | + } |
337 | 367 | } |
338 | 368 | |
339 | 369 | ?> |
Index: branches/filerepo-work/phase3/includes/filerepo/LocalRepo.php |
— | — | @@ -13,4 +13,14 @@ |
14 | 14 | function getMasterDB() { |
15 | 15 | return wfGetDB( DB_MASTER ); |
16 | 16 | } |
| 17 | + |
| 18 | + function newFileFromRow( $row ) { |
| 19 | + if ( isset( $row->img_name ) ) { |
| 20 | + return LocalFile::newFromRow( $row, $this ); |
| 21 | + } elseif ( isset( $row->oi_name ) ) { |
| 22 | + return OldLocalFile::newFromRow( $row, $this ); |
| 23 | + } else { |
| 24 | + throw new MWException( __METHOD__.': invalid row' ); |
| 25 | + } |
| 26 | + } |
17 | 27 | } |
Index: branches/filerepo-work/phase3/includes/AutoLoader.php |
— | — | @@ -257,6 +257,7 @@ |
258 | 258 | 'Image' => 'includes/filerepo/LocalFile.php', |
259 | 259 | 'LocalFile' => 'includes/filerepo/LocalFile.php', |
260 | 260 | 'LocalRepo' => 'includes/filerepo/LocalRepo.php', |
| 261 | + 'OldLocalFile' => 'includes/filerepo/OldLocalFile.php', |
261 | 262 | 'RepoGroup' => 'includes/filerepo/RepoGroup.php', |
262 | 263 | 'UnregisteredLocalFile' => 'includes/filerepo/UnregisteredLocalFile.php', |
263 | 264 | |