r22587 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r22586‎ | r22587 | r22588 >
Date:03:19, 31 May 2007
Author:david
Status:old
Tags:
Comment:
Merged revisions 22555-22586 via svnmerge from
svn+ssh://david@svn.wikimedia.org/svnroot/mediawiki/trunk/phase3

........
r22557 | raymond | 2007-05-30 02:47:31 -0700 (Wed, 30 May 2007) | 2 lines

* (bug 7859) Update Kazakh translations
Patch by AlefZet
........
r22562 | nikerabbit | 2007-05-30 06:41:32 -0700 (Wed, 30 May 2007) | 2 lines

* Start of Burmese (my) translation by Hakka
........
r22563 | wegge | 2007-05-30 06:50:36 -0700 (Wed, 30 May 2007) | 2 lines

New danish translation for whatlinkshere-links
........
r22564 | rotem | 2007-05-30 08:03:45 -0700 (Wed, 30 May 2007) | 1 line

Update.
........
r22565 | brion | 2007-05-30 08:24:36 -0700 (Wed, 30 May 2007) | 3 lines

* Add Google Wireless Transcoder to the Unicode editing blacklist
........
r22569 | nikerabbit | 2007-05-30 08:56:23 -0700 (Wed, 30 May 2007) | 2 lines

* Võro (fiu-vro) updates from Võrok
........
r22571 | raymond | 2007-05-30 09:24:35 -0700 (Wed, 30 May 2007) | 2 lines

* (bug 10071) Update Georgian translations
Patch by Malafaya
........
r22572 | raymond | 2007-05-30 09:35:16 -0700 (Wed, 30 May 2007) | 2 lines

* (bug 10070) Update Old Chinese / Late Middle Chinese (zh-classical) translations
Patch by Shinjiman
........
r22573 | raymond | 2007-05-30 09:42:51 -0700 (Wed, 30 May 2007) | 2 lines

* (bug 10068) Update Chinese (zh-cn and zh-tw) translations
Patch by Alex S.H. Lin
........
r22574 | raymond | 2007-05-30 09:46:21 -0700 (Wed, 30 May 2007) | 2 lines

* (bug 10069) Update Cantonese (zh-yue) translations
Patch by Shinjiman
........
r22576 | raymond | 2007-05-30 10:47:13 -0700 (Wed, 30 May 2007) | 2 lines

* (bug 10070) Update Old Chinese / Late Middle Chinese (zh-classical) translations
Patch by Shinjiman
........
r22577 | brion | 2007-05-30 11:57:17 -0700 (Wed, 30 May 2007) | 2 lines

Honestly this whatlinkshere further link looks weird to me still, but it looks less weird with the parens taken outside the link, matching the behavior of similar links elsewhere. :)
........
r22578 | amidaniel | 2007-05-30 12:48:41 -0700 (Wed, 30 May 2007) | 1 line

Fix for http://bugzilla.wikimedia.org/show_bug.cgi?id=10036 -- add "maxlength=200" to wpBlockReason inputbox.
........
r22580 | tstarling | 2007-05-30 14:02:32 -0700 (Wed, 30 May 2007) | 12 lines

Merged filerepo-work branch:
* Added support for configuration of an arbitrary number of commons-style file repositories.
* Split Image.php into filerepo/File.php and filerepo/LocalFile.php
* Renamed Image::getImagePath() to File::getPath()
* Added initial support for timestamp-based file fetching (OldLocalFile), to be expanded upon by aaron.
* Changed the interface for Image/File object creation: use wfFindFile() or wfLocalFile() depending on semantics
* ImageGallery::add() now accepts a title object as the first parameter
* Moved file handling operations on upload from SpecialUpload to File
* Removed path-related functions from ImageFunctions.php. Removed static path accessors from File.
* Added a Content-Disposition header to thumb.php output
* Improved thumb.php error handling
* Updated the unit test suite to kind of partially work with modern computers. RunTests.php doesn't work just yet. Fixed an actual regression that the test suite detected -- moved some defines to Defines.php where they will be loaded consistently.
........
r22581 | aaron | 2007-05-30 16:06:55 -0700 (Wed, 30 May 2007) | 1 line

*clean up comment
........
r22584 | aaron | 2007-05-30 17:35:07 -0700 (Wed, 30 May 2007) | 1 line

*Add newFromArchiveName()
........
r22585 | tstarling | 2007-05-30 18:43:41 -0700 (Wed, 30 May 2007) | 1 line

Fixed a few filerepo bugs, added some documentation
........
Modified paths:
  • /branches/liquidthreads (modified) (history)
  • /branches/liquidthreads/RELEASE-NOTES (modified) (history)
  • /branches/liquidthreads/StartProfiler.php (modified) (history)
  • /branches/liquidthreads/includes/AutoLoader.php (modified) (history)
  • /branches/liquidthreads/includes/CategoryPage.php (modified) (history)
  • /branches/liquidthreads/includes/DefaultSettings.php (modified) (history)
  • /branches/liquidthreads/includes/Defines.php (modified) (history)
  • /branches/liquidthreads/includes/ExternalEdit.php (modified) (history)
  • /branches/liquidthreads/includes/GlobalFunctions.php (modified) (history)
  • /branches/liquidthreads/includes/Image.php (deleted) (history)
  • /branches/liquidthreads/includes/ImageFunctions.php (modified) (history)
  • /branches/liquidthreads/includes/ImageGallery.php (modified) (history)
  • /branches/liquidthreads/includes/ImagePage.php (modified) (history)
  • /branches/liquidthreads/includes/ImageQueryPage.php (modified) (history)
  • /branches/liquidthreads/includes/Linker.php (modified) (history)
  • /branches/liquidthreads/includes/MediaTransformOutput.php (modified) (history)
  • /branches/liquidthreads/includes/Parser.php (modified) (history)
  • /branches/liquidthreads/includes/SearchEngine.php (modified) (history)
  • /branches/liquidthreads/includes/Setup.php (modified) (history)
  • /branches/liquidthreads/includes/Skin.php (modified) (history)
  • /branches/liquidthreads/includes/SpecialBlockip.php (modified) (history)
  • /branches/liquidthreads/includes/SpecialImagelist.php (modified) (history)
  • /branches/liquidthreads/includes/SpecialMIMEsearch.php (modified) (history)
  • /branches/liquidthreads/includes/SpecialNewimages.php (modified) (history)
  • /branches/liquidthreads/includes/SpecialUndelete.php (modified) (history)
  • /branches/liquidthreads/includes/SpecialUpload.php (modified) (history)
  • /branches/liquidthreads/includes/SpecialWhatlinkshere.php (modified) (history)
  • /branches/liquidthreads/includes/StreamFile.php (modified) (history)
  • /branches/liquidthreads/includes/filerepo (added) (history)
  • /branches/liquidthreads/includes/filerepo (added) (history)
  • /branches/liquidthreads/includes/media/Bitmap.php (modified) (history)
  • /branches/liquidthreads/includes/media/DjVu.php (modified) (history)
  • /branches/liquidthreads/includes/media/Generic.php (modified) (history)
  • /branches/liquidthreads/includes/media/SVG.php (modified) (history)
  • /branches/liquidthreads/includes/normal/UtfNormal.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesDa.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesDe.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesEn.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesFiu_vro.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesHe.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesId.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesKa.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesKk_cn.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesKk_kz.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesKk_tr.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesMy.php (added) (history)
  • /branches/liquidthreads/languages/messages/MessagesMy.php (added) (history)
  • /branches/liquidthreads/languages/messages/MessagesZh_classical.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesZh_cn.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesZh_tw.php (modified) (history)
  • /branches/liquidthreads/languages/messages/MessagesZh_yue.php (modified) (history)
  • /branches/liquidthreads/maintenance/FiveUpgrade.inc (modified) (history)
  • /branches/liquidthreads/maintenance/cleanupImages.php (modified) (history)
  • /branches/liquidthreads/maintenance/importImages.inc.php (modified) (history)
  • /branches/liquidthreads/maintenance/importImages.php (modified) (history)
  • /branches/liquidthreads/maintenance/rebuildImages.php (modified) (history)
  • /branches/liquidthreads/tests/ArticleTest.php (modified) (history)
  • /branches/liquidthreads/tests/DatabaseTest.php (modified) (history)
  • /branches/liquidthreads/tests/GlobalTest.php (modified) (history)
  • /branches/liquidthreads/tests/ImageFunctionsTest.php (added) (history)
  • /branches/liquidthreads/tests/ImageFunctionsTest.php (added) (history)
  • /branches/liquidthreads/tests/ImageTest.php (deleted) (history)
  • /branches/liquidthreads/tests/README (modified) (history)
  • /branches/liquidthreads/tests/RunTests.php (modified) (history)
  • /branches/liquidthreads/tests/SanitizerTest.php (modified) (history)
  • /branches/liquidthreads/tests/SearchEngineTest.php (modified) (history)
  • /branches/liquidthreads/tests/SearchMySQL4Test.php (modified) (history)
  • /branches/liquidthreads/tests/run-test.php (added) (history)
  • /branches/liquidthreads/tests/run-test.php (added) (history)
  • /branches/liquidthreads/thumb.php (modified) (history)

Diff [purge]

Index: branches/liquidthreads/maintenance/rebuildImages.php
@@ -40,8 +40,18 @@
4141
4242 $this->maxLag = 10; # if slaves are lagged more than 10 secs, wait
4343 $this->dryrun = $dryrun;
 44+ if ( $dryrun ) {
 45+ $GLOBALS['wgReadOnly'] = 'Dry run mode, image upgrades are suppressed';
 46+ }
4447 }
4548
 49+ function getRepo() {
 50+ if ( !isset( $this->repo ) ) {
 51+ $this->repo = RepoGroup::singleton()->getLocalRepo();
 52+ }
 53+ return $this->repo;
 54+ }
 55+
4656 function build() {
4757 $this->buildImage();
4858 $this->buildOldImage();
@@ -94,13 +104,7 @@
95105
96106 while( $row = $this->dbr->fetchObject( $result ) ) {
97107 $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 ) {
105109 $this->progress( 1 );
106110 } else {
107111 $this->progress( 0 );
@@ -116,97 +120,43 @@
117121 }
118122
119123 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();
139128 }
140129
141 -
142130 function buildOldImage() {
143131 $this->buildTable( 'oldimage', 'oi_archive_name',
144132 array( &$this, 'oldimageCallback' ) );
145133 }
146134
147135 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;
150141 }
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();
158144 }
159145
160146 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' ) );
177149 }
178150
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;
182156 }
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;
185160 }
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';
211161 $row = $this->dbw->selectRow( 'image',
212162 array( 'img_name' ),
213163 array( 'img_name' => $filename ),
@@ -224,7 +174,7 @@
225175 $fname = 'ImageBuilder::addMissingImage';
226176
227177 $size = filesize( $fullpath );
228 - $info = $this->imageInfo( $filename );
 178+ $info = $this->imageInfo( $fullpath );
229179 $timestamp = $this->dbw->timestamp( filemtime( $fullpath ) );
230180
231181 global $wgContLang;
@@ -242,23 +192,14 @@
243193 $this->log( "Empty filename for $fullpath" );
244194 return;
245195 }
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+ }
263204 }
264205 $this->log( $fullpath );
265206 }
Index: branches/liquidthreads/maintenance/FiveUpgrade.inc
@@ -693,10 +693,7 @@
694694 return $copy;
695695 }
696696
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 ) {
701698 $info = array(
702699 'width' => 0,
703700 'height' => 0,
@@ -711,20 +708,13 @@
712709
713710 $info['media'] = $magic->getMediaType( $filename, $mime );
714711
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'] ) ) {
729719 $info['bits'] = $gis['bits'];
730720 }
731721
@@ -896,7 +886,7 @@
897887 }
898888
899889 function upgradeLogging() {
900 - $tabledef = <<<END
 890+ $tabledef = <<<ENDS
901891 CREATE TABLE $1 (
902892 -- Symbolic keys for the general log type and the action type
903893 -- within the log. The output format will be controlled by the
@@ -926,7 +916,7 @@
927917 KEY page_time (log_namespace, log_title, log_timestamp)
928918
929919 ) TYPE=InnoDB
930 -END;
 920+ENDS;
931921 $fields = array(
932922 'log_type' => MW_UPGRADE_COPY,
933923 'log_action' => MW_UPGRADE_COPY,
@@ -940,7 +930,7 @@
941931 }
942932
943933 function upgradeArchive() {
944 - $tabledef = <<<END
 934+ $tabledef = <<<ENDS
945935 CREATE TABLE $1 (
946936 ar_namespace int NOT NULL default '0',
947937 ar_title varchar(255) binary NOT NULL default '',
@@ -960,7 +950,7 @@
961951 KEY name_title_timestamp (ar_namespace,ar_title,ar_timestamp)
962952
963953 ) TYPE=InnoDB
964 -END;
 954+ENDS;
965955 $fields = array(
966956 'ar_namespace' => MW_UPGRADE_COPY,
967957 'ar_title' => MW_UPGRADE_ENCODE,
@@ -979,7 +969,7 @@
980970 function upgradeImagelinks() {
981971 global $wgUseLatin1;
982972 if( $wgUseLatin1 ) {
983 - $tabledef = <<<END
 973+ $tabledef = <<<ENDS
984974 CREATE TABLE $1 (
985975 -- Key to page_id of the page containing the image / media link.
986976 il_from int(8) unsigned NOT NULL default '0',
@@ -993,7 +983,7 @@
994984 KEY (il_to)
995985
996986 ) TYPE=InnoDB
997 -END;
 987+ENDS;
998988 $fields = array(
999989 'il_from' => MW_UPGRADE_COPY,
1000990 'il_to' => MW_UPGRADE_ENCODE );
@@ -1004,7 +994,7 @@
1005995 function upgradeCategorylinks() {
1006996 global $wgUseLatin1;
1007997 if( $wgUseLatin1 ) {
1008 - $tabledef = <<<END
 998+ $tabledef = <<<ENDS
1009999 CREATE TABLE $1 (
10101000 cl_from int(8) unsigned NOT NULL default '0',
10111001 cl_to varchar(255) binary NOT NULL default '',
@@ -1015,7 +1005,7 @@
10161006 KEY cl_sortkey(cl_to,cl_sortkey),
10171007 KEY cl_timestamp(cl_to,cl_timestamp)
10181008 ) TYPE=InnoDB
1019 -END;
 1009+ENDS;
10201010 $fields = array(
10211011 'cl_from' => MW_UPGRADE_COPY,
10221012 'cl_to' => MW_UPGRADE_ENCODE,
@@ -1028,7 +1018,7 @@
10291019 function upgradeIpblocks() {
10301020 global $wgUseLatin1;
10311021 if( $wgUseLatin1 ) {
1032 - $tabledef = <<<END
 1022+ $tabledef = <<<ENDS
10331023 CREATE TABLE $1 (
10341024 ipb_id int(8) NOT NULL auto_increment,
10351025 ipb_address varchar(40) binary NOT NULL default '',
@@ -1044,7 +1034,7 @@
10451035 INDEX ipb_user (ipb_user)
10461036
10471037 ) TYPE=InnoDB
1048 -END;
 1038+ENDS;
10491039 $fields = array(
10501040 'ipb_id' => MW_UPGRADE_COPY,
10511041 'ipb_address' => MW_UPGRADE_COPY,
@@ -1060,7 +1050,7 @@
10611051
10621052 function upgradeRecentchanges() {
10631053 // There's a format change in the namespace field
1064 - $tabledef = <<<END
 1054+ $tabledef = <<<ENDS
10651055 CREATE TABLE $1 (
10661056 rc_id int(8) NOT NULL auto_increment,
10671057 rc_timestamp varchar(14) binary NOT NULL default '',
@@ -1098,7 +1088,7 @@
10991089 INDEX rc_ip (rc_ip)
11001090
11011091 ) TYPE=InnoDB
1102 -END;
 1092+ENDS;
11031093 $fields = array(
11041094 'rc_id' => MW_UPGRADE_COPY,
11051095 'rc_timestamp' => MW_UPGRADE_COPY,
@@ -1124,7 +1114,7 @@
11251115
11261116 function upgradeQuerycache() {
11271117 // There's a format change in the namespace field
1128 - $tabledef = <<<END
 1118+ $tabledef = <<<ENDS
11291119 CREATE TABLE $1 (
11301120 -- A key name, generally the base name of of the special page.
11311121 qc_type char(32) NOT NULL,
@@ -1139,7 +1129,7 @@
11401130 KEY (qc_type,qc_value)
11411131
11421132 ) TYPE=InnoDB
1143 -END;
 1133+ENDS;
11441134 $fields = array(
11451135 'qc_type' => MW_UPGRADE_COPY,
11461136 'qc_value' => MW_UPGRADE_COPY,
Index: branches/liquidthreads/maintenance/importImages.php
@@ -42,56 +42,40 @@
4343 $license = isset( $options['license'] ) ? $options['license'] : '';
4444
4545 # Batch "upload" operation
 46+ global $wgUploadDirectory;
4647 foreach( $files as $file ) {
47 -
4848 $base = wfBaseName( $file );
4949
5050 # Validate a title
5151 $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+ }
5356
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+ }
5763
58 - global $wgUploadDirectory;
 64+ # Stash the file
 65+ echo( "Saving {$base}..." );
5966
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..." );
6373
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" );
9277 } else {
93 - echo( "{$base} could not be imported; a valid title cannot be produced\n" );
 78+ echo( "failed.\n" );
9479 }
95 -
9680 }
9781
9882 } else {
Index: branches/liquidthreads/maintenance/cleanupImages.php
@@ -89,7 +89,10 @@
9090 }
9191
9292 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;
9497 }
9598
9699 function pokeFile( $orig, $new ) {
Index: branches/liquidthreads/maintenance/importImages.inc.php
@@ -47,20 +47,4 @@
4848 return array( $fname, $ext );
4949 }
5050
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/liquidthreads/tests/ImageTest.php
@@ -1,67 +0,0 @@
2 -<?php
3 -
4 -require_once( 'PHPUnit.php' );
5 -require_once( '../includes/Defines.php' );
6 -#require_once( '../includes/Profiling.php' );
7 -require_once( '../includes/GlobalFunctions.php' );
8 -require_once( '../includes/Image.php' );
9 -require_once( '../includes/ImageFunctions.php' );
10 -
11 -class ImageTest extends PHPUnit_TestCase {
12 - function ImageTest( $name ) {
13 - $this->PHPUnit_TestCase( $name );
14 - }
15 -
16 - function setUp() {
17 - }
18 -
19 - function tearDown() {
20 - }
21 -
22 - function testFitBoxWidth() {
23 - $vals = array(
24 - array(
25 - 'width' => 50,
26 - 'height' => 50,
27 - 'tests' => array(
28 - 50 => 50,
29 - 17 => 17,
30 - 18 => 18 ) ),
31 - array(
32 - 'width' => 366,
33 - 'height' => 300,
34 - 'tests' => array(
35 - 50 => 61,
36 - 17 => 21,
37 - 18 => 22 ) ),
38 - array(
39 - 'width' => 300,
40 - 'height' => 366,
41 - 'tests' => array(
42 - 50 => 41,
43 - 17 => 14,
44 - 18 => 15 ) ),
45 - array(
46 - 'width' => 100,
47 - 'height' => 400,
48 - 'tests' => array(
49 - 50 => 12,
50 - 17 => 4,
51 - 18 => 4 ) ) );
52 - foreach( $vals as $row ) {
53 - extract( $row );
54 - foreach( $tests as $max => $expected ) {
55 - $y = round( $expected * $height / $width );
56 - $result = wfFitBoxWidth( $width, $height, $max );
57 - $y2 = round( $result * $height / $width );
58 - $this->assertEquals( $expected,
59 - $result,
60 - "($width, $height, $max) wanted: {$expected}x$y, got: {$result}x$y2" );
61 - }
62 - }
63 - }
64 -
65 - /* TODO: many more! */
66 -}
67 -
68 -?>
Index: branches/liquidthreads/tests/README
@@ -1,11 +1,9 @@
22 Some quickie unit tests done with the PHPUnit testing framework. To run the
33 test suite, run 'make test' in this dir or 'php RunTests.php'
44
5 -You can install PHPUnit via pear like this:
6 -Firstly, register phpunit channel (it only need to be done once):
 5+PHPUnit is no longer maintained by PEAR. To get the current version of
 6+PHPUnit, first uninstall any old version of PHPUnit or PHPUnit2 from PEAR,
 7+then install the current version from phpunit.de like this:
 8+
79 # pear channel-discover pear.phpunit.de
8 -Then install the package:
910 # pear install phpunit/PHPUnit
10 -
11 -Or fetch and install it manually:
12 -http://www.phpunit.de/
Index: branches/liquidthreads/tests/SanitizerTest.php
@@ -1,22 +1,6 @@
22 <?php
33
4 -require_once( 'PHPUnit.php' );
5 -require_once( '../includes/Defines.php' );
6 -#require_once( '../includes/Profiling.php' );
7 -require_once( '../includes/GlobalFunctions.php' );
8 -require_once( '../includes/Sanitizer.php' );
9 -
10 -class SanitizerTest extends PHPUnit_TestCase {
11 - function SanitizerTest( $name ) {
12 - $this->PHPUnit_TestCase( $name );
13 - }
14 -
15 - function setUp() {
16 - }
17 -
18 - function tearDown() {
19 - }
20 -
 4+class SanitizerTest extends PHPUnit_Framework_TestCase {
215 function testDecodeNamed() {
226 $this->assertEquals(
237 "\xc3\xa9cole",
Index: branches/liquidthreads/tests/ImageFunctionsTest.php
@@ -0,0 +1,48 @@
 2+<?php
 3+
 4+class ImageFunctionsTest extends PHPUnit_Framework_TestCase {
 5+ function testFitBoxWidth() {
 6+ $vals = array(
 7+ array(
 8+ 'width' => 50,
 9+ 'height' => 50,
 10+ 'tests' => array(
 11+ 50 => 50,
 12+ 17 => 17,
 13+ 18 => 18 ) ),
 14+ array(
 15+ 'width' => 366,
 16+ 'height' => 300,
 17+ 'tests' => array(
 18+ 50 => 61,
 19+ 17 => 21,
 20+ 18 => 22 ) ),
 21+ array(
 22+ 'width' => 300,
 23+ 'height' => 366,
 24+ 'tests' => array(
 25+ 50 => 41,
 26+ 17 => 14,
 27+ 18 => 15 ) ),
 28+ array(
 29+ 'width' => 100,
 30+ 'height' => 400,
 31+ 'tests' => array(
 32+ 50 => 12,
 33+ 17 => 4,
 34+ 18 => 4 ) ) );
 35+ foreach( $vals as $row ) {
 36+ extract( $row );
 37+ foreach( $tests as $max => $expected ) {
 38+ $y = round( $expected * $height / $width );
 39+ $result = wfFitBoxWidth( $width, $height, $max );
 40+ $y2 = round( $result * $height / $width );
 41+ $this->assertEquals( $expected,
 42+ $result,
 43+ "($width, $height, $max) wanted: {$expected}x$y, got: {$result}x$y2" );
 44+ }
 45+ }
 46+ }
 47+}
 48+
 49+?>
Property changes on: branches/liquidthreads/tests/ImageFunctionsTest.php
___________________________________________________________________
Added: svn:eol-style
150 + native
Added: svn:keywords
251 + Author Date Id Revision
Index: branches/liquidthreads/tests/SearchMySQL4Test.php
@@ -1,7 +1,5 @@
22 <?php
3 -
43 require_once( 'SearchEngineTest.php' );
5 -require_once( '../includes/SearchMySQL4.php' );
64
75 class SearchMySQL4Test extends SearchEngine_TestCase {
86 var $db;
Index: branches/liquidthreads/tests/run-test.php
@@ -0,0 +1,7 @@
 2+<?php
 3+
 4+require_once( dirname(__FILE__) . '/../maintenance/commandLine.inc' );
 5+ini_set( 'include_path', get_include_path() . PATH_SEPARATOR . /*$_SERVER['PHP_PEAR_INSTALL_DIR']*/ 'C:\php\pear' );
 6+require( 'PHPUnit/TextUI/Command.php' );
 7+
 8+?>
Property changes on: branches/liquidthreads/tests/run-test.php
___________________________________________________________________
Added: svn:eol-style
19 + native
Index: branches/liquidthreads/tests/ArticleTest.php
@@ -1,19 +1,8 @@
22 <?php
33
4 -require_once( 'PHPUnit.php' );
5 -require_once( '../includes/Defines.php' );
6 -require_once( '../includes/Article.php' );
7 -require_once( '../includes/Revision.php' );
8 -require_once( '../includes/ProfilerStub.php' );
9 -require_once( '../includes/normal/UtfNormal.php' );
10 -
11 -class ArticleTest extends PHPUnit_TestCase {
 4+class ArticleTest extends PHPUnit_Framework_TestCase {
125 var $saveGlobals = array();
136
14 - function ArticleTest( $name ) {
15 - $this->PHPUnit_TestCase( $name );
16 - }
17 -
187 function setUp() {
198 $globalSet = array(
209 'wgLegacyEncoding' => false,
@@ -104,20 +93,6 @@
10594 Revision::getRevisionText( $row ), "getRevisionText" );
10695 }
10796
108 - function testCompressRevisionTextLatin1() {
109 - $GLOBALS['wgUseLatin1'] = true;
110 - $row->old_text = "Wiki est l'\xe9cole superieur !";
111 - $row->old_flags = Revision::compressRevisionText( $row->old_text );
112 - $this->assertFalse( false !== strpos( $row->old_flags, 'utf-8' ),
113 - "Flags should not contain 'utf-8'" );
114 - $this->assertFalse( false !== strpos( $row->old_flags, 'gzip' ),
115 - "Flags should not contain 'gzip'" );
116 - $this->assertEquals( "Wiki est l'\xe9cole superieur !",
117 - $row->old_text, "Direct check" );
118 - $this->assertEquals( "Wiki est l'\xe9cole superieur !",
119 - Revision::getRevisionText( $row ), "getRevisionText" );
120 - }
121 -
12297 function testCompressRevisionTextUtf8Gzip() {
12398 $GLOBALS['wgCompressRevisions'] = true;
12499 $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
@@ -131,23 +106,6 @@
132107 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
133108 Revision::getRevisionText( $row ), "getRevisionText" );
134109 }
135 -
136 - function testCompressRevisionTextLatin1Gzip() {
137 - $GLOBALS['wgCompressRevisions'] = true;
138 - $GLOBALS['wgUseLatin1'] = true;
139 - $row = new stdClass;
140 - $row->old_text = "Wiki est l'\xe9cole superieur !";
141 - $row->old_flags = Revision::compressRevisionText( $row->old_text );
142 - $this->assertFalse( false !== strpos( $row->old_flags, 'utf-8' ),
143 - "Flags should not contain 'utf-8'" );
144 - $this->assertTrue( false !== strpos( $row->old_flags, 'gzip' ),
145 - "Flags should contain 'gzip'" );
146 - $this->assertEquals( "Wiki est l'\xe9cole superieur !",
147 - gzinflate( $row->old_text ), "Direct check" );
148 - $this->assertEquals( "Wiki est l'\xe9cole superieur !",
149 - Revision::getRevisionText( $row ), "getRevisionText" );
150 - }
151 -
152110 }
153111
154112 ?>
Index: branches/liquidthreads/tests/RunTests.php
@@ -1,26 +1,12 @@
22 <?php
33
4 -if( php_sapi_name() != 'cli' ) {
5 - echo 'Must be run from the command line.';
6 - die( -1 );
7 -}
 4+die( "This is broken, use run-test.php for now.\n" );
85
 6+require_once( dirname( __FILE__ ) . '/../maintenance/commandLine.inc' );
 7+ini_set( 'include_path', get_include_path() . PATH_SEPARATOR . /*$_SERVER['PHP_PEAR_INSTALL_DIR']*/ 'C:\php\pear' );
98 error_reporting( E_ALL );
10 -define( "MEDIAWIKI", true );
 9+require_once( 'PHPUnit/Framework.php' );
1110
12 -set_include_path( get_include_path() . PATH_SEPARATOR . 'PHPUnit' );
13 -set_include_path( get_include_path() . PATH_SEPARATOR . '..' );
14 -
15 -// Error handling when requiring PHPUnit.php
16 -function phpunitErrorHandler( $erno, $errstr, $errfile, $errline) {
17 - echo "Unable to include PHPUnit.php, you should install it first (see README).\n";
18 - exit(1);
19 -}
20 -
21 -set_error_handler('phpunitErrorHandler');
22 -require_once( 'PHPUnit.php' );
23 -restore_error_handler();
24 -
2511 $testOptions = array(
2612 'mysql4' => array(
2713 'server' => null,
@@ -34,10 +20,6 @@
3521 'database' => null ),
3622 );
3723
38 -if( file_exists( 'LocalTestSettings.php' ) ) {
39 - include( './LocalTestSettings.php' );
40 -}
41 -
4224 $tests = array(
4325 'GlobalTest',
4426 'DatabaseTest',
@@ -47,14 +29,14 @@
4830 'ImageTest'
4931 );
5032
51 -if( isset( $_SERVER['argv'][1] ) ) {
 33+if( count( $args ) ) {
5234 // to override...
53 - $tests = array( $_SERVER['argv'][1] );
 35+ $tests = $args;
5436 }
5537
5638 foreach( $tests as $test ) {
5739 require_once( $test . '.php' );
58 - $suite = new PHPUnit_TestSuite( $test );
 40+ $suite = new PHPUnit_Framework_TestSuite( $test );
5941 $result = PHPUnit::run( $suite );
6042 echo $result->toString();
6143 }
Index: branches/liquidthreads/tests/SearchEngineTest.php
@@ -1,20 +1,7 @@
22 <?php
33
4 -$IP = '..';
5 -require_once( 'PHPUnit.php' );
6 -require_once( '../includes/Defines.php' );
7 -require_once( '../includes/DefaultSettings.php' );
8 -#require_once( '../includes/Profiling.php' );
9 -require_once( '../includes/Hooks.php' );
10 -require_once( '../includes/MagicWord.php' );
11 -require_once( '../languages/Language.php' );
12 -
13 -require_once( '../includes/SearchEngine.php' );
14 -require_once( '../includes/SearchMySQL.php' );
15 -require_once( '../includes/SearchMySQL4.php' );
16 -
174 /** @todo document */
18 -class SearchEngine_TestCase extends PHPUnit_TestCase {
 5+class SearchEngine_TestCase extends PHPUnit_Framework_TestCase {
196 var $db, $search;
207
218 function insertSearchData() {
Index: branches/liquidthreads/tests/GlobalTest.php
@@ -1,32 +1,6 @@
22 <?php
33
4 -require_once( 'PHPUnit.php' );
5 -require_once( '../includes/Defines.php' );
6 -require_once( '../includes/GlobalFunctions.php' );
7 -require_once( '../includes/Exception.php' );
8 -
9 -class GlobalTest extends PHPUnit_TestCase {
10 - function GlobalTest( $name ) {
11 - $this->PHPUnit_TestCase( $name );
12 - }
13 -
14 - function setUp() {
15 - $this->save = array();
16 - $saveVars = array( 'wgReadOnlyFile' );
17 - foreach( $saveVars as $var ) {
18 - if( isset( $GLOBALS[$var] ) ) {
19 - $this->save[$var] = $GLOBALS[$var];
20 - }
21 - }
22 - $GLOBALS['wgReadOnlyFile'] = wfTempDir() . '/testReadOnly-' . mt_rand();
23 - }
24 -
25 - function tearDown() {
26 - foreach( $this->save as $var => $data ) {
27 - $GLOBALS[$var] = $data;
28 - }
29 - }
30 -
 4+class GlobalTest extends PHPUnit_Framework_TestCase {
315 function testRandom() {
326 # This could hypothetically fail, but it shouldn't ;)
337 $this->assertFalse(
Index: branches/liquidthreads/tests/DatabaseTest.php
@@ -1,25 +1,12 @@
22 <?php
33
4 -require_once( 'PHPUnit.php' );
5 -require_once( '../includes/Defines.php' );
6 -require_once( '../includes/Database.php' );
7 -require_once( '../includes/GlobalFunctions.php' );
8 -
9 -class DatabaseTest extends PHPUnit_TestCase {
 4+class DatabaseTest extends PHPUnit_Framework_TestCase {
105 var $db;
116
12 - function DatabaseTest( $name ) {
13 - $this->PHPUnit_TestCase( $name );
14 - }
15 -
167 function setUp() {
17 - $this->db = new Database();
 8+ $this->db = wfGetDB( DB_SLAVE );
189 }
1910
20 - function tearDown() {
21 - unset( $this->db );
22 - }
23 -
2411 function testAddQuotesNull() {
2512 $this->assertEquals(
2613 'NULL',
Index: branches/liquidthreads/includes/Image.php
@@ -1,2154 +0,0 @@
2 -<?php
3 -/**
4 - */
5 -
6 -/**
7 - * NOTE FOR WINDOWS USERS:
8 - * To enable EXIF functions, add the folloing lines to the
9 - * "Windows extensions" section of php.ini:
10 - *
11 - * extension=extensions/php_mbstring.dll
12 - * extension=extensions/php_exif.dll
13 - */
14 -
15 -/**
16 - * Bump this number when serialized cache records may be incompatible.
17 - */
18 -define( 'MW_IMAGE_VERSION', 2 );
19 -
20 -/**
21 - * Class to represent an image
22 - *
23 - * Provides methods to retrieve paths (physical, logical, URL),
24 - * to generate thumbnails or for uploading.
25 - *
26 - * @addtogroup Media
27 - */
28 -class Image
29 -{
30 - const DELETED_FILE = 1;
31 - const DELETED_COMMENT = 2;
32 - const DELETED_USER = 4;
33 - const DELETED_RESTRICTED = 8;
34 - const RENDER_NOW = 1;
35 -
36 - /**#@+
37 - * @private
38 - */
39 - var $name, # name of the image (constructor)
40 - $imagePath, # Path of the image (loadFromXxx)
41 - $url, # Image URL (accessor)
42 - $title, # Title object for this image (constructor)
43 - $fileExists, # does the image file exist on disk? (loadFromXxx)
44 - $fromSharedDirectory, # load this image from $wgSharedUploadDirectory (loadFromXxx)
45 - $historyLine, # Number of line to return by nextHistoryLine() (constructor)
46 - $historyRes, # result of the query for the image's history (nextHistoryLine)
47 - $width, # \
48 - $height, # |
49 - $bits, # --- returned by getimagesize (loadFromXxx)
50 - $attr, # /
51 - $type, # MEDIATYPE_xxx (bitmap, drawing, audio...)
52 - $mime, # MIME type, determined by MimeMagic::guessMimeType
53 - $extension, # The file extension (constructor)
54 - $size, # Size in bytes (loadFromXxx)
55 - $metadata, # Metadata
56 - $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx)
57 - $page, # Page to render when creating thumbnails
58 - $lastError; # Error string associated with a thumbnail display error
59 -
60 -
61 - /**#@-*/
62 -
63 - /**
64 - * Create an Image object from an image name
65 - *
66 - * @param string $name name of the image, used to create a title object using Title::makeTitleSafe
67 - * @return Image
68 - * @public
69 - */
70 - public static function newFromName( $name ) {
71 - $title = Title::makeTitleSafe( NS_IMAGE, $name );
72 - if ( is_object( $title ) ) {
73 - return new Image( $title );
74 - } else {
75 - return NULL;
76 - }
77 - }
78 -
79 - /**
80 - * Obsolete factory function, use constructor
81 - * @param Title $title
82 - * @return Image
83 - * @deprecated
84 - */
85 - function newFromTitle( $title ) {
86 - return new Image( $title );
87 - }
88 -
89 - /**
90 - * Constructor
91 - * @param Title $title
92 - * @return void
93 - */
94 - function Image( $title ) {
95 - if( !is_object( $title ) ) {
96 - throw new MWException( 'Image constructor given bogus title.' );
97 - }
98 - $this->title =& $title;
99 - $this->name = $title->getDBkey();
100 - $this->metadata = '';
101 -
102 - $n = strrpos( $this->name, '.' );
103 - $this->extension = Image::normalizeExtension( $n ?
104 - substr( $this->name, $n + 1 ) : '' );
105 - $this->historyLine = 0;
106 -
107 - $this->dataLoaded = false;
108 - }
109 -
110 - /**
111 - * Normalize a file extension to the common form, and ensure it's clean.
112 - * Extensions with non-alphanumeric characters will be discarded.
113 - *
114 - * @param string $ext (without the .)
115 - * @return string
116 - */
117 - static function normalizeExtension( $ext ) {
118 - $lower = strtolower( $ext );
119 - $squish = array(
120 - 'htm' => 'html',
121 - 'jpeg' => 'jpg',
122 - 'mpeg' => 'mpg',
123 - 'tiff' => 'tif' );
124 - if( isset( $squish[$lower] ) ) {
125 - return $squish[$lower];
126 - } elseif( preg_match( '/^[0-9a-z]+$/', $lower ) ) {
127 - return $lower;
128 - } else {
129 - return '';
130 - }
131 - }
132 -
133 - /**
134 - * Get the memcached keys
135 - * @return array[int]mixed Returns an array, first element is the local cache key, second is the shared cache key, if there is one
136 - */
137 - function getCacheKeys( ) {
138 - global $wgUseSharedUploads, $wgSharedUploadDBname, $wgCacheSharedUploads;
139 -
140 - $hashedName = md5($this->name);
141 - $keys = array( wfMemcKey( 'Image', $hashedName ) );
142 - if ( $wgUseSharedUploads && $wgSharedUploadDBname && $wgCacheSharedUploads ) {
143 - $keys[] = wfForeignMemcKey( $wgSharedUploadDBname, false, 'Image', $hashedName );
144 - }
145 - return $keys;
146 - }
147 -
148 - /**
149 - * Try to load image metadata from memcached. Returns true on success.
150 - */
151 - function loadFromCache() {
152 - global $wgUseSharedUploads, $wgMemc;
153 - wfProfileIn( __METHOD__ );
154 - $this->dataLoaded = false;
155 - $keys = $this->getCacheKeys();
156 - $cachedValues = $wgMemc->get( $keys[0] );
157 -
158 - // Check if the key existed and belongs to this version of MediaWiki
159 - if (!empty($cachedValues) && is_array($cachedValues)
160 - && isset($cachedValues['version']) && ( $cachedValues['version'] == MW_IMAGE_VERSION )
161 - && isset( $cachedValues['mime'] ) && isset( $cachedValues['metadata'] ) )
162 - {
163 - if ( $wgUseSharedUploads && $cachedValues['fromShared']) {
164 - # if this is shared file, we need to check if image
165 - # in shared repository has not changed
166 - if ( isset( $keys[1] ) ) {
167 - $commonsCachedValues = $wgMemc->get( $keys[1] );
168 - if (!empty($commonsCachedValues) && is_array($commonsCachedValues)
169 - && isset($commonsCachedValues['version'])
170 - && ( $commonsCachedValues['version'] == MW_IMAGE_VERSION )
171 - && isset($commonsCachedValues['mime'])) {
172 - wfDebug( "Pulling image metadata from shared repository cache\n" );
173 - $this->name = $commonsCachedValues['name'];
174 - $this->imagePath = $commonsCachedValues['imagePath'];
175 - $this->fileExists = $commonsCachedValues['fileExists'];
176 - $this->width = $commonsCachedValues['width'];
177 - $this->height = $commonsCachedValues['height'];
178 - $this->bits = $commonsCachedValues['bits'];
179 - $this->type = $commonsCachedValues['type'];
180 - $this->mime = $commonsCachedValues['mime'];
181 - $this->metadata = $commonsCachedValues['metadata'];
182 - $this->size = $commonsCachedValues['size'];
183 - $this->fromSharedDirectory = true;
184 - $this->dataLoaded = true;
185 - $this->imagePath = $this->getFullPath(true);
186 - }
187 - }
188 - } else {
189 - wfDebug( "Pulling image metadata from local cache\n" );
190 - $this->name = $cachedValues['name'];
191 - $this->imagePath = $cachedValues['imagePath'];
192 - $this->fileExists = $cachedValues['fileExists'];
193 - $this->width = $cachedValues['width'];
194 - $this->height = $cachedValues['height'];
195 - $this->bits = $cachedValues['bits'];
196 - $this->type = $cachedValues['type'];
197 - $this->mime = $cachedValues['mime'];
198 - $this->metadata = $cachedValues['metadata'];
199 - $this->size = $cachedValues['size'];
200 - $this->fromSharedDirectory = false;
201 - $this->dataLoaded = true;
202 - $this->imagePath = $this->getFullPath();
203 - }
204 - }
205 - if ( $this->dataLoaded ) {
206 - wfIncrStats( 'image_cache_hit' );
207 - } else {
208 - wfIncrStats( 'image_cache_miss' );
209 - }
210 -
211 - wfProfileOut( __METHOD__ );
212 - return $this->dataLoaded;
213 - }
214 -
215 - /**
216 - * Save the image metadata to memcached
217 - */
218 - function saveToCache() {
219 - global $wgMemc, $wgUseSharedUploads;
220 - $this->load();
221 - $keys = $this->getCacheKeys();
222 - // We can't cache negative metadata for non-existent files,
223 - // because if the file later appears in commons, the local
224 - // keys won't be purged.
225 - if ( $this->fileExists || !$wgUseSharedUploads ) {
226 - $cachedValues = array(
227 - 'version' => MW_IMAGE_VERSION,
228 - 'name' => $this->name,
229 - 'imagePath' => $this->imagePath,
230 - 'fileExists' => $this->fileExists,
231 - 'fromShared' => $this->fromSharedDirectory,
232 - 'width' => $this->width,
233 - 'height' => $this->height,
234 - 'bits' => $this->bits,
235 - 'type' => $this->type,
236 - 'mime' => $this->mime,
237 - 'metadata' => $this->metadata,
238 - 'size' => $this->size );
239 -
240 - $wgMemc->set( $keys[0], $cachedValues, 60 * 60 * 24 * 7 ); // A week
241 - } else {
242 - // However we should clear them, so they aren't leftover
243 - // if we've deleted the file.
244 - $wgMemc->delete( $keys[0] );
245 - }
246 - }
247 -
248 - /**
249 - * Load metadata from the file itself
250 - */
251 - function loadFromFile() {
252 - global $wgUseSharedUploads, $wgSharedUploadDirectory, $wgContLang;
253 - wfProfileIn( __METHOD__ );
254 - $this->imagePath = $this->getFullPath();
255 - $this->fileExists = file_exists( $this->imagePath );
256 - $this->fromSharedDirectory = false;
257 - $gis = array();
258 -
259 - if (!$this->fileExists) wfDebug(__METHOD__.': '.$this->imagePath." not found locally!\n");
260 -
261 - # If the file is not found, and a shared upload directory is used, look for it there.
262 - if (!$this->fileExists && $wgUseSharedUploads && $wgSharedUploadDirectory) {
263 - # In case we're on a wgCapitalLinks=false wiki, we
264 - # capitalize the first letter of the filename before
265 - # looking it up in the shared repository.
266 - $sharedImage = Image::newFromName( $wgContLang->ucfirst($this->name) );
267 - $this->fileExists = $sharedImage && file_exists( $sharedImage->getFullPath(true) );
268 - if ( $this->fileExists ) {
269 - $this->name = $sharedImage->name;
270 - $this->imagePath = $this->getFullPath(true);
271 - $this->fromSharedDirectory = true;
272 - }
273 - }
274 -
275 -
276 - if ( $this->fileExists ) {
277 - $magic=& MimeMagic::singleton();
278 -
279 - $this->mime = $magic->guessMimeType($this->imagePath,true);
280 - $this->type = $magic->getMediaType($this->imagePath,$this->mime);
281 - $handler = MediaHandler::getHandler( $this->mime );
282 -
283 - # Get size in bytes
284 - $this->size = filesize( $this->imagePath );
285 -
286 - # Height, width and metadata
287 - if ( $handler ) {
288 - $gis = $handler->getImageSize( $this, $this->imagePath );
289 - $this->metadata = $handler->getMetadata( $this, $this->imagePath );
290 - } else {
291 - $gis = false;
292 - $this->metadata = '';
293 - }
294 -
295 - wfDebug(__METHOD__.': '.$this->imagePath." loaded, ".$this->size." bytes, ".$this->mime.".\n");
296 - }
297 - else {
298 - $this->mime = NULL;
299 - $this->type = MEDIATYPE_UNKNOWN;
300 - $this->metadata = '';
301 - wfDebug(__METHOD__.': '.$this->imagePath." NOT FOUND!\n");
302 - }
303 -
304 - if( $gis ) {
305 - $this->width = $gis[0];
306 - $this->height = $gis[1];
307 - } else {
308 - $this->width = 0;
309 - $this->height = 0;
310 - }
311 -
312 - #NOTE: $gis[2] contains a code for the image type. This is no longer used.
313 -
314 - #NOTE: we have to set this flag early to avoid load() to be called
315 - # be some of the functions below. This may lead to recursion or other bad things!
316 - # as ther's only one thread of execution, this should be safe anyway.
317 - $this->dataLoaded = true;
318 -
319 - if ( isset( $gis['bits'] ) ) $this->bits = $gis['bits'];
320 - else $this->bits = 0;
321 -
322 - wfProfileOut( __METHOD__ );
323 - }
324 -
325 - /**
326 - * Load image metadata from the DB
327 - */
328 - function loadFromDB() {
329 - global $wgUseSharedUploads, $wgSharedUploadDBname, $wgSharedUploadDBprefix, $wgContLang;
330 - wfProfileIn( __METHOD__ );
331 -
332 - $dbr = wfGetDB( DB_SLAVE );
333 -
334 - $row = $dbr->selectRow( 'image',
335 - array( 'img_size', 'img_width', 'img_height', 'img_bits',
336 - 'img_media_type', 'img_major_mime', 'img_minor_mime', 'img_metadata' ),
337 - array( 'img_name' => $this->name ), __METHOD__ );
338 - if ( $row ) {
339 - $this->fromSharedDirectory = false;
340 - $this->fileExists = true;
341 - $this->loadFromRow( $row );
342 - $this->imagePath = $this->getFullPath();
343 - // Check for rows from a previous schema, quietly upgrade them
344 - $this->maybeUpgradeRow();
345 - } elseif ( $wgUseSharedUploads && $wgSharedUploadDBname ) {
346 - # In case we're on a wgCapitalLinks=false wiki, we
347 - # capitalize the first letter of the filename before
348 - # looking it up in the shared repository.
349 - $name = $wgContLang->ucfirst($this->name);
350 - $dbc = Image::getCommonsDB();
351 -
352 - $row = $dbc->selectRow( "`$wgSharedUploadDBname`.{$wgSharedUploadDBprefix}image",
353 - array(
354 - 'img_size', 'img_width', 'img_height', 'img_bits',
355 - 'img_media_type', 'img_major_mime', 'img_minor_mime', 'img_metadata' ),
356 - array( 'img_name' => $name ), __METHOD__ );
357 - if ( $row ) {
358 - $this->fromSharedDirectory = true;
359 - $this->fileExists = true;
360 - $this->imagePath = $this->getFullPath(true);
361 - $this->name = $name;
362 - $this->loadFromRow( $row );
363 -
364 - // Check for rows from a previous schema, quietly upgrade them
365 - $this->maybeUpgradeRow();
366 - }
367 - }
368 -
369 - if ( !$row ) {
370 - $this->size = 0;
371 - $this->width = 0;
372 - $this->height = 0;
373 - $this->bits = 0;
374 - $this->type = 0;
375 - $this->fileExists = false;
376 - $this->fromSharedDirectory = false;
377 - $this->metadata = '';
378 - $this->mime = false;
379 - }
380 -
381 - # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
382 - $this->dataLoaded = true;
383 - wfProfileOut( __METHOD__ );
384 - }
385 -
386 - /*
387 - * Load image metadata from a DB result row
388 - */
389 - function loadFromRow( &$row ) {
390 - $this->size = $row->img_size;
391 - $this->width = $row->img_width;
392 - $this->height = $row->img_height;
393 - $this->bits = $row->img_bits;
394 - $this->type = $row->img_media_type;
395 -
396 - $major= $row->img_major_mime;
397 - $minor= $row->img_minor_mime;
398 -
399 - if (!$major) $this->mime = "unknown/unknown";
400 - else {
401 - if (!$minor) $minor= "unknown";
402 - $this->mime = $major.'/'.$minor;
403 - }
404 - $this->metadata = $row->img_metadata;
405 -
406 - $this->dataLoaded = true;
407 - }
408 -
409 - /**
410 - * Load image metadata from cache or DB, unless already loaded
411 - */
412 - function load() {
413 - global $wgSharedUploadDBname, $wgUseSharedUploads;
414 - if ( !$this->dataLoaded ) {
415 - if ( !$this->loadFromCache() ) {
416 - $this->loadFromDB();
417 - if ( !$wgSharedUploadDBname && $wgUseSharedUploads ) {
418 - $this->loadFromFile();
419 - } elseif ( $this->fileExists || !$wgUseSharedUploads ) {
420 - // We can do negative caching for local images, because the cache
421 - // will be purged on upload. But we can't do it when shared images
422 - // are enabled, since updates to that won't purge foreign caches.
423 - $this->saveToCache();
424 - }
425 - }
426 - $this->dataLoaded = true;
427 - }
428 - }
429 -
430 - /**
431 - * Upgrade a row if it needs it
432 - * @return void
433 - */
434 - function maybeUpgradeRow() {
435 - if ( is_null($this->type) || $this->mime == 'image/svg' ) {
436 - $this->upgradeRow();
437 - } else {
438 - $handler = $this->getHandler();
439 - if ( $handler && !$handler->isMetadataValid( $this, $this->metadata ) ) {
440 - $this->upgradeRow();
441 - }
442 - }
443 - }
444 -
445 - /**
446 - * Fix assorted version-related problems with the image row by reloading it from the file
447 - */
448 - function upgradeRow() {
449 - global $wgDBname, $wgSharedUploadDBname;
450 - wfProfileIn( __METHOD__ );
451 -
452 - $this->loadFromFile();
453 -
454 - if ( $this->fromSharedDirectory ) {
455 - if ( !$wgSharedUploadDBname ) {
456 - wfProfileOut( __METHOD__ );
457 - return;
458 - }
459 -
460 - // Write to the other DB using selectDB, not database selectors
461 - // This avoids breaking replication in MySQL
462 - $dbw = Image::getCommonsDB();
463 - } else {
464 - $dbw = wfGetDB( DB_MASTER );
465 - }
466 -
467 - list( $major, $minor ) = self::splitMime( $this->mime );
468 -
469 - wfDebug(__METHOD__.': upgrading '.$this->name." to the current schema\n");
470 -
471 - $dbw->update( 'image',
472 - array(
473 - 'img_width' => $this->width,
474 - 'img_height' => $this->height,
475 - 'img_bits' => $this->bits,
476 - 'img_media_type' => $this->type,
477 - 'img_major_mime' => $major,
478 - 'img_minor_mime' => $minor,
479 - 'img_metadata' => $this->metadata,
480 - ), array( 'img_name' => $this->name ), __METHOD__
481 - );
482 - if ( $this->fromSharedDirectory ) {
483 - $dbw->selectDB( $wgDBname );
484 - }
485 - wfProfileOut( __METHOD__ );
486 - }
487 -
488 - /**
489 - * Split an internet media type into its two components; if not
490 - * a two-part name, set the minor type to 'unknown'.
491 - *
492 - * @param string $mime "text/html" etc
493 - * @return array ("text", "html") etc
494 - */
495 - static function splitMime( $mime ) {
496 - if( strpos( $mime, '/' ) !== false ) {
497 - return explode( '/', $mime, 2 );
498 - } else {
499 - return array( $mime, 'unknown' );
500 - }
501 - }
502 -
503 - /**
504 - * Return the name of this image
505 - * @public
506 - */
507 - function getName() {
508 - return $this->name;
509 - }
510 -
511 - /**
512 - * Return the associated title object
513 - * @public
514 - */
515 - function getTitle() {
516 - return $this->title;
517 - }
518 -
519 - /**
520 - * Return the URL of the image file
521 - * @public
522 - */
523 - function getURL() {
524 - if ( !$this->url ) {
525 - $this->load();
526 - if($this->fileExists) {
527 - $this->url = Image::imageUrl( $this->name, $this->fromSharedDirectory );
528 - } else {
529 - $this->url = '';
530 - }
531 - }
532 - return $this->url;
533 - }
534 -
535 - function getViewURL() {
536 - if( $this->mustRender()) {
537 - if( $this->canRender() ) {
538 - return $this->createThumb( $this->getWidth() );
539 - }
540 - else {
541 - wfDebug('Image::getViewURL(): supposed to render '.$this->name.' ('.$this->mime."), but can't!\n");
542 - return $this->getURL(); #hm... return NULL?
543 - }
544 - } else {
545 - return $this->getURL();
546 - }
547 - }
548 -
549 - /**
550 - * Return the image path of the image in the
551 - * local file system as an absolute path
552 - * @public
553 - */
554 - function getImagePath() {
555 - $this->load();
556 - return $this->imagePath;
557 - }
558 -
559 - /**
560 - * @return mixed Return the width of the image; returns false on error.
561 - * @param int $page Page number to find the width of.
562 - * @public
563 - */
564 - function getWidth( $page = 1 ) {
565 - $this->load();
566 - if ( $this->isMultipage() ) {
567 - $dim = $this->getHandler()->getPageDimensions( $this, $page );
568 - if ( $dim ) {
569 - return $dim['width'];
570 - } else {
571 - return false;
572 - }
573 - } else {
574 - return $this->width;
575 - }
576 - }
577 -
578 - /**
579 - * @return mixed Return the height of the image; Returns false on error.
580 - * @param int $page Page number to find the height of.
581 - * @public
582 - */
583 - function getHeight( $page = 1 ) {
584 - $this->load();
585 - if ( $this->isMultipage() ) {
586 - $dim = $this->getHandler()->getPageDimensions( $this, $page );
587 - if ( $dim ) {
588 - return $dim['height'];
589 - } else {
590 - return false;
591 - }
592 - } else {
593 - return $this->height;
594 - }
595 - }
596 -
597 - /**
598 - * Get handler-specific metadata
599 - */
600 - function getMetadata() {
601 - $this->load();
602 - return $this->metadata;
603 - }
604 -
605 - /**
606 - * @return int the size of the image file, in bytes
607 - * @public
608 - */
609 - function getSize() {
610 - $this->load();
611 - return $this->size;
612 - }
613 -
614 - /**
615 - * @return string the mime type of the file.
616 - */
617 - function getMimeType() {
618 - $this->load();
619 - return $this->mime;
620 - }
621 -
622 - /**
623 - * Return the type of the media in the file.
624 - * Use the value returned by this function with the MEDIATYPE_xxx constants.
625 - */
626 - function getMediaType() {
627 - $this->load();
628 - return $this->type;
629 - }
630 -
631 - /**
632 - * Checks if the file can be presented to the browser as a bitmap.
633 - *
634 - * Currently, this checks if the file is an image format
635 - * that can be converted to a format
636 - * supported by all browsers (namely GIF, PNG and JPEG),
637 - * or if it is an SVG image and SVG conversion is enabled.
638 - *
639 - * @todo remember the result of this check.
640 - * @return boolean
641 - */
642 - function canRender() {
643 - $handler = $this->getHandler();
644 - return $handler && $handler->canRender();
645 - }
646 -
647 - /**
648 - * Return true if the file is of a type that can't be directly
649 - * rendered by typical browsers and needs to be re-rasterized.
650 - *
651 - * This returns true for everything but the bitmap types
652 - * supported by all browsers, i.e. JPEG; GIF and PNG. It will
653 - * also return true for any non-image formats.
654 - *
655 - * @return bool
656 - */
657 - function mustRender() {
658 - $handler = $this->getHandler();
659 - return $handler && $handler->mustRender();
660 - }
661 -
662 - /**
663 - * Determines if this media file may be shown inline on a page.
664 - *
665 - * This is currently synonymous to canRender(), but this could be
666 - * extended to also allow inline display of other media,
667 - * like flash animations or videos. If you do so, please keep in mind that
668 - * that could be a security risk.
669 - */
670 - function allowInlineDisplay() {
671 - return $this->canRender();
672 - }
673 -
674 - /**
675 - * Determines if this media file is in a format that is unlikely to
676 - * contain viruses or malicious content. It uses the global
677 - * $wgTrustedMediaFormats list to determine if the file is safe.
678 - *
679 - * This is used to show a warning on the description page of non-safe files.
680 - * It may also be used to disallow direct [[media:...]] links to such files.
681 - *
682 - * Note that this function will always return true if allowInlineDisplay()
683 - * or isTrustedFile() is true for this file.
684 - *
685 - * @return boolean
686 - */
687 - function isSafeFile() {
688 - if ($this->allowInlineDisplay()) return true;
689 - if ($this->isTrustedFile()) return true;
690 -
691 - global $wgTrustedMediaFormats;
692 -
693 - $type= $this->getMediaType();
694 - $mime= $this->getMimeType();
695 - #wfDebug("Image::isSafeFile: type= $type, mime= $mime\n");
696 -
697 - if (!$type || $type===MEDIATYPE_UNKNOWN) return false; #unknown type, not trusted
698 - if ( in_array( $type, $wgTrustedMediaFormats) ) return true;
699 -
700 - if ($mime==="unknown/unknown") return false; #unknown type, not trusted
701 - if ( in_array( $mime, $wgTrustedMediaFormats) ) return true;
702 -
703 - return false;
704 - }
705 -
706 - /**
707 - * Returns true if the file is flagged as trusted. Files flagged that way
708 - * can be linked to directly, even if that is not allowed for this type of
709 - * file normally.
710 - *
711 - * This is a dummy function right now and always returns false. It could be
712 - * implemented to extract a flag from the database. The trusted flag could be
713 - * set on upload, if the user has sufficient privileges, to bypass script-
714 - * and html-filters. It may even be coupled with cryptographics signatures
715 - * or such.
716 - * @return boolean
717 - */
718 - function isTrustedFile() {
719 - #this could be implemented to check a flag in the database,
720 - #look for signatures, etc
721 - return false;
722 - }
723 -
724 - /**
725 - * Return the escapeLocalURL of this image
726 - * @param string $query URL query string
727 - * @public
728 - */
729 - function getEscapeLocalURL( $query=false) {
730 - return $this->getTitle()->escapeLocalURL( $query );
731 - }
732 -
733 - /**
734 - * Return the escapeFullURL of this image
735 - * @public
736 - */
737 - function getEscapeFullURL() {
738 - $this->getTitle();
739 - return $this->title->escapeFullURL();
740 - }
741 -
742 - /**
743 - * Return the URL of an image, provided its name.
744 - *
745 - * @param string $name Name of the image, without the leading "Image:"
746 - * @param boolean $fromSharedDirectory Should this be in $wgSharedUploadPath?
747 - * @return string URL of $name image
748 - * @public
749 - */
750 - static function imageUrl( $name, $fromSharedDirectory = false ) {
751 - global $wgUploadPath,$wgUploadBaseUrl,$wgSharedUploadPath;
752 - if($fromSharedDirectory) {
753 - $base = '';
754 - $path = $wgSharedUploadPath;
755 - } else {
756 - $base = $wgUploadBaseUrl;
757 - $path = $wgUploadPath;
758 - }
759 - $url = "{$base}{$path}" . wfGetHashPath($name, $fromSharedDirectory) . "{$name}";
760 - return wfUrlencode( $url );
761 - }
762 -
763 - /**
764 - * Returns true if the image file exists on disk.
765 - * @return boolean Whether image file exist on disk.
766 - * @public
767 - */
768 - function exists() {
769 - $this->load();
770 - return $this->fileExists;
771 - }
772 -
773 - /**
774 - * @todo document
775 - * @param string $thumbName
776 - * @param string $subdir
777 - * @return string
778 - * @private
779 - */
780 - function thumbUrlFromName( $thumbName, $subdir = 'thumb' ) {
781 - global $wgUploadPath, $wgUploadBaseUrl, $wgSharedUploadPath;
782 - if($this->fromSharedDirectory) {
783 - $base = '';
784 - $path = $wgSharedUploadPath;
785 - } else {
786 - $base = $wgUploadBaseUrl;
787 - $path = $wgUploadPath;
788 - }
789 - if ( Image::isHashed( $this->fromSharedDirectory ) ) {
790 - $hashdir = wfGetHashPath($this->name, $this->fromSharedDirectory) .
791 - wfUrlencode( $this->name );
792 - } else {
793 - $hashdir = '';
794 - }
795 - $url = "{$base}{$path}/{$subdir}{$hashdir}/" . wfUrlencode( $thumbName );
796 - return $url;
797 - }
798 -
799 - /**
800 - * @deprecated Use $image->transform()->getUrl() or thumbUrlFromName()
801 - */
802 - function thumbUrl( $width, $subdir = 'thumb' ) {
803 - $name = $this->thumbName( array( 'width' => $width ) );
804 - if ( strval( $name ) !== '' ) {
805 - return array( false, $this->thumbUrlFromName( $name, $subdir ) );
806 - } else {
807 - return array( false, false );
808 - }
809 - }
810 -
811 - /**
812 - * @return mixed
813 - */
814 - function getTransformScript() {
815 - global $wgSharedThumbnailScriptPath, $wgThumbnailScriptPath;
816 - if ( $this->fromSharedDirectory ) {
817 - $script = $wgSharedThumbnailScriptPath;
818 - } else {
819 - $script = $wgThumbnailScriptPath;
820 - }
821 - if ( $script ) {
822 - return "$script?f=" . urlencode( $this->name );
823 - } else {
824 - return false;
825 - }
826 - }
827 -
828 - /**
829 - * Get a ThumbnailImage which is the same size as the source
830 - * @param mixed $page
831 - * @return MediaTransformOutput
832 - */
833 - function getUnscaledThumb( $page = false ) {
834 - if ( $page ) {
835 - $params = array(
836 - 'page' => $page,
837 - 'width' => $this->getWidth( $page )
838 - );
839 - } else {
840 - $params = array( 'width' => $this->getWidth() );
841 - }
842 - return $this->transform( $params );
843 - }
844 -
845 - /**
846 - * Return the file name of a thumbnail with the specified parameters
847 - *
848 - * @param array $params Handler-specific parameters
849 - * @return string file name of a thumbnail with the specified parameters
850 - * @private
851 - */
852 - function thumbName( $params ) {
853 - $handler = $this->getHandler();
854 - if ( !$handler ) {
855 - return null;
856 - }
857 - list( $thumbExt, /* $thumbMime */ ) = self::getThumbType( $this->extension, $this->mime );
858 - $thumbName = $handler->makeParamString( $params ) . '-' . $this->name;
859 - if ( $thumbExt != $this->extension ) {
860 - $thumbName .= ".$thumbExt";
861 - }
862 - return $thumbName;
863 - }
864 -
865 - /**
866 - * Create a thumbnail of the image having the specified width/height.
867 - * The thumbnail will not be created if the width is larger than the
868 - * image's width. Let the browser do the scaling in this case.
869 - * The thumbnail is stored on disk and is only computed if the thumbnail
870 - * file does not exist OR if it is older than the image.
871 - * Returns the URL.
872 - *
873 - * Keeps aspect ratio of original image. If both width and height are
874 - * specified, the generated image will be no bigger than width x height,
875 - * and will also have correct aspect ratio.
876 - *
877 - * @param integer $width maximum width of the generated thumbnail
878 - * @param integer $height maximum height of the image (optional)
879 - * @public
880 - */
881 - function createThumb( $width, $height = -1 ) {
882 - $params = array( 'width' => $width );
883 - if ( $height != -1 ) {
884 - $params['height'] = $height;
885 - }
886 - $thumb = $this->transform( $params );
887 - if( is_null( $thumb ) || $thumb->isError() ) return '';
888 - return $thumb->getUrl();
889 - }
890 -
891 - /**
892 - * As createThumb, but returns a ThumbnailImage object. This can
893 - * provide access to the actual file, the real size of the thumb,
894 - * and can produce a convenient <img> tag for you.
895 - *
896 - * For non-image formats, this may return a filetype-specific icon.
897 - *
898 - * @param integer $width maximum width of the generated thumbnail
899 - * @param integer $height maximum height of the image (optional)
900 - * @param boolean $render True to render the thumbnail if it doesn't exist,
901 - * false to just return the URL
902 - *
903 - * @return ThumbnailImage or null on failure
904 - * @public
905 - *
906 - * @deprecated use transform()
907 - */
908 - function getThumbnail( $width, $height=-1, $render = true ) {
909 - $params = array( 'width' => $width );
910 - if ( $height != -1 ) {
911 - $params['height'] = $height;
912 - }
913 - $flags = $render ? self::RENDER_NOW : 0;
914 - return $this->transform( $params, $flags );
915 - }
916 -
917 - /**
918 - * Transform a media file
919 - *
920 - * @param array[string]mixed $params An associative array of handler-specific parameters.
921 - * Typical keys are width, height and page.
922 - * @param integer $flags A bitfield, may contain self::RENDER_NOW to force rendering
923 - * @return MediaTransformOutput
924 - */
925 - function transform( $params, $flags = 0 ) {
926 - global $wgGenerateThumbnailOnParse, $wgUseSquid, $wgIgnoreImageErrors;
927 -
928 - wfProfileIn( __METHOD__ );
929 - do {
930 - $handler = $this->getHandler();
931 - if ( !$handler || !$handler->canRender() ) {
932 - // not a bitmap or renderable image, don't try.
933 - $thumb = $this->iconThumb();
934 - break;
935 - }
936 -
937 - $script = $this->getTransformScript();
938 - if ( $script && !($flags & self::RENDER_NOW) ) {
939 - // Use a script to transform on client request
940 - $thumb = $handler->getScriptedTransform( $this, $script, $params );
941 - break;
942 - }
943 -
944 - $normalisedParams = $params;
945 - $handler->normaliseParams( $this, $normalisedParams );
946 - $thumbName = $this->thumbName( $normalisedParams );
947 - $thumbPath = wfImageThumbDir( $this->name, $this->fromSharedDirectory ) . "/$thumbName";
948 - $thumbUrl = $this->thumbUrlFromName( $thumbName );
949 -
950 -
951 - if ( !$wgGenerateThumbnailOnParse && !($flags & self::RENDER_NOW ) ) {
952 - $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
953 - break;
954 - }
955 -
956 - wfDebug( "Doing stat for $thumbPath\n" );
957 - $this->migrateThumbFile( $thumbName );
958 - if ( file_exists( $thumbPath ) ) {
959 - $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
960 - break;
961 - }
962 -
963 - $thumb = $handler->doTransform( $this, $thumbPath, $thumbUrl, $params );
964 -
965 - // Ignore errors if requested
966 - if ( !$thumb ) {
967 - $thumb = null;
968 - } elseif ( $thumb->isError() ) {
969 - $this->lastError = $thumb->toText();
970 - if ( $wgIgnoreImageErrors && !($flags & self::RENDER_NOW) ) {
971 - $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
972 - }
973 - }
974 -
975 - if ( $wgUseSquid ) {
976 - wfPurgeSquidServers( array( $thumbUrl ) );
977 - }
978 - } while (false);
979 -
980 - wfProfileOut( __METHOD__ );
981 - return $thumb;
982 - }
983 -
984 - /**
985 - * Fix thumbnail files from 1.4 or before, with extreme prejudice
986 - * @param string $thumbName File name of thumbnail.
987 - * @return void
988 - */
989 - function migrateThumbFile( $thumbName ) {
990 - $thumbDir = wfImageThumbDir( $this->name, $this->fromSharedDirectory );
991 - $thumbPath = "$thumbDir/$thumbName";
992 - if ( is_dir( $thumbPath ) ) {
993 - // Directory where file should be
994 - // This happened occasionally due to broken migration code in 1.5
995 - // Rename to broken-*
996 - global $wgUploadDirectory;
997 - for ( $i = 0; $i < 100 ; $i++ ) {
998 - $broken = "$wgUploadDirectory/broken-$i-$thumbName";
999 - if ( !file_exists( $broken ) ) {
1000 - rename( $thumbPath, $broken );
1001 - break;
1002 - }
1003 - }
1004 - // Doesn't exist anymore
1005 - clearstatcache();
1006 - }
1007 - if ( is_file( $thumbDir ) ) {
1008 - // File where directory should be
1009 - unlink( $thumbDir );
1010 - // Doesn't exist anymore
1011 - clearstatcache();
1012 - }
1013 - }
1014 -
1015 - /**
1016 - * Get a MediaHandler instance for this image
1017 - */
1018 - function getHandler() {
1019 - return MediaHandler::getHandler( $this->getMimeType() );
1020 - }
1021 -
1022 - /**
1023 - * Get a ThumbnailImage representing a file type icon
1024 - * @return ThumbnailImage
1025 - */
1026 - function iconThumb() {
1027 - global $wgStylePath, $wgStyleDirectory;
1028 -
1029 - $icons = array( 'fileicon-' . $this->extension . '.png', 'fileicon.png' );
1030 - foreach( $icons as $icon ) {
1031 - $path = '/common/images/icons/' . $icon;
1032 - $filepath = $wgStyleDirectory . $path;
1033 - if( file_exists( $filepath ) ) {
1034 - return new ThumbnailImage( $wgStylePath . $path, 120, 120 );
1035 - }
1036 - }
1037 - return null;
1038 - }
1039 -
1040 - /**
1041 - * Get last thumbnailing error.
1042 - * Largely obsolete.
1043 - * @return mixed
1044 - */
1045 - function getLastError() {
1046 - return $this->lastError;
1047 - }
1048 -
1049 - /**
1050 - * Get all thumbnail names previously generated for this image
1051 - * @param boolean $shared
1052 - * @return array[]string
1053 - */
1054 - function getThumbnails( $shared = false ) {
1055 - if ( Image::isHashed( $shared ) ) {
1056 - $this->load();
1057 - $files = array();
1058 - $dir = wfImageThumbDir( $this->name, $shared );
1059 -
1060 - if ( is_dir( $dir ) ) {
1061 - $handle = opendir( $dir );
1062 -
1063 - if ( $handle ) {
1064 - while ( false !== ( $file = readdir($handle) ) ) {
1065 - if ( $file[0] != '.' ) {
1066 - $files[] = $file;
1067 - }
1068 - }
1069 - closedir( $handle );
1070 - }
1071 - }
1072 - } else {
1073 - $files = array();
1074 - }
1075 -
1076 - return $files;
1077 - }
1078 -
1079 - /**
1080 - * Refresh metadata in memcached, but don't touch thumbnails or squid
1081 - * @return void
1082 - */
1083 - function purgeMetadataCache() {
1084 - clearstatcache();
1085 - $this->loadFromFile();
1086 - $this->saveToCache();
1087 - }
1088 -
1089 - /**
1090 - * Delete all previously generated thumbnails, refresh metadata in memcached and purge the squid
1091 - * @param array $archiveFiles
1092 - * @param boolean $shared
1093 - * @return void
1094 - */
1095 - function purgeCache( $archiveFiles = array(), $shared = false ) {
1096 - global $wgUseSquid;
1097 -
1098 - // Refresh metadata cache
1099 - $this->purgeMetadataCache();
1100 -
1101 - // Delete thumbnails
1102 - $files = $this->getThumbnails( $shared );
1103 - $dir = wfImageThumbDir( $this->name, $shared );
1104 - $urls = array();
1105 - foreach ( $files as $file ) {
1106 - # Check that the base image name is part of the thumb name
1107 - # This is a basic sanity check to avoid erasing unrelated directories
1108 - if ( strpos( $file, $this->name ) !== false ) {
1109 - $url = $this->thumbUrlFromName( $file );
1110 - $urls[] = $url;
1111 - @unlink( "$dir/$file" );
1112 - }
1113 - }
1114 -
1115 - // Purge the squid
1116 - if ( $wgUseSquid ) {
1117 - $urls[] = $this->getURL();
1118 - foreach ( $archiveFiles as $file ) {
1119 - $urls[] = wfImageArchiveUrl( $file );
1120 - }
1121 - wfPurgeSquidServers( $urls );
1122 - }
1123 - }
1124 -
1125 - /**
1126 - * Purge the image description page, but don't go after
1127 - * pages using the image. Use when modifying file history
1128 - * but not the current data.
1129 - * @return void
1130 - */
1131 - function purgeDescription() {
1132 - $page = Title::makeTitle( NS_IMAGE, $this->name );
1133 - $page->invalidateCache();
1134 - $page->purgeSquid();
1135 - }
1136 -
1137 - /**
1138 - * Purge metadata and all affected pages when the image is created,
1139 - * deleted, or majorly updated.
1140 - * @param array $urlArray A set of additional URLs may be passed to purge,
1141 - * such as specific image files which have changed (param not used?)
1142 - * @return void
1143 - */
1144 - function purgeEverything( $urlArr=array() ) {
1145 - // Delete thumbnails and refresh image metadata cache
1146 - $this->purgeCache();
1147 - $this->purgeDescription();
1148 -
1149 - // Purge cache of all pages using this image
1150 - $update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' );
1151 - $update->doUpdate();
1152 - }
1153 -
1154 - /**
1155 - * Return the image history of this image, line by line.
1156 - * starts with current version, then old versions.
1157 - * uses $this->historyLine to check which line to return:
1158 - * 0 return line for current version
1159 - * 1 query for old versions, return first one
1160 - * 2, ... return next old version from above query
1161 - *
1162 - * @public
1163 - * @return mixed false on no next history, object otherwise.
1164 - */
1165 - function nextHistoryLine() {
1166 - $dbr = wfGetDB( DB_SLAVE );
1167 -
1168 - if ( $this->historyLine == 0 ) {// called for the first time, return line from cur
1169 - $this->historyRes = $dbr->select( 'image',
1170 - array(
1171 - 'img_size',
1172 - 'img_description',
1173 - 'img_user','img_user_text',
1174 - 'img_timestamp',
1175 - 'img_width',
1176 - 'img_height',
1177 - "'' AS oi_archive_name"
1178 - ),
1179 - array( 'img_name' => $this->title->getDBkey() ),
1180 - __METHOD__
1181 - );
1182 - if ( 0 == $dbr->numRows( $this->historyRes ) ) {
1183 - return FALSE;
1184 - }
1185 - } else if ( $this->historyLine == 1 ) {
1186 - $this->historyRes = $dbr->select( 'oldimage',
1187 - array(
1188 - 'oi_size AS img_size',
1189 - 'oi_description AS img_description',
1190 - 'oi_user AS img_user',
1191 - 'oi_user_text AS img_user_text',
1192 - 'oi_timestamp AS img_timestamp',
1193 - 'oi_width as img_width',
1194 - 'oi_height as img_height',
1195 - 'oi_archive_name'
1196 - ),
1197 - array( 'oi_name' => $this->title->getDBkey() ),
1198 - __METHOD__,
1199 - array( 'ORDER BY' => 'oi_timestamp DESC' )
1200 - );
1201 - }
1202 - $this->historyLine ++;
1203 -
1204 - return $dbr->fetchObject( $this->historyRes );
1205 - }
1206 -
1207 - /**
1208 - * Reset the history pointer to the first element of the history
1209 - * @public
1210 - * @return void
1211 - */
1212 - function resetHistory() {
1213 - $this->historyLine = 0;
1214 - }
1215 -
1216 - /**
1217 - * Return the full filesystem path to the file. Note that this does
1218 - * not mean that a file actually exists under that location.
1219 - *
1220 - * This path depends on whether directory hashing is active or not,
1221 - * i.e. whether the images are all found in the same directory,
1222 - * or in hashed paths like /images/3/3c.
1223 - *
1224 - * @public
1225 - * @param boolean $fromSharedDirectory Return the path to the file
1226 - * in a shared repository (see $wgUseSharedRepository and related
1227 - * options in DefaultSettings.php) instead of a local one.
1228 - * @return string Full filesystem path to the file.
1229 - */
1230 - function getFullPath( $fromSharedRepository = false ) {
1231 - global $wgUploadDirectory, $wgSharedUploadDirectory;
1232 -
1233 - $dir = $fromSharedRepository ? $wgSharedUploadDirectory :
1234 - $wgUploadDirectory;
1235 -
1236 - // $wgSharedUploadDirectory may be false, if thumb.php is used
1237 - if ( $dir ) {
1238 - $fullpath = $dir . wfGetHashPath($this->name, $fromSharedRepository) . $this->name;
1239 - } else {
1240 - $fullpath = false;
1241 - }
1242 -
1243 - return $fullpath;
1244 - }
1245 -
1246 - /**
1247 - * @param boolean $shared
1248 - * @return bool
1249 - */
1250 - public static function isHashed( $shared ) {
1251 - global $wgHashedUploadDirectory, $wgHashedSharedUploadDirectory;
1252 - return $shared ? $wgHashedSharedUploadDirectory : $wgHashedUploadDirectory;
1253 - }
1254 -
1255 - /**
1256 - * Record an image upload in the upload log and the image table
1257 - * @param string $oldver
1258 - * @param string $desc
1259 - * @param string $license
1260 - * @param string $copyStatus
1261 - * @param string $source
1262 - * @param boolean $watch
1263 - * @return boolean
1264 - */
1265 - function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', $watch = false ) {
1266 - global $wgUser, $wgUseCopyrightUpload;
1267 -
1268 - $dbw = wfGetDB( DB_MASTER );
1269 -
1270 - // Delete thumbnails and refresh the metadata cache
1271 - $this->purgeCache();
1272 -
1273 - // Fail now if the image isn't there
1274 - if ( !$this->fileExists || $this->fromSharedDirectory ) {
1275 - wfDebug( "Image::recordUpload: File ".$this->imagePath." went missing!\n" );
1276 - return false;
1277 - }
1278 -
1279 - if ( $wgUseCopyrightUpload ) {
1280 - if ( $license != '' ) {
1281 - $licensetxt = '== ' . wfMsgForContent( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
1282 - }
1283 - $textdesc = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $desc . "\n" .
1284 - '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" .
1285 - "$licensetxt" .
1286 - '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ;
1287 - } else {
1288 - if ( $license != '' ) {
1289 - $filedesc = $desc == '' ? '' : '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $desc . "\n";
1290 - $textdesc = $filedesc .
1291 - '== ' . wfMsgForContent ( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
1292 - } else {
1293 - $textdesc = $desc;
1294 - }
1295 - }
1296 -
1297 - $now = $dbw->timestamp();
1298 -
1299 - #split mime type
1300 - if (strpos($this->mime,'/')!==false) {
1301 - list($major,$minor)= explode('/',$this->mime,2);
1302 - }
1303 - else {
1304 - $major= $this->mime;
1305 - $minor= "unknown";
1306 - }
1307 -
1308 - # Test to see if the row exists using INSERT IGNORE
1309 - # This avoids race conditions by locking the row until the commit, and also
1310 - # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
1311 - $dbw->insert( 'image',
1312 - array(
1313 - 'img_name' => $this->name,
1314 - 'img_size'=> $this->size,
1315 - 'img_width' => intval( $this->width ),
1316 - 'img_height' => intval( $this->height ),
1317 - 'img_bits' => $this->bits,
1318 - 'img_media_type' => $this->type,
1319 - 'img_major_mime' => $major,
1320 - 'img_minor_mime' => $minor,
1321 - 'img_timestamp' => $now,
1322 - 'img_description' => $desc,
1323 - 'img_user' => $wgUser->getID(),
1324 - 'img_user_text' => $wgUser->getName(),
1325 - 'img_metadata' => $this->metadata,
1326 - ),
1327 - __METHOD__,
1328 - 'IGNORE'
1329 - );
1330 -
1331 - if( $dbw->affectedRows() == 0 ) {
1332 - # Collision, this is an update of an image
1333 - # Insert previous contents into oldimage
1334 - $dbw->insertSelect( 'oldimage', 'image',
1335 - array(
1336 - 'oi_name' => 'img_name',
1337 - 'oi_archive_name' => $dbw->addQuotes( $oldver ),
1338 - 'oi_size' => 'img_size',
1339 - 'oi_width' => 'img_width',
1340 - 'oi_height' => 'img_height',
1341 - 'oi_bits' => 'img_bits',
1342 - 'oi_timestamp' => 'img_timestamp',
1343 - 'oi_description' => 'img_description',
1344 - 'oi_user' => 'img_user',
1345 - 'oi_user_text' => 'img_user_text',
1346 - ), array( 'img_name' => $this->name ), __METHOD__
1347 - );
1348 -
1349 - # Update the current image row
1350 - $dbw->update( 'image',
1351 - array( /* SET */
1352 - 'img_size' => $this->size,
1353 - 'img_width' => intval( $this->width ),
1354 - 'img_height' => intval( $this->height ),
1355 - 'img_bits' => $this->bits,
1356 - 'img_media_type' => $this->type,
1357 - 'img_major_mime' => $major,
1358 - 'img_minor_mime' => $minor,
1359 - 'img_timestamp' => $now,
1360 - 'img_description' => $desc,
1361 - 'img_user' => $wgUser->getID(),
1362 - 'img_user_text' => $wgUser->getName(),
1363 - 'img_metadata' => $this->metadata,
1364 - ), array( /* WHERE */
1365 - 'img_name' => $this->name
1366 - ), __METHOD__
1367 - );
1368 - } else {
1369 - # This is a new image
1370 - # Update the image count
1371 - $site_stats = $dbw->tableName( 'site_stats' );
1372 - $dbw->query( "UPDATE $site_stats SET ss_images=ss_images+1", __METHOD__ );
1373 - }
1374 -
1375 - $descTitle = $this->getTitle();
1376 - $article = new Article( $descTitle );
1377 - $minor = false;
1378 - $watch = $watch || $wgUser->isWatched( $descTitle );
1379 - $suppressRC = true; // There's already a log entry, so don't double the RC load
1380 -
1381 - if( $descTitle->exists() ) {
1382 - // TODO: insert a null revision into the page history for this update.
1383 - if( $watch ) {
1384 - $wgUser->addWatch( $descTitle );
1385 - }
1386 -
1387 - # Invalidate the cache for the description page
1388 - $descTitle->invalidateCache();
1389 - $descTitle->purgeSquid();
1390 - } else {
1391 - // New image; create the description page.
1392 - $article->insertNewArticle( $textdesc, $desc, $minor, $watch, $suppressRC );
1393 - }
1394 -
1395 - # Hooks, hooks, the magic of hooks...
1396 - wfRunHooks( 'FileUpload', array( $this ) );
1397 -
1398 - # Add the log entry
1399 - $log = new LogPage( 'upload' );
1400 - $log->addEntry( 'upload', $descTitle, $desc );
1401 -
1402 - # Commit the transaction now, in case something goes wrong later
1403 - # The most important thing is that images don't get lost, especially archives
1404 - $dbw->immediateCommit();
1405 -
1406 - # Invalidate cache for all pages using this image
1407 - $update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' );
1408 - $update->doUpdate();
1409 -
1410 - return true;
1411 - }
1412 -
1413 - /**
1414 - * Get an array of Title objects which are articles which use this image
1415 - * Also adds their IDs to the link cache
1416 - *
1417 - * This is mostly copied from Title::getLinksTo()
1418 - *
1419 - * @deprecated Use HTMLCacheUpdate, this function uses too much memory
1420 - * @param string $options
1421 - * @return array[int]Title
1422 - */
1423 - function getLinksTo( $options = '' ) {
1424 - wfProfileIn( __METHOD__ );
1425 -
1426 - if ( $options ) {
1427 - $db = wfGetDB( DB_MASTER );
1428 - } else {
1429 - $db = wfGetDB( DB_SLAVE );
1430 - }
1431 - $linkCache =& LinkCache::singleton();
1432 -
1433 - list( $page, $imagelinks ) = $db->tableNamesN( 'page', 'imagelinks' );
1434 - $encName = $db->addQuotes( $this->name );
1435 - $sql = "SELECT page_namespace,page_title,page_id FROM $page,$imagelinks WHERE page_id=il_from AND il_to=$encName $options";
1436 - $res = $db->query( $sql, __METHOD__ );
1437 -
1438 - $retVal = array();
1439 - if ( $db->numRows( $res ) ) {
1440 - while ( $row = $db->fetchObject( $res ) ) {
1441 - if ( $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ) ) {
1442 - $linkCache->addGoodLinkObj( $row->page_id, $titleObj );
1443 - $retVal[] = $titleObj;
1444 - }
1445 - }
1446 - }
1447 - $db->freeResult( $res );
1448 - wfProfileOut( __METHOD__ );
1449 - return $retVal;
1450 - }
1451 -
1452 - /**
1453 - * @return array
1454 - */
1455 - function getExifData() {
1456 - $handler = $this->getHandler();
1457 - if ( !$handler || $handler->getMetadataType( $this ) != 'exif' ) {
1458 - return array();
1459 - }
1460 - if ( !$this->metadata ) {
1461 - return array();
1462 - }
1463 - $exif = unserialize( $this->metadata );
1464 - if ( !$exif ) {
1465 - return array();
1466 - }
1467 - unset( $exif['MEDIAWIKI_EXIF_VERSION'] );
1468 - $format = new FormatExif( $exif );
1469 -
1470 - return $format->getFormattedData();
1471 - }
1472 -
1473 - /**
1474 - * Returns true if the image does not come from the shared
1475 - * image repository.
1476 - *
1477 - * @return bool
1478 - */
1479 - function isLocal() {
1480 - return !$this->fromSharedDirectory;
1481 - }
1482 -
1483 - /**
1484 - * Was this image ever deleted from the wiki?
1485 - *
1486 - * @return bool
1487 - */
1488 - function wasDeleted() {
1489 - $title = Title::makeTitle( NS_IMAGE, $this->name );
1490 - return ( $title->isDeleted() > 0 );
1491 - }
1492 -
1493 - /**
1494 - * Delete all versions of the image.
1495 - *
1496 - * Moves the files into an archive directory (or deletes them)
1497 - * and removes the database rows.
1498 - *
1499 - * Cache purging is done; logging is caller's responsibility.
1500 - *
1501 - * @param string $reason
1502 - * @param boolean $suppress
1503 - * @return boolean true on success, false on some kind of failure
1504 - */
1505 - function delete( $reason, $suppress=false ) {
1506 - $transaction = new FSTransaction();
1507 - $urlArr = array( $this->getURL() );
1508 -
1509 - if( !FileStore::lock() ) {
1510 - wfDebug( __METHOD__.": failed to acquire file store lock, aborting\n" );
1511 - return false;
1512 - }
1513 -
1514 - try {
1515 - $dbw = wfGetDB( DB_MASTER );
1516 - $dbw->begin();
1517 -
1518 - // Delete old versions
1519 - $result = $dbw->select( 'oldimage',
1520 - array( 'oi_archive_name' ),
1521 - array( 'oi_name' => $this->name ) );
1522 -
1523 - while( $row = $dbw->fetchObject( $result ) ) {
1524 - $oldName = $row->oi_archive_name;
1525 -
1526 - $transaction->add( $this->prepareDeleteOld( $oldName, $reason, $suppress ) );
1527 -
1528 - // We'll need to purge this URL from caches...
1529 - $urlArr[] = wfImageArchiveUrl( $oldName );
1530 - }
1531 - $dbw->freeResult( $result );
1532 -
1533 - // And the current version...
1534 - $transaction->add( $this->prepareDeleteCurrent( $reason, $suppress ) );
1535 -
1536 - $dbw->immediateCommit();
1537 - } catch( MWException $e ) {
1538 - wfDebug( __METHOD__.": db error, rolling back file transactions\n" );
1539 - $transaction->rollback();
1540 - FileStore::unlock();
1541 - throw $e;
1542 - }
1543 -
1544 - wfDebug( __METHOD__.": deleted db items, applying file transactions\n" );
1545 - $transaction->commit();
1546 - FileStore::unlock();
1547 -
1548 -
1549 - // Update site_stats
1550 - $site_stats = $dbw->tableName( 'site_stats' );
1551 - $dbw->query( "UPDATE $site_stats SET ss_images=ss_images-1", __METHOD__ );
1552 -
1553 - $this->purgeEverything( $urlArr );
1554 -
1555 - return true;
1556 - }
1557 -
1558 -
1559 - /**
1560 - * Delete an old version of the image.
1561 - *
1562 - * Moves the file into an archive directory (or deletes it)
1563 - * and removes the database row.
1564 - *
1565 - * Cache purging is done; logging is caller's responsibility.
1566 - *
1567 - * @param string $archiveName
1568 - * @param string $reason
1569 - * @param boolean $suppress
1570 - * @throws MWException or FSException on database or filestore failure
1571 - * @return boolean true on success, false on some kind of failure
1572 - */
1573 - function deleteOld( $archiveName, $reason, $suppress=false ) {
1574 - $transaction = new FSTransaction();
1575 - $urlArr = array();
1576 -
1577 - if( !FileStore::lock() ) {
1578 - wfDebug( __METHOD__.": failed to acquire file store lock, aborting\n" );
1579 - return false;
1580 - }
1581 -
1582 - $transaction = new FSTransaction();
1583 - try {
1584 - $dbw = wfGetDB( DB_MASTER );
1585 - $dbw->begin();
1586 - $transaction->add( $this->prepareDeleteOld( $archiveName, $reason, $suppress ) );
1587 - $dbw->immediateCommit();
1588 - } catch( MWException $e ) {
1589 - wfDebug( __METHOD__.": db error, rolling back file transaction\n" );
1590 - $transaction->rollback();
1591 - FileStore::unlock();
1592 - throw $e;
1593 - }
1594 -
1595 - wfDebug( __METHOD__.": deleted db items, applying file transaction\n" );
1596 - $transaction->commit();
1597 - FileStore::unlock();
1598 -
1599 - $this->purgeDescription();
1600 -
1601 - // Squid purging
1602 - global $wgUseSquid;
1603 - if ( $wgUseSquid ) {
1604 - $urlArr = array(
1605 - wfImageArchiveUrl( $archiveName ),
1606 - );
1607 - wfPurgeSquidServers( $urlArr );
1608 - }
1609 - return true;
1610 - }
1611 -
1612 - /**
1613 - * Delete the current version of a file.
1614 - * May throw a database error.
1615 - * @param string $reason
1616 - * @param boolean $suppress
1617 - * @return boolean true on success, false on failure
1618 - */
1619 - private function prepareDeleteCurrent( $reason, $suppress=false ) {
1620 - return $this->prepareDeleteVersion(
1621 - $this->getFullPath(),
1622 - $reason,
1623 - 'image',
1624 - array(
1625 - 'fa_name' => 'img_name',
1626 - 'fa_archive_name' => 'NULL',
1627 - 'fa_size' => 'img_size',
1628 - 'fa_width' => 'img_width',
1629 - 'fa_height' => 'img_height',
1630 - 'fa_metadata' => 'img_metadata',
1631 - 'fa_bits' => 'img_bits',
1632 - 'fa_media_type' => 'img_media_type',
1633 - 'fa_major_mime' => 'img_major_mime',
1634 - 'fa_minor_mime' => 'img_minor_mime',
1635 - 'fa_description' => 'img_description',
1636 - 'fa_user' => 'img_user',
1637 - 'fa_user_text' => 'img_user_text',
1638 - 'fa_timestamp' => 'img_timestamp' ),
1639 - array( 'img_name' => $this->name ),
1640 - $suppress,
1641 - __METHOD__ );
1642 - }
1643 -
1644 - /**
1645 - * Delete a given older version of a file.
1646 - * May throw a database error.
1647 - * @param string $archiveName
1648 - * @param string $reason
1649 - * @param boolean $suppress
1650 - * @return boolean true on success, false on failure
1651 - */
1652 - private function prepareDeleteOld( $archiveName, $reason, $suppress=false ) {
1653 - $oldpath = wfImageArchiveDir( $this->name ) .
1654 - DIRECTORY_SEPARATOR . $archiveName;
1655 - return $this->prepareDeleteVersion(
1656 - $oldpath,
1657 - $reason,
1658 - 'oldimage',
1659 - array(
1660 - 'fa_name' => 'oi_name',
1661 - 'fa_archive_name' => 'oi_archive_name',
1662 - 'fa_size' => 'oi_size',
1663 - 'fa_width' => 'oi_width',
1664 - 'fa_height' => 'oi_height',
1665 - 'fa_metadata' => 'NULL',
1666 - 'fa_bits' => 'oi_bits',
1667 - 'fa_media_type' => 'NULL',
1668 - 'fa_major_mime' => 'NULL',
1669 - 'fa_minor_mime' => 'NULL',
1670 - 'fa_description' => 'oi_description',
1671 - 'fa_user' => 'oi_user',
1672 - 'fa_user_text' => 'oi_user_text',
1673 - 'fa_timestamp' => 'oi_timestamp' ),
1674 - array(
1675 - 'oi_name' => $this->name,
1676 - 'oi_archive_name' => $archiveName ),
1677 - $suppress,
1678 - __METHOD__ );
1679 - }
1680 -
1681 - /**
1682 - * Do the dirty work of backing up an image row and its file
1683 - * (if $wgSaveDeletedFiles is on) and removing the originals.
1684 - *
1685 - * Must be run while the file store is locked and a database
1686 - * transaction is open to avoid race conditions.
1687 - *
1688 - * @return FSTransaction
1689 - */
1690 - private function prepareDeleteVersion( $path, $reason, $table, $fieldMap, $where, $suppress=false, $fname ) {
1691 - global $wgUser, $wgSaveDeletedFiles;
1692 -
1693 - // Dupe the file into the file store
1694 - if( file_exists( $path ) ) {
1695 - if( $wgSaveDeletedFiles ) {
1696 - $group = 'deleted';
1697 -
1698 - $store = FileStore::get( $group );
1699 - $key = FileStore::calculateKey( $path, $this->extension );
1700 - $transaction = $store->insert( $key, $path,
1701 - FileStore::DELETE_ORIGINAL );
1702 - } else {
1703 - $group = null;
1704 - $key = null;
1705 - $transaction = FileStore::deleteFile( $path );
1706 - }
1707 - } else {
1708 - wfDebug( __METHOD__." deleting already-missing '$path'; moving on to database\n" );
1709 - $group = null;
1710 - $key = null;
1711 - $transaction = new FSTransaction(); // empty
1712 - }
1713 -
1714 - if( $transaction === false ) {
1715 - // Fail to restore?
1716 - wfDebug( __METHOD__.": import to file store failed, aborting\n" );
1717 - throw new MWException( "Could not archive and delete file $path" );
1718 - return false;
1719 - }
1720 -
1721 - // Bitfields to further supress the image content
1722 - // Note that currently, live images are stored elsewhere
1723 - // and cannot be partially deleted
1724 - $bitfield = 0;
1725 - if ( $suppress ) {
1726 - $bitfield |= self::DELETED_FILE;
1727 - $bitfield |= self::DELETED_COMMENT;
1728 - $bitfield |= self::DELETED_USER;
1729 - $bitfield |= self::DELETED_RESTRICTED;
1730 - }
1731 -
1732 - $dbw = wfGetDB( DB_MASTER );
1733 - $storageMap = array(
1734 - 'fa_storage_group' => $dbw->addQuotes( $group ),
1735 - 'fa_storage_key' => $dbw->addQuotes( $key ),
1736 -
1737 - 'fa_deleted_user' => $dbw->addQuotes( $wgUser->getId() ),
1738 - 'fa_deleted_timestamp' => $dbw->timestamp(),
1739 - 'fa_deleted_reason' => $dbw->addQuotes( $reason ),
1740 - 'fa_deleted' => $bitfield);
1741 - $allFields = array_merge( $storageMap, $fieldMap );
1742 -
1743 - try {
1744 - if( $wgSaveDeletedFiles ) {
1745 - $dbw->insertSelect( 'filearchive', $table, $allFields, $where, $fname );
1746 - }
1747 - $dbw->delete( $table, $where, $fname );
1748 - } catch( DBQueryError $e ) {
1749 - // Something went horribly wrong!
1750 - // Leave the file as it was...
1751 - wfDebug( __METHOD__.": database error, rolling back file transaction\n" );
1752 - $transaction->rollback();
1753 - throw $e;
1754 - }
1755 -
1756 - return $transaction;
1757 - }
1758 -
1759 - /**
1760 - * Restore all or specified deleted revisions to the given file.
1761 - * Permissions and logging are left to the caller.
1762 - *
1763 - * May throw database exceptions on error.
1764 - *
1765 - * @param $versions set of record ids of deleted items to restore,
1766 - * or empty to restore all revisions.
1767 - * @return the number of file revisions restored if successful,
1768 - * or false on failure
1769 - */
1770 - function restore( $versions=array(), $Unsuppress=false ) {
1771 - global $wgUser;
1772 -
1773 - if( !FileStore::lock() ) {
1774 - wfDebug( __METHOD__." could not acquire filestore lock\n" );
1775 - return false;
1776 - }
1777 -
1778 - $transaction = new FSTransaction();
1779 - try {
1780 - $dbw = wfGetDB( DB_MASTER );
1781 - $dbw->begin();
1782 -
1783 - // Re-confirm whether this image presently exists;
1784 - // if no we'll need to create an image record for the
1785 - // first item we restore.
1786 - $exists = $dbw->selectField( 'image', '1',
1787 - array( 'img_name' => $this->name ),
1788 - __METHOD__ );
1789 -
1790 - // Fetch all or selected archived revisions for the file,
1791 - // sorted from the most recent to the oldest.
1792 - $conditions = array( 'fa_name' => $this->name );
1793 - if( $versions ) {
1794 - $conditions['fa_id'] = $versions;
1795 - }
1796 -
1797 - $result = $dbw->select( 'filearchive', '*',
1798 - $conditions,
1799 - __METHOD__,
1800 - array( 'ORDER BY' => 'fa_timestamp DESC' ) );
1801 -
1802 - if( $dbw->numRows( $result ) < count( $versions ) ) {
1803 - // There's some kind of conflict or confusion;
1804 - // we can't restore everything we were asked to.
1805 - wfDebug( __METHOD__.": couldn't find requested items\n" );
1806 - $dbw->rollback();
1807 - FileStore::unlock();
1808 - return false;
1809 - }
1810 -
1811 - if( $dbw->numRows( $result ) == 0 ) {
1812 - // Nothing to do.
1813 - wfDebug( __METHOD__.": nothing to do\n" );
1814 - $dbw->rollback();
1815 - FileStore::unlock();
1816 - return true;
1817 - }
1818 -
1819 - $revisions = 0;
1820 - while( $row = $dbw->fetchObject( $result ) ) {
1821 - if ( $Unsuppress ) {
1822 - // Currently, fa_deleted flags fall off upon restore, lets be careful about this
1823 - } else if ( ($row->fa_deleted & Revision::DELETED_RESTRICTED) && !$wgUser->isAllowed('hiderevision') ) {
1824 - // Skip restoring file revisions that the user cannot restore
1825 - continue;
1826 - }
1827 - $revisions++;
1828 - $store = FileStore::get( $row->fa_storage_group );
1829 - if( !$store ) {
1830 - wfDebug( __METHOD__.": skipping row with no file.\n" );
1831 - continue;
1832 - }
1833 -
1834 - if( $revisions == 1 && !$exists ) {
1835 - $destDir = wfImageDir( $row->fa_name );
1836 - if ( !is_dir( $destDir ) ) {
1837 - wfMkdirParents( $destDir );
1838 - }
1839 - $destPath = $destDir . DIRECTORY_SEPARATOR . $row->fa_name;
1840 -
1841 - // We may have to fill in data if this was originally
1842 - // an archived file revision.
1843 - if( is_null( $row->fa_metadata ) ) {
1844 - $tempFile = $store->filePath( $row->fa_storage_key );
1845 -
1846 - $magic = MimeMagic::singleton();
1847 - $mime = $magic->guessMimeType( $tempFile, true );
1848 - $media_type = $magic->getMediaType( $tempFile, $mime );
1849 - list( $major_mime, $minor_mime ) = self::splitMime( $mime );
1850 - $handler = MediaHandler::getHandler( $mime );
1851 - if ( $handler ) {
1852 - $metadata = $handler->getMetadata( $image, $tempFile );
1853 - } else {
1854 - $metadata = '';
1855 - }
1856 - } else {
1857 - $metadata = $row->fa_metadata;
1858 - $major_mime = $row->fa_major_mime;
1859 - $minor_mime = $row->fa_minor_mime;
1860 - $media_type = $row->fa_media_type;
1861 - }
1862 -
1863 - $table = 'image';
1864 - $fields = array(
1865 - 'img_name' => $row->fa_name,
1866 - 'img_size' => $row->fa_size,
1867 - 'img_width' => $row->fa_width,
1868 - 'img_height' => $row->fa_height,
1869 - 'img_metadata' => $metadata,
1870 - 'img_bits' => $row->fa_bits,
1871 - 'img_media_type' => $media_type,
1872 - 'img_major_mime' => $major_mime,
1873 - 'img_minor_mime' => $minor_mime,
1874 - 'img_description' => $row->fa_description,
1875 - 'img_user' => $row->fa_user,
1876 - 'img_user_text' => $row->fa_user_text,
1877 - 'img_timestamp' => $row->fa_timestamp );
1878 - } else {
1879 - $archiveName = $row->fa_archive_name;
1880 - if( $archiveName == '' ) {
1881 - // This was originally a current version; we
1882 - // have to devise a new archive name for it.
1883 - // Format is <timestamp of archiving>!<name>
1884 - $archiveName =
1885 - wfTimestamp( TS_MW, $row->fa_deleted_timestamp ) .
1886 - '!' . $row->fa_name;
1887 - }
1888 - $destDir = wfImageArchiveDir( $row->fa_name );
1889 - if ( !is_dir( $destDir ) ) {
1890 - wfMkdirParents( $destDir );
1891 - }
1892 - $destPath = $destDir . DIRECTORY_SEPARATOR . $archiveName;
1893 -
1894 - $table = 'oldimage';
1895 - $fields = array(
1896 - 'oi_name' => $row->fa_name,
1897 - 'oi_archive_name' => $archiveName,
1898 - 'oi_size' => $row->fa_size,
1899 - 'oi_width' => $row->fa_width,
1900 - 'oi_height' => $row->fa_height,
1901 - 'oi_bits' => $row->fa_bits,
1902 - 'oi_description' => $row->fa_description,
1903 - 'oi_user' => $row->fa_user,
1904 - 'oi_user_text' => $row->fa_user_text,
1905 - 'oi_timestamp' => $row->fa_timestamp );
1906 - }
1907 -
1908 - $dbw->insert( $table, $fields, __METHOD__ );
1909 - // @todo this delete is not totally safe, potentially
1910 - $dbw->delete( 'filearchive',
1911 - array( 'fa_id' => $row->fa_id ),
1912 - __METHOD__ );
1913 -
1914 - // Check if any other stored revisions use this file;
1915 - // if so, we shouldn't remove the file from the deletion
1916 - // archives so they will still work.
1917 - $useCount = $dbw->selectField( 'filearchive',
1918 - 'COUNT(*)',
1919 - array(
1920 - 'fa_storage_group' => $row->fa_storage_group,
1921 - 'fa_storage_key' => $row->fa_storage_key ),
1922 - __METHOD__ );
1923 - if( $useCount == 0 ) {
1924 - wfDebug( __METHOD__.": nothing else using {$row->fa_storage_key}, will deleting after\n" );
1925 - $flags = FileStore::DELETE_ORIGINAL;
1926 - } else {
1927 - $flags = 0;
1928 - }
1929 -
1930 - $transaction->add( $store->export( $row->fa_storage_key,
1931 - $destPath, $flags ) );
1932 - }
1933 -
1934 - $dbw->immediateCommit();
1935 - } catch( MWException $e ) {
1936 - wfDebug( __METHOD__." caught error, aborting\n" );
1937 - $transaction->rollback();
1938 - throw $e;
1939 - }
1940 -
1941 - $transaction->commit();
1942 - FileStore::unlock();
1943 -
1944 - if( $revisions > 0 ) {
1945 - if( !$exists ) {
1946 - wfDebug( __METHOD__." restored $revisions items, creating a new current\n" );
1947 -
1948 - // Update site_stats
1949 - $site_stats = $dbw->tableName( 'site_stats' );
1950 - $dbw->query( "UPDATE $site_stats SET ss_images=ss_images+1", __METHOD__ );
1951 -
1952 - $this->purgeEverything();
1953 - } else {
1954 - wfDebug( __METHOD__." restored $revisions as archived versions\n" );
1955 - $this->purgeDescription();
1956 - }
1957 - }
1958 -
1959 - return $revisions;
1960 - }
1961 -
1962 - /**
1963 - * Returns 'true' if this image is a multipage document, e.g. a DJVU
1964 - * document.
1965 - *
1966 - * @return Bool
1967 - */
1968 - function isMultipage() {
1969 - $handler = $this->getHandler();
1970 - return $handler && $handler->isMultiPage();
1971 - }
1972 -
1973 - /**
1974 - * Returns the number of pages of a multipage document, or NULL for
1975 - * documents which aren't multipage documents
1976 - */
1977 - function pageCount() {
1978 - $handler = $this->getHandler();
1979 - if ( $handler && $handler->isMultiPage() ) {
1980 - return $handler->pageCount( $this );
1981 - } else {
1982 - return null;
1983 - }
1984 - }
1985 -
1986 - static function getCommonsDB() {
1987 - static $dbc;
1988 - global $wgLoadBalancer, $wgSharedUploadDBname;
1989 - if ( !isset( $dbc ) ) {
1990 - $i = $wgLoadBalancer->getGroupIndex( 'commons' );
1991 - $dbinfo = $wgLoadBalancer->mServers[$i];
1992 - $dbc = new Database( $dbinfo['host'], $dbinfo['user'],
1993 - $dbinfo['password'], $wgSharedUploadDBname );
1994 - }
1995 - return $dbc;
1996 - }
1997 -
1998 - /**
1999 - * Calculate the height of a thumbnail using the source and destination width
2000 - */
2001 - static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) {
2002 - // Exact integer multiply followed by division
2003 - if ( $srcWidth == 0 ) {
2004 - return 0;
2005 - } else {
2006 - return round( $srcHeight * $dstWidth / $srcWidth );
2007 - }
2008 - }
2009 -
2010 - /**
2011 - * Get an image size array like that returned by getimagesize(), or false if it
2012 - * can't be determined.
2013 - *
2014 - * @param string $fileName The filename
2015 - * @return array
2016 - */
2017 - function getImageSize( $fileName ) {
2018 - $handler = $this->getHandler();
2019 - return $handler->getImageSize( $this, $fileName );
2020 - }
2021 -
2022 - /**
2023 - * Get the thumbnail extension and MIME type for a given source MIME type
2024 - * @return array thumbnail extension and MIME type
2025 - */
2026 - static function getThumbType( $ext, $mime ) {
2027 - $handler = MediaHandler::getHandler( $mime );
2028 - if ( $handler ) {
2029 - return $handler->getThumbType( $ext, $mime );
2030 - } else {
2031 - return array( $ext, $mime );
2032 - }
2033 - }
2034 -
2035 -} //class
2036 -
2037 -
2038 -/**
2039 - * @addtogroup Media
2040 - */
2041 -class ArchivedFile
2042 -{
2043 - /**
2044 - * Returns a file object from the filearchive table
2045 - * In the future, all current and old image storage
2046 - * may use FileStore. There will be a "old" storage
2047 - * for current and previous file revisions as well as
2048 - * the "deleted" group for archived revisions
2049 - * @param $title, the corresponding image page title
2050 - * @param $id, the image id, a unique key
2051 - * @param $key, optional storage key
2052 - * @return ResultWrapper
2053 - */
2054 - function ArchivedFile( $title, $id=0, $key='' ) {
2055 - if( !is_object( $title ) ) {
2056 - throw new MWException( 'Image constructor given bogus title.' );
2057 - }
2058 - $conds = ($id) ? "fa_id = $id" : "fa_storage_key = '$key'";
2059 - if( $title->getNamespace() == NS_IMAGE ) {
2060 - $dbr = wfGetDB( DB_SLAVE );
2061 - $res = $dbr->select( 'filearchive',
2062 - array(
2063 - 'fa_id',
2064 - 'fa_name',
2065 - 'fa_storage_key',
2066 - 'fa_storage_group',
2067 - 'fa_size',
2068 - 'fa_bits',
2069 - 'fa_width',
2070 - 'fa_height',
2071 - 'fa_metadata',
2072 - 'fa_media_type',
2073 - 'fa_major_mime',
2074 - 'fa_minor_mime',
2075 - 'fa_description',
2076 - 'fa_user',
2077 - 'fa_user_text',
2078 - 'fa_timestamp',
2079 - 'fa_deleted' ),
2080 - array(
2081 - 'fa_name' => $title->getDbKey(),
2082 - $conds ),
2083 - __METHOD__,
2084 - array( 'ORDER BY' => 'fa_timestamp DESC' ) );
2085 -
2086 - if ( $dbr->numRows( $res ) == 0 ) {
2087 - // this revision does not exist?
2088 - return;
2089 - }
2090 - $ret = $dbr->resultObject( $res );
2091 - $row = $ret->fetchObject();
2092 -
2093 - // initialize fields for filestore image object
2094 - $this->mId = intval($row->fa_id);
2095 - $this->mName = $row->fa_name;
2096 - $this->mGroup = $row->fa_storage_group;
2097 - $this->mKey = $row->fa_storage_key;
2098 - $this->mSize = $row->fa_size;
2099 - $this->mBits = $row->fa_bits;
2100 - $this->mWidth = $row->fa_width;
2101 - $this->mHeight = $row->fa_height;
2102 - $this->mMetaData = $row->fa_metadata;
2103 - $this->mMime = "$row->fa_major_mime/$row->fa_minor_mime";
2104 - $this->mType = $row->fa_media_type;
2105 - $this->mDescription = $row->fa_description;
2106 - $this->mUser = $row->fa_user;
2107 - $this->mUserText = $row->fa_user_text;
2108 - $this->mTimestamp = $row->fa_timestamp;
2109 - $this->mDeleted = $row->fa_deleted;
2110 - } else {
2111 - throw new MWException( 'This title does not correspond to an image page.' );
2112 - return;
2113 - }
2114 - return true;
2115 - }
2116 -
2117 - /**
2118 - * int $field one of DELETED_* bitfield constants
2119 - * for file or revision rows
2120 - * @return bool
2121 - */
2122 - function isDeleted( $field ) {
2123 - return ($this->mDeleted & $field) == $field;
2124 - }
2125 -
2126 - /**
2127 - * Determine if the current user is allowed to view a particular
2128 - * field of this FileStore image file, if it's marked as deleted.
2129 - * @param int $field
2130 - * @return bool
2131 - */
2132 - function userCan( $field ) {
2133 - if( isset($this->mDeleted) && ($this->mDeleted & $field) == $field ) {
2134 - // images
2135 - global $wgUser;
2136 - $permission = ( $this->mDeleted & Revision::DELETED_RESTRICTED ) == Revision::DELETED_RESTRICTED
2137 - ? 'hiderevision'
2138 - : 'deleterevision';
2139 - wfDebug( "Checking for $permission due to $field match on $this->mDeleted\n" );
2140 - return $wgUser->isAllowed( $permission );
2141 - } else {
2142 - return true;
2143 - }
2144 - }
2145 -}
2146 -
2147 -/**
2148 - * Aliases for backwards compatibility with 1.6
2149 - */
2150 -define( 'MW_IMG_DELETED_FILE', Image::DELETED_FILE );
2151 -define( 'MW_IMG_DELETED_COMMENT', Image::DELETED_COMMENT );
2152 -define( 'MW_IMG_DELETED_USER', Image::DELETED_USER );
2153 -define( 'MW_IMG_DELETED_RESTRICTED', Image::DELETED_RESTRICTED );
2154 -
2155 -?>
Index: branches/liquidthreads/includes/SpecialWhatlinkshere.php
@@ -231,8 +231,10 @@
232232 }
233233
234234 //add whatlinkshere link
235 - $whatlink=$this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Whatlinkshere', $nt->getPrefixedDBkey() ), wfMsgHtml( 'whatlinkshere-links' ) );
236 - $wgOut->addHTML(' ' . $whatlink);
 235+ $whatlink = $this->skin->makeKnownLinkObj(
 236+ SpecialPage::getTitleFor( 'Whatlinkshere', $nt->getPrefixedDBkey() ),
 237+ wfMsgHtml( 'whatlinkshere-links' ) );
 238+ $wgOut->addHTML(" ($whatlink)" );
237239
238240 if ( $row->page_is_redirect ) {
239241 if ( $level < 2 ) {
Index: branches/liquidthreads/includes/ExternalEdit.php
@@ -46,7 +46,7 @@
4747 $extension="wiki";
4848 } elseif($this->mMode=="file") {
4949 $type="Edit file";
50 - $image = new Image( $this->mTitle );
 50+ $image = wfLocalFile( $this->mTitle );
5151 $img_url = $image->getURL();
5252 if(strpos($img_url,"://")) {
5353 $url = $img_url;
Index: branches/liquidthreads/includes/SearchEngine.php
@@ -122,8 +122,8 @@
123123 # There may have been a funny upload, or it may be on a shared
124124 # file repository such as Wikimedia Commons.
125125 if( $title->getNamespace() == NS_IMAGE ) {
126 - $image = new Image( $title );
127 - if( $image->exists() ) {
 126+ $image = wfFindFile( $title );
 127+ if( $image ) {
128128 return $title;
129129 }
130130 }
Index: branches/liquidthreads/includes/AutoLoader.php
@@ -96,8 +96,6 @@
9797 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
9898 'EnotifNotifyJob' => 'includes/JobQueue.php',
9999 'Http' => 'includes/HttpFunctions.php',
100 - 'Image' => 'includes/Image.php',
101 - 'ArchivedFile' => 'includes/Image.php',
102100 'IP' => 'includes/IP.php',
103101 'ThumbnailImage' => 'includes/Image.php',
104102 'ImageGallery' => 'includes/ImageGallery.php',
@@ -250,6 +248,19 @@
251249 'memcached' => 'includes/memcached-client.php',
252250 'EmaillingJob' => 'includes/JobQueue.php',
253251
 252+ # filerepo
 253+ 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
 254+ 'File' => 'includes/filerepo/File.php',
 255+ 'ForeignDBFile' => 'includes/filerepo/ForeignDBFile.php',
 256+ 'ForeignDBRepo' => 'includes/filerepo/ForeignDBRepo.php',
 257+ 'FSRepo' => 'includes/filerepo/FSRepo.php',
 258+ 'Image' => 'includes/filerepo/LocalFile.php',
 259+ 'LocalFile' => 'includes/filerepo/LocalFile.php',
 260+ 'LocalRepo' => 'includes/filerepo/LocalRepo.php',
 261+ 'OldLocalFile' => 'includes/filerepo/OldLocalFile.php',
 262+ 'RepoGroup' => 'includes/filerepo/RepoGroup.php',
 263+ 'UnregisteredLocalFile' => 'includes/filerepo/UnregisteredLocalFile.php',
 264+
254265 # Media
255266 'BitmapHandler' => 'includes/media/Bitmap.php',
256267 'BmpHandler' => 'includes/media/BMP.php',
Index: branches/liquidthreads/includes/ImageQueryPage.php
@@ -30,8 +30,8 @@
3131 # $num [should update this to use a Pager]
3232 for( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
3333 $image = $this->prepareImage( $row );
34 - if( $image instanceof Image ) {
35 - $gallery->add( $image, $this->getCellHtml( $row ) );
 34+ if( $image ) {
 35+ $gallery->add( $image->getTitle(), $this->getCellHtml( $row ) );
3636 }
3737 }
3838
@@ -49,7 +49,7 @@
5050 $namespace = isset( $row->namespace ) ? $row->namespace : NS_IMAGE;
5151 $title = Title::makeTitleSafe( $namespace, $row->title );
5252 return ( $title instanceof Title && $title->getNamespace() == NS_IMAGE )
53 - ? new Image( $title )
 53+ ? wfFindFile( $title )
5454 : null;
5555 }
5656
Index: branches/liquidthreads/includes/filerepo/FSRepo.php
@@ -0,0 +1,412 @@
 2+<?php
 3+
 4+/**
 5+ * A repository for files accessible via the local filesystem. Does not support
 6+ * database access or registration.
 7+ *
 8+ * TODO: split off abstract base FileRepo
 9+ */
 10+
 11+class FSRepo {
 12+ const DELETE_SOURCE = 1;
 13+
 14+ var $directory, $url, $hashLevels, $thumbScriptUrl, $transformVia404;
 15+ var $descBaseUrl, $scriptDirUrl, $articleUrl, $fetchDescription;
 16+ var $fileFactory = array( 'UnregisteredLocalFile', 'newFromTitle' );
 17+ var $oldFileFactory = false;
 18+
 19+ function __construct( $info ) {
 20+ // Required settings
 21+ $this->name = $info['name'];
 22+ $this->directory = $info['directory'];
 23+ $this->url = $info['url'];
 24+ $this->hashLevels = $info['hashLevels'];
 25+ $this->transformVia404 = !empty( $info['transformVia404'] );
 26+
 27+ // Optional settings
 28+ foreach ( array( 'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription',
 29+ 'thumbScriptUrl' ) as $var )
 30+ {
 31+ if ( isset( $info[$var] ) ) {
 32+ $this->$var = $info[$var];
 33+ }
 34+ }
 35+ }
 36+
 37+ /**
 38+ * Create a new File object from the local repository
 39+ * @param mixed $title Title object or string
 40+ * @param mixed $time Time at which the image is supposed to have existed.
 41+ * If this is specified, the returned object will be an
 42+ * instance of the repository's old file class instead of
 43+ * a current file. Repositories not supporting version
 44+ * control should return false if this parameter is set.
 45+ */
 46+ function newFile( $title, $time = false ) {
 47+ if ( !($title instanceof Title) ) {
 48+ $title = Title::makeTitleSafe( NS_IMAGE, $title );
 49+ if ( !is_object( $title ) ) {
 50+ return null;
 51+ }
 52+ }
 53+ if ( $time ) {
 54+ if ( $this->oldFileFactory ) {
 55+ return call_user_func( $this->oldFileFactory, $title, $this, $time );
 56+ } else {
 57+ return false;
 58+ }
 59+ } else {
 60+ return call_user_func( $this->fileFactory, $title, $this );
 61+ }
 62+ }
 63+
 64+ /**
 65+ * Find an instance of the named file that existed at the specified time
 66+ * Returns false if the file did not exist. Repositories not supporting
 67+ * version control should return false if the time is specified.
 68+ *
 69+ * @param mixed $time 14-character timestamp, or false for the current version
 70+ */
 71+ function findFile( $title, $time = false ) {
 72+ # First try the current version of the file to see if it precedes the timestamp
 73+ $img = $this->newFile( $title );
 74+ if ( !$img ) {
 75+ return false;
 76+ }
 77+ if ( $img->exists() && ( !$time || $img->getTimestamp() <= $time ) ) {
 78+ return $img;
 79+ }
 80+ # Now try an old version of the file
 81+ $img = $this->newFile( $title, $time );
 82+ if ( $img->exists() ) {
 83+ return $img;
 84+ }
 85+ }
 86+
 87+ /**
 88+ * Get the public root directory of the repository.
 89+ */
 90+ function getRootDirectory() {
 91+ return $this->directory;
 92+ }
 93+
 94+ /**
 95+ * Get the public root URL of the repository
 96+ */
 97+ function getRootUrl() {
 98+ return $this->url;
 99+ }
 100+
 101+ /**
 102+ * Returns true if the repository uses a multi-level directory structure
 103+ */
 104+ function isHashed() {
 105+ return (bool)$this->hashLevels;
 106+ }
 107+
 108+ /**
 109+ * Get the URL of thumb.php
 110+ */
 111+ function getThumbScriptUrl() {
 112+ return $this->thumbScriptUrl;
 113+ }
 114+
 115+ /**
 116+ * Returns true if the repository can transform files via a 404 handler
 117+ */
 118+ function canTransformVia404() {
 119+ return $this->transformVia404;
 120+ }
 121+
 122+ /**
 123+ * Get the local directory corresponding to one of the three basic zones
 124+ */
 125+ function getZonePath( $zone ) {
 126+ switch ( $zone ) {
 127+ case 'public':
 128+ return $this->directory;
 129+ case 'temp':
 130+ return "{$this->directory}/temp";
 131+ case 'deleted':
 132+ return $GLOBALS['wgFileStore']['deleted']['directory'];
 133+ default:
 134+ return false;
 135+ }
 136+ }
 137+
 138+ /**
 139+ * Get the URL corresponding to one of the three basic zones
 140+ */
 141+ function getZoneUrl( $zone ) {
 142+ switch ( $zone ) {
 143+ case 'public':
 144+ return $this->url;
 145+ case 'temp':
 146+ return "{$this->url}/temp";
 147+ case 'deleted':
 148+ return $GLOBALS['wgFileStore']['deleted']['url'];
 149+ default:
 150+ return false;
 151+ }
 152+ }
 153+
 154+ /**
 155+ * Get a URL referring to this repository, with the private mwrepo protocol.
 156+ */
 157+ function getVirtualUrl( $suffix = false ) {
 158+ $path = 'mwrepo://';
 159+ if ( $suffix !== false ) {
 160+ $path .= '/' . $suffix;
 161+ }
 162+ return $path;
 163+ }
 164+
 165+ /**
 166+ * Get the local path corresponding to a virtual URL
 167+ */
 168+ function resolveVirtualUrl( $url ) {
 169+ if ( substr( $url, 0, 9 ) != 'mwrepo://' ) {
 170+ throw new MWException( __METHOD__.': unknown protoocl' );
 171+ }
 172+
 173+ $bits = explode( '/', substr( $url, 9 ), 3 );
 174+ if ( count( $bits ) != 3 ) {
 175+ throw new MWException( __METHOD__.": invalid mwrepo URL: $url" );
 176+ }
 177+ list( $host, $zone, $rel ) = $bits;
 178+ if ( $host !== '' ) {
 179+ throw new MWException( __METHOD__.": fetching from a foreign repo is not supported" );
 180+ }
 181+ $base = $this->getZonePath( $zone );
 182+ if ( !$base ) {
 183+ throw new MWException( __METHOD__.": invalid zone: $zone" );
 184+ }
 185+ return $base . '/' . urldecode( $rel );
 186+ }
 187+
 188+ /**
 189+ * Store a file to a given destination.
 190+ */
 191+ function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) {
 192+ $root = $this->getZonePath( $dstZone );
 193+ if ( !$root ) {
 194+ throw new MWException( "Invalid zone: $dstZone" );
 195+ }
 196+ $dstPath = "$root/$dstRel";
 197+
 198+ if ( !is_dir( dirname( $dstPath ) ) ) {
 199+ wfMkdirParents( dirname( $dstPath ) );
 200+ }
 201+
 202+ if ( substr( $srcPath, 0, 9 ) == 'mwrepo://' ) {
 203+ $srcPath = $this->resolveVirtualUrl( $srcPath );
 204+ }
 205+
 206+ if ( $flags & self::DELETE_SOURCE ) {
 207+ if ( !rename( $srcPath, $dstPath ) ) {
 208+ return new WikiErrorMsg( 'filerenameerror', wfEscapeWikiText( $srcPath ),
 209+ wfEscapeWikiText( $dstPath ) );
 210+ }
 211+ } else {
 212+ if ( !copy( $srcPath, $dstPath ) ) {
 213+ return new WikiErrorMsg( 'filecopyerror', wfEscapeWikiText( $srcPath ),
 214+ wfEscapeWikiText( $dstPath ) );
 215+ }
 216+ }
 217+ chmod( $dstPath, 0644 );
 218+ return true;
 219+ }
 220+
 221+ /**
 222+ * Pick a random name in the temp zone and store a file to it.
 223+ * Returns the URL, or a WikiError on failure.
 224+ * @param string $originalName The base name of the file as specified
 225+ * by the user. The file extension will be maintained.
 226+ * @param string $srcPath The current location of the file.
 227+ */
 228+ function storeTemp( $originalName, $srcPath ) {
 229+ $dstRel = $this->getHashPath( $originalName ) .
 230+ gmdate( "YmdHis" ) . '!' . $originalName;
 231+ $result = $this->store( $srcPath, 'temp', $dstRel );
 232+ if ( WikiError::isError( $result ) ) {
 233+ return $result;
 234+ } else {
 235+ return $this->getVirtualUrl( "temp/$dstRel" );
 236+ }
 237+ }
 238+
 239+ /**
 240+ * Copy or move a file either from the local filesystem or from an mwrepo://
 241+ * virtual URL, into this repository at the specified destination location.
 242+ *
 243+ * @param string $srcPath The source path or URL
 244+ * @param string $dstPath The destination relative path
 245+ * @param string $archivePath The relative path where the existing file is to
 246+ * be archived, if there is one.
 247+ * @param integer $flags Bitfield, may be FSRepo::DELETE_SOURCE to indicate
 248+ * that the source file should be deleted if possible
 249+ */
 250+ function publish( $srcPath, $dstPath, $archivePath, $flags = 0 ) {
 251+ if ( substr( $srcPath, 0, 9 ) == 'mwrepo://' ) {
 252+ $srcPath = $this->resolveVirtualUrl( $srcPath );
 253+ }
 254+ $dstDir = dirname( $dstPath );
 255+ if ( !is_dir( $dstDir ) ) wfMkdirParents( $dstDir );
 256+
 257+ if( is_file( $dstPath ) ) {
 258+ $archiveDir = dirname( $archivePath );
 259+ if ( !is_dir( $archiveDir ) ) wfMkdirParents( $archiveDir );
 260+ wfSuppressWarnings();
 261+ $success = rename( $dstPath, $archivePath );
 262+ wfRestoreWarnings();
 263+
 264+ if( ! $success ) {
 265+ return new WikiErrorMsg( 'filerenameerror', wfEscapeWikiText( $dstPath ),
 266+ wfEscapeWikiText( $archivePath ) );
 267+ }
 268+ else wfDebug(__METHOD__.": moved file $dstPath to $archivePath\n");
 269+ $status = 'archived';
 270+ }
 271+ else {
 272+ $status = 'new';
 273+ }
 274+
 275+ $error = false;
 276+ wfSuppressWarnings();
 277+ if ( $flags & self::DELETE_SOURCE ) {
 278+ if ( !rename( $srcPath, $dstPath ) ) {
 279+ $error = new WikiErrorMsg( 'filerenameerror', wfEscapeWikiText( $srcPath ),
 280+ wfEscapeWikiText( $dstPath ) );
 281+ }
 282+ } else {
 283+ if ( !copy( $srcPath, $dstPath ) ) {
 284+ $error = new WikiErrorMsg( 'filerenameerror', wfEscapeWikiText( $srcPath ),
 285+ wfEscapeWikiText( $dstPath ) );
 286+ }
 287+ }
 288+ wfRestoreWarnings();
 289+
 290+ if( $error ) {
 291+ return $error;
 292+ } else {
 293+ wfDebug(__METHOD__.": wrote tempfile $srcPath to $dstPath\n");
 294+ }
 295+
 296+ chmod( $dstPath, 0644 );
 297+ return $status;
 298+ }
 299+
 300+ /**
 301+ * Get a relative path including trailing slash, e.g. f/fa/
 302+ * If the repo is not hashed, returns an empty string
 303+ */
 304+ function getHashPath( $name ) {
 305+ if ( $this->isHashed() ) {
 306+ $hash = md5( $name );
 307+ $path = '';
 308+ for ( $i = 1; $i <= $this->hashLevels; $i++ ) {
 309+ $path .= substr( $hash, 0, $i ) . '/';
 310+ }
 311+ return $path;
 312+ } else {
 313+ return '';
 314+ }
 315+ }
 316+
 317+ /**
 318+ * Get the name of this repository, as specified by $info['name]' to the constructor
 319+ */
 320+ function getName() {
 321+ return $this->name;
 322+ }
 323+
 324+ /**
 325+ * Get the file description page base URL, or false if there isn't one.
 326+ * @private
 327+ */
 328+ function getDescBaseUrl() {
 329+ if ( is_null( $this->descBaseUrl ) ) {
 330+ if ( !is_null( $this->articleUrl ) ) {
 331+ $this->descBaseUrl = str_replace( '$1',
 332+ urlencode( Namespace::getCanonicalName( NS_IMAGE ) ) . ':', $this->articleUrl );
 333+ } elseif ( !is_null( $this->scriptDirUrl ) ) {
 334+ $this->descBaseUrl = $this->scriptDirUrl . '/index.php?title=' .
 335+ urlencode( Namespace::getCanonicalName( NS_IMAGE ) ) . ':';
 336+ } else {
 337+ $this->descBaseUrl = false;
 338+ }
 339+ }
 340+ return $this->descBaseUrl;
 341+ }
 342+
 343+ /**
 344+ * Get the URL of an image description page. May return false if it is
 345+ * unknown or not applicable. In general this should only be called by the
 346+ * File class, since it may return invalid results for certain kinds of
 347+ * repositories. Use File::getDescriptionUrl() in user code.
 348+ *
 349+ * In particular, it uses the article paths as specified to the repository
 350+ * constructor, whereas local repositories use the local Title functions.
 351+ */
 352+ function getDescriptionUrl( $name ) {
 353+ $base = $this->getDescBaseUrl();
 354+ if ( $base ) {
 355+ return $base . wfUrlencode( $name );
 356+ } else {
 357+ return false;
 358+ }
 359+ }
 360+
 361+ /**
 362+ * Get the URL of the content-only fragment of the description page. For
 363+ * MediaWiki this means action=render. This should only be called by the
 364+ * repository's file class, since it may return invalid results. User code
 365+ * should use File::getDescriptionText().
 366+ */
 367+ function getDescriptionRenderUrl( $name ) {
 368+ if ( isset( $this->scriptDirUrl ) ) {
 369+ return $this->scriptDirUrl . '/index.php?title=' .
 370+ wfUrlencode( Namespace::getCanonicalName( NS_IMAGE ) . ':' . $name ) .
 371+ '&action=render';
 372+ } else {
 373+ $descBase = $this->getDescBaseUrl();
 374+ if ( $descBase ) {
 375+ return wfAppendQuery( $descBase . wfUrlencode( $name ), 'action=render' );
 376+ } else {
 377+ return false;
 378+ }
 379+ }
 380+ }
 381+
 382+ /**
 383+ * Call a callback function for every file in the repository.
 384+ * Uses the filesystem even in child classes.
 385+ */
 386+ function enumFilesInFS( $callback ) {
 387+ $numDirs = 1 << ( $this->hashLevels * 4 );
 388+ for ( $flatIndex = 0; $flatIndex < $numDirs; $flatIndex++ ) {
 389+ $hexString = sprintf( "%0{$this->hashLevels}x", $flatIndex );
 390+ $path = $this->directory;
 391+ for ( $hexPos = 0; $hexPos < $this->hashLevels; $hexPos++ ) {
 392+ $path .= '/' . substr( $hexString, 0, $hexPos + 1 );
 393+ }
 394+ if ( !file_exists( $path ) || !is_dir( $path ) ) {
 395+ continue;
 396+ }
 397+ $dir = opendir( $path );
 398+ while ( false !== ( $name = readdir( $dir ) ) ) {
 399+ call_user_func( $callback, $path . '/' . $name );
 400+ }
 401+ }
 402+ }
 403+
 404+ /**
 405+ * Call a callaback function for every file in the repository
 406+ * May use either the database or the filesystem
 407+ */
 408+ function enumFiles( $callback ) {
 409+ $this->enumFilesInFS( $callback );
 410+ }
 411+}
 412+
 413+?>
Property changes on: branches/liquidthreads/includes/filerepo/FSRepo.php
___________________________________________________________________
Added: svn:eol-style
1414 + native
Index: branches/liquidthreads/includes/filerepo/File.php
@@ -0,0 +1,989 @@
 2+<?php
 3+
 4+/**
 5+ * Base file class. Do not instantiate.
 6+ *
 7+ * Implements some public methods and some protected utility functions which
 8+ * are required by multiple child classes. Contains stub functionality for
 9+ * unimplemented public methods.
 10+ *
 11+ * Stub functions which should be overridden are marked with STUB. Some more
 12+ * concrete functions are also typically overridden by child classes.
 13+ *
 14+ * Note that only the repo object knows what its file class is called. You should
 15+ * never name a file class explictly outside of the repo class. Instead use the
 16+ * repo's factory functions to generate file objects, for example:
 17+ *
 18+ * RepoGroup::singleton()->getLocalRepo()->newFile($title);
 19+ *
 20+ * The convenience functions wfLocalFile() and wfFindFile() should be sufficient
 21+ * in most cases.
 22+ *
 23+ * @addtogroup FileRepo
 24+ */
 25+class File {
 26+ const DELETED_FILE = 1;
 27+ const DELETED_COMMENT = 2;
 28+ const DELETED_USER = 4;
 29+ const DELETED_RESTRICTED = 8;
 30+ const RENDER_NOW = 1;
 31+
 32+ const DELETE_SOURCE = 1;
 33+
 34+ /**
 35+ * Some member variables can be lazy-initialised using __get(). The
 36+ * initialisation function for these variables is always a function named
 37+ * like getVar(), where Var is the variable name with upper-case first
 38+ * letter.
 39+ *
 40+ * The following variables are initialised in this way in this base class:
 41+ * name, extension, handler, path, canRender, isSafeFile,
 42+ * transformScript, hashPath, pageCount, url
 43+ *
 44+ * Code within this class should generally use the accessor function
 45+ * directly, since __get() isn't re-entrant and therefore causes bugs that
 46+ * depend on initialisation order.
 47+ */
 48+
 49+ /**
 50+ * The following member variables are not lazy-initialised
 51+ */
 52+ var $repo, $title, $lastError;
 53+
 54+ /**
 55+ * Call this constructor from child classes
 56+ */
 57+ function __construct( $title, $repo ) {
 58+ $this->title = $title;
 59+ $this->repo = $repo;
 60+ }
 61+
 62+ function __get( $name ) {
 63+ $function = array( $this, 'get' . ucfirst( $name ) );
 64+ if ( !is_callable( $function ) ) {
 65+ return null;
 66+ } else {
 67+ $this->$name = call_user_func( $function );
 68+ return $this->$name;
 69+ }
 70+ }
 71+
 72+ /**
 73+ * Normalize a file extension to the common form, and ensure it's clean.
 74+ * Extensions with non-alphanumeric characters will be discarded.
 75+ *
 76+ * @param $ext string (without the .)
 77+ * @return string
 78+ */
 79+ static function normalizeExtension( $ext ) {
 80+ $lower = strtolower( $ext );
 81+ $squish = array(
 82+ 'htm' => 'html',
 83+ 'jpeg' => 'jpg',
 84+ 'mpeg' => 'mpg',
 85+ 'tiff' => 'tif' );
 86+ if( isset( $squish[$lower] ) ) {
 87+ return $squish[$lower];
 88+ } elseif( preg_match( '/^[0-9a-z]+$/', $lower ) ) {
 89+ return $lower;
 90+ } else {
 91+ return '';
 92+ }
 93+ }
 94+
 95+ /**
 96+ * Upgrade the database row if there is one
 97+ * Called by ImagePage
 98+ * STUB
 99+ */
 100+ function upgradeRow() {}
 101+
 102+ /**
 103+ * Split an internet media type into its two components; if not
 104+ * a two-part name, set the minor type to 'unknown'.
 105+ *
 106+ * @param $mime "text/html" etc
 107+ * @return array ("text", "html") etc
 108+ */
 109+ static function splitMime( $mime ) {
 110+ if( strpos( $mime, '/' ) !== false ) {
 111+ return explode( '/', $mime, 2 );
 112+ } else {
 113+ return array( $mime, 'unknown' );
 114+ }
 115+ }
 116+
 117+ /**
 118+ * Return the name of this file
 119+ * @public
 120+ */
 121+ function getName() {
 122+ if ( !isset( $this->name ) ) {
 123+ $this->name = $this->title->getDBkey();
 124+ }
 125+ return $this->name;
 126+ }
 127+
 128+ /**
 129+ * Get the file extension, e.g. "svg"
 130+ */
 131+ function getExtension() {
 132+ if ( !isset( $this->extension ) ) {
 133+ $n = strrpos( $this->getName(), '.' );
 134+ $this->extension = self::normalizeExtension(
 135+ $n ? substr( $this->getName(), $n + 1 ) : '' );
 136+ }
 137+ return $this->extension;
 138+ }
 139+
 140+ /**
 141+ * Return the associated title object
 142+ * @public
 143+ */
 144+ function getTitle() { return $this->title; }
 145+
 146+ /**
 147+ * Return the URL of the file
 148+ * @public
 149+ */
 150+ function getUrl() {
 151+ if ( !isset( $this->url ) ) {
 152+ $this->url = $this->repo->getZoneUrl( 'public' ) . '/' . $this->getUrlRel();
 153+ }
 154+ return $this->url;
 155+ }
 156+
 157+ function getViewURL() {
 158+ if( $this->mustRender()) {
 159+ if( $this->canRender() ) {
 160+ return $this->createThumb( $this->getWidth() );
 161+ }
 162+ else {
 163+ wfDebug(__METHOD__.': supposed to render '.$this->getName().' ('.$this->getMimeType()."), but can't!\n");
 164+ return $this->getURL(); #hm... return NULL?
 165+ }
 166+ } else {
 167+ return $this->getURL();
 168+ }
 169+ }
 170+
 171+ /**
 172+ * Return the full filesystem path to the file. Note that this does
 173+ * not mean that a file actually exists under that location.
 174+ *
 175+ * This path depends on whether directory hashing is active or not,
 176+ * i.e. whether the files are all found in the same directory,
 177+ * or in hashed paths like /images/3/3c.
 178+ *
 179+ * May return false if the file is not locally accessible.
 180+ *
 181+ * @public
 182+ */
 183+ function getPath() {
 184+ if ( !isset( $this->path ) ) {
 185+ $this->path = $this->repo->getZonePath('public') . '/' . $this->getRel();
 186+ }
 187+ return $this->path;
 188+ }
 189+
 190+ /**
 191+ * Alias for getPath()
 192+ * @public
 193+ */
 194+ function getFullPath() {
 195+ return $this->getPath();
 196+ }
 197+
 198+ /**
 199+ * Return the width of the image. Returns false if the width is unknown
 200+ * or undefined.
 201+ *
 202+ * STUB
 203+ * Overridden by LocalFile, UnregisteredLocalFile
 204+ * @public
 205+ */
 206+ function getWidth( $page = 1 ) { return false; }
 207+
 208+ /**
 209+ * Return the height of the image. Returns false if the height is unknown
 210+ * or undefined
 211+ *
 212+ * STUB
 213+ * Overridden by LocalFile, UnregisteredLocalFile
 214+ * @public
 215+ */
 216+ function getHeight( $page = 1 ) { return false; }
 217+
 218+ /**
 219+ * Get handler-specific metadata
 220+ * Overridden by LocalFile, UnregisteredLocalFile
 221+ * STUB
 222+ */
 223+ function getMetadata() { return false; }
 224+
 225+ /**
 226+ * Return the size of the image file, in bytes
 227+ * Overridden by LocalFile, UnregisteredLocalFile
 228+ * STUB
 229+ * @public
 230+ */
 231+ function getSize() { return false; }
 232+
 233+ /**
 234+ * Returns the mime type of the file.
 235+ * Overridden by LocalFile, UnregisteredLocalFile
 236+ * STUB
 237+ */
 238+ function getMimeType() { return 'unknown/unknown'; }
 239+
 240+ /**
 241+ * Return the type of the media in the file.
 242+ * Use the value returned by this function with the MEDIATYPE_xxx constants.
 243+ * Overridden by LocalFile,
 244+ * STUB
 245+ */
 246+ function getMediaType() { return MEDIATYPE_UNKNOWN; }
 247+
 248+ /**
 249+ * Checks if the file can be presented to the browser as a bitmap.
 250+ *
 251+ * Currently, this checks if the file is an image format
 252+ * that can be converted to a format
 253+ * supported by all browsers (namely GIF, PNG and JPEG),
 254+ * or if it is an SVG image and SVG conversion is enabled.
 255+ */
 256+ function canRender() {
 257+ if ( !isset( $this->canRender ) ) {
 258+ $this->canRender = $this->getHandler() && $this->handler->canRender();
 259+ }
 260+ return $this->canRender;
 261+ }
 262+
 263+ /**
 264+ * Accessor for __get()
 265+ */
 266+ protected function getCanRender() {
 267+ return $this->canRender();
 268+ }
 269+
 270+ /**
 271+ * Return true if the file is of a type that can't be directly
 272+ * rendered by typical browsers and needs to be re-rasterized.
 273+ *
 274+ * This returns true for everything but the bitmap types
 275+ * supported by all browsers, i.e. JPEG; GIF and PNG. It will
 276+ * also return true for any non-image formats.
 277+ *
 278+ * @return bool
 279+ */
 280+ function mustRender() {
 281+ return $this->getHandler() && $this->handler->mustRender();
 282+ }
 283+
 284+ /**
 285+ * Determines if this media file may be shown inline on a page.
 286+ *
 287+ * This is currently synonymous to canRender(), but this could be
 288+ * extended to also allow inline display of other media,
 289+ * like flash animations or videos. If you do so, please keep in mind that
 290+ * that could be a security risk.
 291+ */
 292+ function allowInlineDisplay() {
 293+ return $this->canRender();
 294+ }
 295+
 296+ /**
 297+ * Determines if this media file is in a format that is unlikely to
 298+ * contain viruses or malicious content. It uses the global
 299+ * $wgTrustedMediaFormats list to determine if the file is safe.
 300+ *
 301+ * This is used to show a warning on the description page of non-safe files.
 302+ * It may also be used to disallow direct [[media:...]] links to such files.
 303+ *
 304+ * Note that this function will always return true if allowInlineDisplay()
 305+ * or isTrustedFile() is true for this file.
 306+ */
 307+ function isSafeFile() {
 308+ if ( !isset( $this->isSafeFile ) ) {
 309+ $this->isSafeFile = $this->_getIsSafeFile();
 310+ }
 311+ return $this->isSafeFile;
 312+ }
 313+
 314+ /** Accessor for __get() */
 315+ protected function getIsSafeFile() {
 316+ return $this->isSafeFile();
 317+ }
 318+
 319+ /** Uncached accessor */
 320+ protected function _getIsSafeFile() {
 321+ if ($this->allowInlineDisplay()) return true;
 322+ if ($this->isTrustedFile()) return true;
 323+
 324+ global $wgTrustedMediaFormats;
 325+
 326+ $type= $this->getMediaType();
 327+ $mime= $this->getMimeType();
 328+ #wfDebug("LocalFile::isSafeFile: type= $type, mime= $mime\n");
 329+
 330+ if (!$type || $type===MEDIATYPE_UNKNOWN) return false; #unknown type, not trusted
 331+ if ( in_array( $type, $wgTrustedMediaFormats) ) return true;
 332+
 333+ if ($mime==="unknown/unknown") return false; #unknown type, not trusted
 334+ if ( in_array( $mime, $wgTrustedMediaFormats) ) return true;
 335+
 336+ return false;
 337+ }
 338+
 339+ /** Returns true if the file is flagged as trusted. Files flagged that way
 340+ * can be linked to directly, even if that is not allowed for this type of
 341+ * file normally.
 342+ *
 343+ * This is a dummy function right now and always returns false. It could be
 344+ * implemented to extract a flag from the database. The trusted flag could be
 345+ * set on upload, if the user has sufficient privileges, to bypass script-
 346+ * and html-filters. It may even be coupled with cryptographics signatures
 347+ * or such.
 348+ */
 349+ function isTrustedFile() {
 350+ #this could be implemented to check a flag in the databas,
 351+ #look for signatures, etc
 352+ return false;
 353+ }
 354+
 355+ /**
 356+ * Returns true if file exists in the repository.
 357+ *
 358+ * Overridden by LocalFile to avoid unnecessary stat calls.
 359+ *
 360+ * @return boolean Whether file exists in the repository.
 361+ * @public
 362+ */
 363+ function exists() {
 364+ return $this->getPath() && file_exists( $this->path );
 365+ }
 366+
 367+ function getTransformScript() {
 368+ if ( !isset( $this->transformScript ) ) {
 369+ $this->transformScript = false;
 370+ if ( $this->repo ) {
 371+ $script = $this->repo->getThumbScriptUrl();
 372+ if ( $script ) {
 373+ $this->transformScript = "$script?f=" . urlencode( $this->getName() );
 374+ }
 375+ }
 376+ }
 377+ return $this->transformScript;
 378+ }
 379+
 380+ /**
 381+ * Get a ThumbnailImage which is the same size as the source
 382+ */
 383+ function getUnscaledThumb( $page = false ) {
 384+ $width = $this->getWidth( $page );
 385+ if ( !$width ) {
 386+ return $this->iconThumb();
 387+ }
 388+ if ( $page ) {
 389+ $params = array(
 390+ 'page' => $page,
 391+ 'width' => $this->getWidth( $page )
 392+ );
 393+ } else {
 394+ $params = array( 'width' => $this->getWidth() );
 395+ }
 396+ return $this->transform( $params );
 397+ }
 398+
 399+ /**
 400+ * Return the file name of a thumbnail with the specified parameters
 401+ *
 402+ * @param array $params Handler-specific parameters
 403+ * @private
 404+ */
 405+ function thumbName( $params ) {
 406+ if ( !$this->getHandler() ) {
 407+ return null;
 408+ }
 409+ $extension = $this->getExtension();
 410+ list( $thumbExt, $thumbMime ) = $this->handler->getThumbType( $extension, $this->getMimeType() );
 411+ $thumbName = $this->handler->makeParamString( $params ) . '-' . $this->getName();
 412+ if ( $thumbExt != $extension ) {
 413+ $thumbName .= ".$thumbExt";
 414+ }
 415+ return $thumbName;
 416+ }
 417+
 418+ /**
 419+ * Create a thumbnail of the image having the specified width/height.
 420+ * The thumbnail will not be created if the width is larger than the
 421+ * image's width. Let the browser do the scaling in this case.
 422+ * The thumbnail is stored on disk and is only computed if the thumbnail
 423+ * file does not exist OR if it is older than the image.
 424+ * Returns the URL.
 425+ *
 426+ * Keeps aspect ratio of original image. If both width and height are
 427+ * specified, the generated image will be no bigger than width x height,
 428+ * and will also have correct aspect ratio.
 429+ *
 430+ * @param integer $width maximum width of the generated thumbnail
 431+ * @param integer $height maximum height of the image (optional)
 432+ * @public
 433+ */
 434+ function createThumb( $width, $height = -1 ) {
 435+ $params = array( 'width' => $width );
 436+ if ( $height != -1 ) {
 437+ $params['height'] = $height;
 438+ }
 439+ $thumb = $this->transform( $params );
 440+ if( is_null( $thumb ) || $thumb->isError() ) return '';
 441+ return $thumb->getUrl();
 442+ }
 443+
 444+ /**
 445+ * As createThumb, but returns a ThumbnailImage object. This can
 446+ * provide access to the actual file, the real size of the thumb,
 447+ * and can produce a convenient <img> tag for you.
 448+ *
 449+ * For non-image formats, this may return a filetype-specific icon.
 450+ *
 451+ * @param integer $width maximum width of the generated thumbnail
 452+ * @param integer $height maximum height of the image (optional)
 453+ * @param boolean $render True to render the thumbnail if it doesn't exist,
 454+ * false to just return the URL
 455+ *
 456+ * @return ThumbnailImage or null on failure
 457+ * @public
 458+ *
 459+ * @deprecated use transform()
 460+ */
 461+ function getThumbnail( $width, $height=-1, $render = true ) {
 462+ $params = array( 'width' => $width );
 463+ if ( $height != -1 ) {
 464+ $params['height'] = $height;
 465+ }
 466+ $flags = $render ? self::RENDER_NOW : 0;
 467+ return $this->transform( $params, $flags );
 468+ }
 469+
 470+ /**
 471+ * Transform a media file
 472+ *
 473+ * @param array $params An associative array of handler-specific parameters. Typical
 474+ * keys are width, height and page.
 475+ * @param integer $flags A bitfield, may contain self::RENDER_NOW to force rendering
 476+ * @return MediaTransformOutput
 477+ */
 478+ function transform( $params, $flags = 0 ) {
 479+ global $wgUseSquid, $wgIgnoreImageErrors;
 480+
 481+ wfProfileIn( __METHOD__ );
 482+ do {
 483+ if ( !$this->getHandler() || !$this->handler->canRender() ) {
 484+ // not a bitmap or renderable image, don't try.
 485+ $thumb = $this->iconThumb();
 486+ break;
 487+ }
 488+
 489+ $script = $this->getTransformScript();
 490+ if ( $script && !($flags & self::RENDER_NOW) ) {
 491+ // Use a script to transform on client request
 492+ $thumb = $this->handler->getScriptedTransform( $this, $script, $params );
 493+ break;
 494+ }
 495+
 496+ $normalisedParams = $params;
 497+ $this->handler->normaliseParams( $this, $normalisedParams );
 498+ $thumbName = $this->thumbName( $normalisedParams );
 499+ $thumbPath = $this->getThumbPath( $thumbName );
 500+ $thumbUrl = $this->getThumbUrl( $thumbName );
 501+
 502+ if ( $this->repo->canTransformVia404() && !($flags & self::RENDER_NOW ) ) {
 503+ $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
 504+ break;
 505+ }
 506+
 507+ wfDebug( "Doing stat for $thumbPath\n" );
 508+ $this->migrateThumbFile( $thumbName );
 509+ if ( file_exists( $thumbPath ) ) {
 510+ $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
 511+ break;
 512+ }
 513+ $thumb = $this->handler->doTransform( $this, $thumbPath, $thumbUrl, $params );
 514+
 515+ // Ignore errors if requested
 516+ if ( !$thumb ) {
 517+ $thumb = null;
 518+ } elseif ( $thumb->isError() ) {
 519+ $this->lastError = $thumb->toText();
 520+ if ( $wgIgnoreImageErrors && !($flags & self::RENDER_NOW) ) {
 521+ $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
 522+ }
 523+ }
 524+
 525+ if ( $wgUseSquid ) {
 526+ wfPurgeSquidServers( array( $thumbUrl ) );
 527+ }
 528+ } while (false);
 529+
 530+ wfProfileOut( __METHOD__ );
 531+ return $thumb;
 532+ }
 533+
 534+ /**
 535+ * Hook into transform() to allow migration of thumbnail files
 536+ * STUB
 537+ * Overridden by LocalFile
 538+ */
 539+ function migrateThumbFile() {}
 540+
 541+ /**
 542+ * Get a MediaHandler instance for this file
 543+ */
 544+ function getHandler() {
 545+ if ( !isset( $this->handler ) ) {
 546+ $this->handler = MediaHandler::getHandler( $this->getMimeType() );
 547+ }
 548+ return $this->handler;
 549+ }
 550+
 551+ /**
 552+ * Get a ThumbnailImage representing a file type icon
 553+ * @return ThumbnailImage
 554+ */
 555+ function iconThumb() {
 556+ global $wgStylePath, $wgStyleDirectory;
 557+
 558+ $try = array( 'fileicon-' . $this->getExtension() . '.png', 'fileicon.png' );
 559+ foreach( $try as $icon ) {
 560+ $path = '/common/images/icons/' . $icon;
 561+ $filepath = $wgStyleDirectory . $path;
 562+ if( file_exists( $filepath ) ) {
 563+ return new ThumbnailImage( $wgStylePath . $path, 120, 120 );
 564+ }
 565+ }
 566+ return null;
 567+ }
 568+
 569+ /**
 570+ * Get last thumbnailing error.
 571+ * Largely obsolete.
 572+ */
 573+ function getLastError() {
 574+ return $this->lastError;
 575+ }
 576+
 577+ /**
 578+ * Get all thumbnail names previously generated for this file
 579+ * STUB
 580+ * Overridden by LocalFile
 581+ */
 582+ function getThumbnails() { return array(); }
 583+
 584+ /**
 585+ * Purge shared caches such as thumbnails and DB data caching
 586+ * STUB
 587+ * Overridden by LocalFile
 588+ */
 589+ function purgeCache( $archiveFiles = array() ) {}
 590+
 591+ /**
 592+ * Purge the file description page, but don't go after
 593+ * pages using the file. Use when modifying file history
 594+ * but not the current data.
 595+ */
 596+ function purgeDescription() {
 597+ $title = $this->getTitle();
 598+ if ( $title ) {
 599+ $title->invalidateCache();
 600+ $title->purgeSquid();
 601+ }
 602+ }
 603+
 604+ /**
 605+ * Purge metadata and all affected pages when the file is created,
 606+ * deleted, or majorly updated. A set of additional URLs may be
 607+ * passed to purge, such as specific file files which have changed.
 608+ * @param $urlArray array
 609+ */
 610+ function purgeEverything( $urlArr=array() ) {
 611+ // Delete thumbnails and refresh file metadata cache
 612+ $this->purgeCache();
 613+ $this->purgeDescription();
 614+
 615+ // Purge cache of all pages using this file
 616+ $title = $this->getTitle();
 617+ if ( $title ) {
 618+ $update = new HTMLCacheUpdate( $title, 'imagelinks' );
 619+ $update->doUpdate();
 620+ }
 621+ }
 622+
 623+ /**
 624+ * Return the history of this file, line by line. Starts with current version,
 625+ * then old versions. Should return an object similar to an image/oldimage
 626+ * database row.
 627+ *
 628+ * @public
 629+ * STUB
 630+ * Overridden in LocalFile
 631+ */
 632+ function nextHistoryLine() {
 633+ return false;
 634+ }
 635+
 636+ /**
 637+ * Reset the history pointer to the first element of the history
 638+ * @public
 639+ * STUB
 640+ * Overridden in LocalFile.
 641+ */
 642+ function resetHistory() {}
 643+
 644+ /**
 645+ * Get the filename hash component of the directory including trailing slash,
 646+ * e.g. f/fa/
 647+ * If the repository is not hashed, returns an empty string.
 648+ */
 649+ function getHashPath() {
 650+ if ( !isset( $this->hashPath ) ) {
 651+ $this->hashPath = $this->repo->getHashPath( $this->getName() );
 652+ }
 653+ return $this->hashPath;
 654+ }
 655+
 656+ /**
 657+ * Get the path of the file relative to the public zone root
 658+ */
 659+ function getRel() {
 660+ return $this->getHashPath() . $this->getName();
 661+ }
 662+
 663+ /**
 664+ * Get urlencoded relative path of the file
 665+ */
 666+ function getUrlRel() {
 667+ return $this->getHashPath() . urlencode( $this->getName() );
 668+ }
 669+
 670+ /** Get the path of the archive directory, or a particular file if $suffix is specified */
 671+ function getArchivePath( $suffix = false ) {
 672+ $path = $this->repo->getZonePath('public') . '/archive/' . $this->getHashPath();
 673+ if ( $suffix !== false ) {
 674+ $path .= '/' . $suffix;
 675+ }
 676+ return $path;
 677+ }
 678+
 679+ /** Get the path of the thumbnail directory, or a particular file if $suffix is specified */
 680+ function getThumbPath( $suffix = false ) {
 681+ $path = $this->repo->getZonePath('public') . '/thumb/' . $this->getRel();
 682+ if ( $suffix !== false ) {
 683+ $path .= '/' . $suffix;
 684+ }
 685+ return $path;
 686+ }
 687+
 688+ /** Get the URL of the archive directory, or a particular file if $suffix is specified */
 689+ function getArchiveUrl( $suffix = false ) {
 690+ $path = $this->repo->getZoneUrl('public') . '/archive/' . $this->getHashPath();
 691+ if ( $suffix !== false ) {
 692+ $path .= '/' . urlencode( $suffix );
 693+ }
 694+ return $path;
 695+ }
 696+
 697+ /** Get the URL of the thumbnail directory, or a particular file if $suffix is specified */
 698+ function getThumbUrl( $suffix = false ) {
 699+ $path = $this->repo->getZoneUrl('public') . '/thumb/' . $this->getUrlRel();
 700+ if ( $suffix !== false ) {
 701+ $path .= '/' . urlencode( $suffix );
 702+ }
 703+ return $path;
 704+ }
 705+
 706+ /** Get the virtual URL for an archive file or directory */
 707+ function getArchiveVirtualUrl( $suffix = false ) {
 708+ $path = $this->repo->getVirtualUrl() . '/public/archive/' . $this->getHashPath();
 709+ if ( $suffix !== false ) {
 710+ $path .= '/' . urlencode( $suffix );
 711+ }
 712+ return $path;
 713+ }
 714+
 715+ /** Get the virtual URL for a thumbnail file or directory */
 716+ function getThumbVirtualUrl( $suffix = false ) {
 717+ $path = $this->repo->getVirtualUrl() . '/public/thumb/' . $this->getHashPath();
 718+ if ( $suffix !== false ) {
 719+ $path .= '/' . urlencode( $suffix );
 720+ }
 721+ return $path;
 722+ }
 723+
 724+ /**
 725+ * @return bool
 726+ */
 727+ function isHashed() {
 728+ return $this->repo->isHashed();
 729+ }
 730+
 731+ function readOnlyError() {
 732+ throw new MWException( get_class($this) . ': write operations are not supported' );
 733+ }
 734+
 735+ /**
 736+ * Record a file upload in the upload log and the image table
 737+ * STUB
 738+ * Overridden by LocalFile
 739+ */
 740+ function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', $watch = false ) {
 741+ $this->readOnlyError();
 742+ }
 743+
 744+ /**
 745+ * Move or copy a file to its public location. If a file exists at the
 746+ * destination, move it to an archive. Returns the archive name on success
 747+ * or an empty string if it was a new file, and a wikitext-formatted
 748+ * WikiError object on failure.
 749+ *
 750+ * The archive name should be passed through to recordUpload for database
 751+ * registration.
 752+ *
 753+ * @param string $sourcePath Local filesystem path to the source image
 754+ * @param integer $flags A bitwise combination of:
 755+ * File::DELETE_SOURCE Delete the source file, i.e. move
 756+ * rather than copy
 757+ * @return The archive name on success or an empty string if it was a new
 758+ * file, and a wikitext-formatted WikiError object on failure.
 759+ *
 760+ * STUB
 761+ * Overridden by LocalFile
 762+ */
 763+ function publish( $srcPath, $flags = 0 ) {
 764+ $this->readOnlyError();
 765+ }
 766+
 767+ /**
 768+ * Get an array of Title objects which are articles which use this file
 769+ * Also adds their IDs to the link cache
 770+ *
 771+ * This is mostly copied from Title::getLinksTo()
 772+ *
 773+ * @deprecated Use HTMLCacheUpdate, this function uses too much memory
 774+ */
 775+ function getLinksTo( $options = '' ) {
 776+ wfProfileIn( __METHOD__ );
 777+
 778+ // Note: use local DB not repo DB, we want to know local links
 779+ if ( $options ) {
 780+ $db = wfGetDB( DB_MASTER );
 781+ } else {
 782+ $db = wfGetDB( DB_SLAVE );
 783+ }
 784+ $linkCache =& LinkCache::singleton();
 785+
 786+ list( $page, $imagelinks ) = $db->tableNamesN( 'page', 'imagelinks' );
 787+ $encName = $db->addQuotes( $this->getName() );
 788+ $sql = "SELECT page_namespace,page_title,page_id FROM $page,$imagelinks WHERE page_id=il_from AND il_to=$encName $options";
 789+ $res = $db->query( $sql, __METHOD__ );
 790+
 791+ $retVal = array();
 792+ if ( $db->numRows( $res ) ) {
 793+ while ( $row = $db->fetchObject( $res ) ) {
 794+ if ( $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ) ) {
 795+ $linkCache->addGoodLinkObj( $row->page_id, $titleObj );
 796+ $retVal[] = $titleObj;
 797+ }
 798+ }
 799+ }
 800+ $db->freeResult( $res );
 801+ wfProfileOut( __METHOD__ );
 802+ return $retVal;
 803+ }
 804+
 805+ function getExifData() {
 806+ if ( !$this->getHandler() || $this->handler->getMetadataType( $this ) != 'exif' ) {
 807+ return array();
 808+ }
 809+ $metadata = $this->getMetadata();
 810+ if ( !$metadata ) {
 811+ return array();
 812+ }
 813+ $exif = unserialize( $metadata );
 814+ if ( !$exif ) {
 815+ return array();
 816+ }
 817+ unset( $exif['MEDIAWIKI_EXIF_VERSION'] );
 818+ $format = new FormatExif( $exif );
 819+
 820+ return $format->getFormattedData();
 821+ }
 822+
 823+ /**
 824+ * Returns true if the file comes from the local file repository.
 825+ *
 826+ * @return bool
 827+ */
 828+ function isLocal() {
 829+ return $this->repo && $this->repo->getName() == 'local';
 830+ }
 831+
 832+ /**
 833+ * Returns true if the image is an old version
 834+ * STUB
 835+ */
 836+ function isOld() {
 837+ return false;
 838+ }
 839+
 840+ /**
 841+ * Is this file a "deleted" file in a private archive?
 842+ * STUB
 843+ */
 844+ function isDeleted( $field ) {
 845+ return false;
 846+ }
 847+
 848+ /**
 849+ * Was this file ever deleted from the wiki?
 850+ *
 851+ * @return bool
 852+ */
 853+ function wasDeleted() {
 854+ $title = $this->getTitle();
 855+ return $title && $title->isDeleted() > 0;
 856+ }
 857+
 858+ /**
 859+ * Delete all versions of the file.
 860+ *
 861+ * Moves the files into an archive directory (or deletes them)
 862+ * and removes the database rows.
 863+ *
 864+ * Cache purging is done; logging is caller's responsibility.
 865+ *
 866+ * @param $reason
 867+ * @return true on success, false on some kind of failure
 868+ * STUB
 869+ * Overridden by LocalFile
 870+ */
 871+ function delete( $reason, $suppress=false ) {
 872+ $this->readOnlyError();
 873+ }
 874+
 875+ /**
 876+ * Restore all or specified deleted revisions to the given file.
 877+ * Permissions and logging are left to the caller.
 878+ *
 879+ * May throw database exceptions on error.
 880+ *
 881+ * @param $versions set of record ids of deleted items to restore,
 882+ * or empty to restore all revisions.
 883+ * @return the number of file revisions restored if successful,
 884+ * or false on failure
 885+ * STUB
 886+ * Overridden by LocalFile
 887+ */
 888+ function restore( $versions=array(), $Unsuppress=false ) {
 889+ $this->readOnlyError();
 890+ }
 891+
 892+ /**
 893+ * Returns 'true' if this image is a multipage document, e.g. a DJVU
 894+ * document.
 895+ *
 896+ * @return Bool
 897+ */
 898+ function isMultipage() {
 899+ return $this->getHandler() && $this->handler->isMultiPage();
 900+ }
 901+
 902+ /**
 903+ * Returns the number of pages of a multipage document, or NULL for
 904+ * documents which aren't multipage documents
 905+ */
 906+ function pageCount() {
 907+ if ( !isset( $this->pageCount ) ) {
 908+ if ( $this->getHandler() && $this->handler->isMultiPage() ) {
 909+ $this->pageCount = $this->handler->pageCount( $this );
 910+ } else {
 911+ $this->pageCount = false;
 912+ }
 913+ }
 914+ return $this->pageCount;
 915+ }
 916+
 917+ /**
 918+ * Calculate the height of a thumbnail using the source and destination width
 919+ */
 920+ static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) {
 921+ // Exact integer multiply followed by division
 922+ if ( $srcWidth == 0 ) {
 923+ return 0;
 924+ } else {
 925+ return round( $srcHeight * $dstWidth / $srcWidth );
 926+ }
 927+ }
 928+
 929+ /**
 930+ * Get an image size array like that returned by getimagesize(), or false if it
 931+ * can't be determined.
 932+ *
 933+ * @param string $fileName The filename
 934+ * @return array
 935+ */
 936+ function getImageSize( $fileName ) {
 937+ if ( !$this->getHandler() ) {
 938+ return false;
 939+ }
 940+ return $this->handler->getImageSize( $this, $fileName );
 941+ }
 942+
 943+ /**
 944+ * Get the URL of the image description page. May return false if it is
 945+ * unknown or not applicable.
 946+ */
 947+ function getDescriptionUrl() {
 948+ return $this->repo->getDescriptionUrl( $this->getName() );
 949+ }
 950+
 951+ /**
 952+ * Get the HTML text of the description page, if available
 953+ */
 954+ function getDescriptionText() {
 955+ if ( !$this->repo->fetchDescription ) {
 956+ return false;
 957+ }
 958+ $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName() );
 959+ if ( $renderUrl ) {
 960+ wfDebug( "Fetching shared description from $renderUrl\n" );
 961+ return Http::get( $renderUrl );
 962+ } else {
 963+ return false;
 964+ }
 965+ }
 966+
 967+ /**
 968+ * Get the 14-character timestamp of the file upload, or false if
 969+ */
 970+ function getTimestmap() {
 971+ $path = $this->getPath();
 972+ if ( !file_exists( $path ) ) {
 973+ return false;
 974+ }
 975+ return wfTimestamp( filemtime( $path ) );
 976+ }
 977+
 978+ /**
 979+ * Determine if the current user is allowed to view a particular
 980+ * field of this file, if it's marked as deleted.
 981+ * STUB
 982+ * @param int $field
 983+ * @return bool
 984+ */
 985+ function userCan( $field ) {
 986+ return true;
 987+ }
 988+}
 989+
 990+?>
Property changes on: branches/liquidthreads/includes/filerepo/File.php
___________________________________________________________________
Added: svn:keywords
1991 + Author Date Id Revision
Added: svn:eol-style
2992 + native
Index: branches/liquidthreads/includes/filerepo/ForeignDBFile.php
@@ -0,0 +1,39 @@
 2+<?php
 3+
 4+class ForeignDBFile extends LocalFile {
 5+ function newFromTitle( $title, $repo ) {
 6+ return new self( $title, $repo );
 7+ }
 8+
 9+ function getCacheKey() {
 10+ if ( $this->repo->hasSharedCache ) {
 11+ $hashedName = md5($this->name);
 12+ return wfForeignMemcKey( $this->repo->dbName, $this->repo->tablePrefix,
 13+ 'file', $hashedName );
 14+ } else {
 15+ return false;
 16+ }
 17+ }
 18+
 19+ function publish( /*...*/ ) {
 20+ $this->readOnlyError();
 21+ }
 22+
 23+ function recordUpload( /*...*/ ) {
 24+ $this->readOnlyError();
 25+ }
 26+ function restore( /*...*/ ) {
 27+ $this->readOnlyError();
 28+ }
 29+
 30+ function getDescriptionUrl() {
 31+ // Restore remote behaviour
 32+ return File::getDescriptionUrl();
 33+ }
 34+
 35+ function getDescriptionText() {
 36+ // Restore remote behaviour
 37+ return File::getDescriptionText();
 38+ }
 39+}
 40+?>
Property changes on: branches/liquidthreads/includes/filerepo/ForeignDBFile.php
___________________________________________________________________
Added: svn:eol-style
141 + native
Index: branches/liquidthreads/includes/filerepo/LocalRepo.php
@@ -0,0 +1,31 @@
 2+<?php
 3+/**
 4+ * A repository that stores files in the local filesystem and registers them
 5+ * in the wiki's own database. This is the most commonly used repository class.
 6+ */
 7+class LocalRepo extends FSRepo {
 8+ var $fileFactory = array( 'LocalFile', 'newFromTitle' );
 9+ var $oldFileFactory = array( 'OldLocalFile', 'newFromTitle' );
 10+
 11+ function getSlaveDB() {
 12+ return wfGetDB( DB_SLAVE );
 13+ }
 14+
 15+ function getMasterDB() {
 16+ return wfGetDB( DB_MASTER );
 17+ }
 18+
 19+ function newFileFromRow( $row ) {
 20+ if ( isset( $row->img_name ) ) {
 21+ return LocalFile::newFromRow( $row, $this );
 22+ } elseif ( isset( $row->oi_name ) ) {
 23+ return OldLocalFile::newFromRow( $row, $this );
 24+ } else {
 25+ throw new MWException( __METHOD__.': invalid row' );
 26+ }
 27+ }
 28+
 29+ function newFromArchiveName( $title, $archiveName ) {
 30+ return OldLocalFile::newFromArchiveName( $title, $this, $archiveName );
 31+ }
 32+}
Property changes on: branches/liquidthreads/includes/filerepo/LocalRepo.php
___________________________________________________________________
Added: svn:eol-style
133 + native
Index: branches/liquidthreads/includes/filerepo/RepoGroup.php
@@ -0,0 +1,113 @@
 2+<?php
 3+
 4+/**
 5+ * Prioritized list of file repositories
 6+ * @addtogroup filerepo
 7+ */
 8+class RepoGroup {
 9+ var $localRepo, $foreignRepos, $reposInitialised = false;
 10+ var $localInfo, $foreignInfo;
 11+
 12+ protected static $instance;
 13+
 14+ /**
 15+ * Get a RepoGroup instance. At present only one instance of RepoGroup is
 16+ * needed in a MediaWiki invocation, this may change in the future.
 17+ */
 18+ function singleton() {
 19+ if ( self::$instance ) {
 20+ return self::$instance;
 21+ }
 22+ global $wgLocalFileRepo, $wgForeignFileRepos;
 23+ self::$instance = new RepoGroup( $wgLocalFileRepo, $wgForeignFileRepos );
 24+ return self::$instance;
 25+ }
 26+
 27+ /**
 28+ * Construct a group of file repositories.
 29+ * @param array $data Array of repository info arrays.
 30+ * Each info array is an associative array with the 'class' member
 31+ * giving the class name. The entire array is passed to the repository
 32+ * constructor as the first parameter.
 33+ */
 34+ function __construct( $localInfo, $foreignInfo ) {
 35+ $this->localInfo = $localInfo;
 36+ $this->foreignInfo = $foreignInfo;
 37+ }
 38+
 39+ /**
 40+ * Search repositories for an image.
 41+ * You can also use wfGetFile() to do this.
 42+ * @param mixed $title Title object or string
 43+ * @param mixed $time The 14-char timestamp before which the file should
 44+ * have been uploaded, or false for the current version
 45+ * @return File object or false if it is not found
 46+ */
 47+ function findFile( $title, $time = false ) {
 48+ if ( !$this->reposInitialised ) {
 49+ $this->initialiseRepos();
 50+ }
 51+
 52+ $image = $this->localRepo->findFile( $title, $time );
 53+ if ( $image ) {
 54+ return $image;
 55+ }
 56+ foreach ( $this->foreignRepos as $repo ) {
 57+ $image = $repo->findFile( $title, $time );
 58+ if ( $image ) {
 59+ return $image;
 60+ }
 61+ }
 62+ return false;
 63+ }
 64+
 65+ /**
 66+ * Get the repo instance with a given key.
 67+ */
 68+ function getRepo( $index ) {
 69+ if ( !$this->reposInitialised ) {
 70+ $this->initialiseRepos();
 71+ }
 72+ if ( $index == 'local' ) {
 73+ return $this->localRepo;
 74+ } elseif ( isset( $this->foreignRepos[$index] ) ) {
 75+ return $this->foreignRepos[$index];
 76+ } else {
 77+ return false;
 78+ }
 79+ }
 80+
 81+ /**
 82+ * Get the local repository, i.e. the one corresponding to the local image
 83+ * table. Files are typically uploaded to the local repository.
 84+ */
 85+ function getLocalRepo() {
 86+ return $this->getRepo( 'local' );
 87+ }
 88+
 89+ /**
 90+ * Initialise the $repos array
 91+ */
 92+ function initialiseRepos() {
 93+ if ( $this->reposInitialised ) {
 94+ return;
 95+ }
 96+ $this->reposInitialised = true;
 97+
 98+ $this->localRepo = $this->newRepo( $this->localInfo );
 99+ $this->foreignRepos = array();
 100+ foreach ( $this->foreignInfo as $key => $info ) {
 101+ $this->foreignRepos[$key] = $this->newRepo( $info );
 102+ }
 103+ }
 104+
 105+ /**
 106+ * Create a repo class based on an info structure
 107+ */
 108+ protected function newRepo( $info ) {
 109+ $class = $info['class'];
 110+ return new $class( $info );
 111+ }
 112+}
 113+
 114+?>
Property changes on: branches/liquidthreads/includes/filerepo/RepoGroup.php
___________________________________________________________________
Added: svn:eol-style
1115 + native
Index: branches/liquidthreads/includes/filerepo/ArchivedFile.php
@@ -0,0 +1,108 @@
 2+<?php
 3+
 4+/**
 5+ * @addtogroup Media
 6+ */
 7+class ArchivedFile
 8+{
 9+ /**
 10+ * Returns a file object from the filearchive table
 11+ * @param $title, the corresponding image page title
 12+ * @param $id, the image id, a unique key
 13+ * @param $key, optional storage key
 14+ * @return ResultWrapper
 15+ */
 16+ function ArchivedFile( $title, $id=0, $key='' ) {
 17+ if( !is_object( $title ) ) {
 18+ throw new MWException( 'ArchivedFile constructor given bogus title.' );
 19+ }
 20+ $conds = ($id) ? "fa_id = $id" : "fa_storage_key = '$key'";
 21+ if( $title->getNamespace() == NS_IMAGE ) {
 22+ $dbr = wfGetDB( DB_SLAVE );
 23+ $res = $dbr->select( 'filearchive',
 24+ array(
 25+ 'fa_id',
 26+ 'fa_name',
 27+ 'fa_storage_key',
 28+ 'fa_storage_group',
 29+ 'fa_size',
 30+ 'fa_bits',
 31+ 'fa_width',
 32+ 'fa_height',
 33+ 'fa_metadata',
 34+ 'fa_media_type',
 35+ 'fa_major_mime',
 36+ 'fa_minor_mime',
 37+ 'fa_description',
 38+ 'fa_user',
 39+ 'fa_user_text',
 40+ 'fa_timestamp',
 41+ 'fa_deleted' ),
 42+ array(
 43+ 'fa_name' => $title->getDbKey(),
 44+ $conds ),
 45+ __METHOD__,
 46+ array( 'ORDER BY' => 'fa_timestamp DESC' ) );
 47+
 48+ if ( $dbr->numRows( $res ) == 0 ) {
 49+ // this revision does not exist?
 50+ return;
 51+ }
 52+ $ret = $dbr->resultObject( $res );
 53+ $row = $ret->fetchObject();
 54+
 55+ // initialize fields for filestore image object
 56+ $this->mId = intval($row->fa_id);
 57+ $this->mName = $row->fa_name;
 58+ $this->mGroup = $row->fa_storage_group;
 59+ $this->mKey = $row->fa_storage_key;
 60+ $this->mSize = $row->fa_size;
 61+ $this->mBits = $row->fa_bits;
 62+ $this->mWidth = $row->fa_width;
 63+ $this->mHeight = $row->fa_height;
 64+ $this->mMetaData = $row->fa_metadata;
 65+ $this->mMime = "$row->fa_major_mime/$row->fa_minor_mime";
 66+ $this->mType = $row->fa_media_type;
 67+ $this->mDescription = $row->fa_description;
 68+ $this->mUser = $row->fa_user;
 69+ $this->mUserText = $row->fa_user_text;
 70+ $this->mTimestamp = $row->fa_timestamp;
 71+ $this->mDeleted = $row->fa_deleted;
 72+ } else {
 73+ throw new MWException( 'This title does not correspond to an image page.' );
 74+ return;
 75+ }
 76+ return true;
 77+ }
 78+
 79+ /**
 80+ * int $field one of DELETED_* bitfield constants
 81+ * for file or revision rows
 82+ * @return bool
 83+ */
 84+ function isDeleted( $field ) {
 85+ return ($this->mDeleted & $field) == $field;
 86+ }
 87+
 88+ /**
 89+ * Determine if the current user is allowed to view a particular
 90+ * field of this FileStore image file, if it's marked as deleted.
 91+ * @param int $field
 92+ * @return bool
 93+ */
 94+ function userCan( $field ) {
 95+ if( isset($this->mDeleted) && ($this->mDeleted & $field) == $field ) {
 96+ // images
 97+ global $wgUser;
 98+ $permission = ( $this->mDeleted & File::DELETED_RESTRICTED ) == File::DELETED_RESTRICTED
 99+ ? 'hiderevision'
 100+ : 'deleterevision';
 101+ wfDebug( "Checking for $permission due to $field match on $this->mDeleted\n" );
 102+ return $wgUser->isAllowed( $permission );
 103+ } else {
 104+ return true;
 105+ }
 106+ }
 107+}
 108+
 109+?>
Property changes on: branches/liquidthreads/includes/filerepo/ArchivedFile.php
___________________________________________________________________
Added: svn:eol-style
1110 + native
Index: branches/liquidthreads/includes/filerepo/ForeignDBRepo.php
@@ -0,0 +1,51 @@
 2+<?php
 3+
 4+/**
 5+ * A foreign repository with an accessible MediaWiki database
 6+ */
 7+
 8+class ForeignDBRepo extends LocalRepo {
 9+ # Settings
 10+ var $dbType, $dbServer, $dbUser, $dbPassword, $dbName, $dbFlags,
 11+ $tablePrefix, $hasSharedCache;
 12+
 13+ # Other stuff
 14+ var $dbConn;
 15+ var $fileFactory = array( 'ForeignDBFile', 'newFromTitle' );
 16+
 17+ function __construct( $info ) {
 18+ parent::__construct( $info );
 19+ $this->dbType = $info['dbType'];
 20+ $this->dbServer = $info['dbServer'];
 21+ $this->dbUser = $info['dbUser'];
 22+ $this->dbPassword = $info['dbPassword'];
 23+ $this->dbName = $info['dbName'];
 24+ $this->dbFlags = $info['dbFlags'];
 25+ $this->tablePrefix = $info['tablePrefix'];
 26+ $this->hasSharedCache = $info['hasSharedCache'];
 27+ }
 28+
 29+ function getMasterDB() {
 30+ if ( !isset( $this->dbConn ) ) {
 31+ $class = 'Database' . ucfirst( $this->dbType );
 32+ $this->dbConn = new $class( $this->dbServer, $this->dbUser,
 33+ $this->dbPassword, $this->dbName, false, $this->dbFlags,
 34+ $this->tablePrefix );
 35+ }
 36+ return $this->dbConn;
 37+ }
 38+
 39+ function getSlaveDB() {
 40+ return $this->getMasterDB();
 41+ }
 42+
 43+ function hasSharedCache() {
 44+ return $this->hasSharedCache;
 45+ }
 46+
 47+ function store( /*...*/ ) {
 48+ throw new MWException( get_class($this) . ': write operations are not supported' );
 49+ }
 50+}
 51+
 52+?>
Property changes on: branches/liquidthreads/includes/filerepo/ForeignDBRepo.php
___________________________________________________________________
Added: svn:eol-style
153 + native
Index: branches/liquidthreads/includes/filerepo/OldLocalFile.php
@@ -0,0 +1,222 @@
 2+<?php
 3+
 4+/**
 5+ * Class to represent a file in the oldimage table
 6+ *
 7+ * @addtogroup FileRepo
 8+ */
 9+class OldLocalFile extends LocalFile {
 10+ var $requestedTime, $archive_name;
 11+
 12+ const CACHE_VERSION = 1;
 13+ const MAX_CACHE_ROWS = 20;
 14+
 15+ function newFromTitle( $title, $repo, $time ) {
 16+ return new self( $title, $repo, $time, null );
 17+ }
 18+
 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 ) {
 37+ parent::__construct( $title, $repo );
 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+ }
 43+ }
 44+
 45+ function getCacheKey() {
 46+ $hashedName = md5($this->getName());
 47+ return wfMemcKey( 'oldfile', $hashedName );
 48+ }
 49+
 50+ function getArchiveName() {
 51+ if ( !isset( $this->archive_name ) ) {
 52+ $this->load();
 53+ }
 54+ return $this->archive_name;
 55+ }
 56+
 57+ function isOld() {
 58+ return true;
 59+ }
 60+
 61+ /**
 62+ * Try to load file metadata from memcached. Returns true on success.
 63+ */
 64+ function loadFromCache() {
 65+ global $wgMemc;
 66+ wfProfileIn( __METHOD__ );
 67+ $this->dataLoaded = false;
 68+ $key = $this->getCacheKey();
 69+ if ( !$key ) {
 70+ return false;
 71+ }
 72+ $oldImages = $wgMemc->get( $key );
 73+
 74+ if ( isset( $oldImages['version'] ) && $oldImages['version'] == MW_OLDFILE_VERSION ) {
 75+ unset( $oldImages['version'] );
 76+ $more = isset( $oldImages['more'] );
 77+ unset( $oldImages['more'] );
 78+ $found = false;
 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+ }
 85+ }
 86+ } else {
 87+ krsort( $oldImages );
 88+ foreach ( $oldImages as $timestamp => $info ) {
 89+ if ( $timestamp <= $this->requestedTime ) {
 90+ $found = true;
 91+ break;
 92+ }
 93+ }
 94+ }
 95+ if ( $found ) {
 96+ wfDebug( "Pulling file metadata from cache key {$key}[{$timestamp}]\n" );
 97+ $this->dataLoaded = true;
 98+ foreach ( $cachedValues as $name => $value ) {
 99+ $this->$name = $value;
 100+ }
 101+ } elseif ( $more ) {
 102+ wfDebug( "Cache key was truncated, oldimage row might be found in the database\n" );
 103+ } else {
 104+ wfDebug( "Image did not exist at the specified time.\n" );
 105+ $this->fileExists = false;
 106+ $this->dataLoaded = true;
 107+ }
 108+ }
 109+
 110+ if ( $this->dataLoaded ) {
 111+ wfIncrStats( 'image_cache_hit' );
 112+ } else {
 113+ wfIncrStats( 'image_cache_miss' );
 114+ }
 115+
 116+ wfProfileOut( __METHOD__ );
 117+ return $this->dataLoaded;
 118+ }
 119+
 120+ function saveToCache() {
 121+ // Cache the entire history of the image (up to MAX_CACHE_ROWS).
 122+ // This is expensive, so we only do it if $wgMemc is real
 123+ global $wgMemc;
 124+ if ( $wgMemc instanceof FakeMemcachedClient ) {
 125+ return;
 126+ }
 127+ $key = $this->getCacheKey();
 128+ if ( !$key ) {
 129+ return;
 130+ }
 131+ wfProfileIn( __METHOD__ );
 132+
 133+ $dbr = $this->repo->getSlaveDB();
 134+ $res = $dbr->select( 'oldimage', $this->getCacheFields(),
 135+ array( 'oi_name' => $this->getName() ), __METHOD__,
 136+ array(
 137+ 'LIMIT' => self::MAX_CACHE_ROWS + 1,
 138+ 'ORDER BY' => 'oi_timestamp DESC',
 139+ ));
 140+ $cache = array( 'version' => self::CACHE_VERSION );
 141+ $numRows = $dbr->numRows( $res );
 142+ if ( $numRows > self::MAX_CACHE_ROWS ) {
 143+ $cache['more'] = true;
 144+ $numRows--;
 145+ }
 146+ for ( $i = 0; $i < $numRows; $i++ ) {
 147+ $row = $dbr->fetchObject( $res );
 148+ $this->decodeRow( $row, 'oi_' );
 149+ $cache[$row->oi_timestamp] = $row;
 150+ }
 151+ $dbr->freeResult( $res );
 152+ $wgMemc->set( $key, $cache, 7*86400 /* 1 week */ );
 153+ wfProfileOut( __METHOD__ );
 154+ }
 155+
 156+ function loadFromDB() {
 157+ wfProfileIn( __METHOD__ );
 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+ }
 165+ $row = $dbr->selectRow( 'oldimage', $this->getCacheFields( 'oi_' ),
 166+ $conds, __METHOD__, array( 'ORDER BY' => 'oi_timestamp DESC' ) );
 167+ if ( $row ) {
 168+ $this->loadFromRow( $row, 'oi_' );
 169+ } else {
 170+ $this->fileExists = false;
 171+ }
 172+ $this->dataLoaded = true;
 173+ }
 174+
 175+ function getCacheFields( $prefix = 'img_' ) {
 176+ $fields = parent::getCacheFields( $prefix );
 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;
 183+ }
 184+
 185+ function getRel() {
 186+ return 'archive/' . $this->getHashPath() . $this->getArchiveName();
 187+ }
 188+
 189+ function getUrlRel() {
 190+ return 'archive/' . $this->getHashPath() . urlencode( $this->getArchiveName() );
 191+ }
 192+
 193+ function upgradeRow() {
 194+ wfProfileIn( __METHOD__ );
 195+
 196+ $this->loadFromFile();
 197+
 198+ $dbw = $this->repo->getMasterDB();
 199+ list( $major, $minor ) = self::splitMime( $this->mime );
 200+
 201+ wfDebug(__METHOD__.': upgrading '.$this->archive_name." to the current schema\n");
 202+ $dbw->update( 'oldimage',
 203+ array(
 204+ 'oi_width' => $this->width,
 205+ 'oi_height' => $this->height,
 206+ 'oi_bits' => $this->bits,
 207+ #'oi_media_type' => $this->media_type,
 208+ #'oi_major_mime' => $major,
 209+ #'oi_minor_mime' => $minor,
 210+ #'oi_metadata' => $this->metadata,
 211+ ), array( 'oi_name' => $this->getName(), 'oi_timestamp' => $this->requestedTime ),
 212+ __METHOD__
 213+ );
 214+ wfProfileOut( __METHOD__ );
 215+ }
 216+
 217+ // XXX: Temporary hack before schema update
 218+ function maybeUpgradeRow() {}
 219+
 220+}
 221+
 222+
 223+?>
Index: branches/liquidthreads/includes/filerepo/LocalFile.php
@@ -0,0 +1,1352 @@
 2+<?php
 3+/**
 4+ */
 5+
 6+/**
 7+ * Bump this number when serialized cache records may be incompatible.
 8+ */
 9+define( 'MW_FILE_VERSION', 4 );
 10+
 11+/**
 12+ * Class to represent a local file in the wiki's own database
 13+ *
 14+ * Provides methods to retrieve paths (physical, logical, URL),
 15+ * to generate image thumbnails or for uploading.
 16+ *
 17+ * Note that only the repo object knows what its file class is called. You should
 18+ * never name a file class explictly outside of the repo class. Instead use the
 19+ * repo's factory functions to generate file objects, for example:
 20+ *
 21+ * RepoGroup::singleton()->getLocalRepo()->newFile($title);
 22+ *
 23+ * The convenience functions wfLocalFile() and wfFindFile() should be sufficient
 24+ * in most cases.
 25+ *
 26+ * @addtogroup FileRepo
 27+ */
 28+class LocalFile extends File
 29+{
 30+ /**#@+
 31+ * @private
 32+ */
 33+ var $fileExists, # does the file file exist on disk? (loadFromXxx)
 34+ $historyLine, # Number of line to return by nextHistoryLine() (constructor)
 35+ $historyRes, # result of the query for the file's history (nextHistoryLine)
 36+ $width, # \
 37+ $height, # |
 38+ $bits, # --- returned by getimagesize (loadFromXxx)
 39+ $attr, # /
 40+ $media_type, # MEDIATYPE_xxx (bitmap, drawing, audio...)
 41+ $mime, # MIME type, determined by MimeMagic::guessMimeType
 42+ $major_mime, # Major mime type
 43+ $minor_mine, # Minor mime type
 44+ $size, # Size in bytes (loadFromXxx)
 45+ $metadata, # Metadata
 46+ $timestamp, # Upload timestamp
 47+ $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx)
 48+ $upgraded; # Whether the row was upgraded on load
 49+
 50+ /**#@-*/
 51+
 52+ /**
 53+ * Create a LocalFile from a title
 54+ * Do not call this except from inside a repo class.
 55+ */
 56+ function newFromTitle( $title, $repo ) {
 57+ return new self( $title, $repo );
 58+ }
 59+
 60+ /**
 61+ * Create a LocalFile from a title
 62+ * Do not call this except from inside a repo class.
 63+ */
 64+ function newFromRow( $row, $repo ) {
 65+ $title = Title::makeTitle( NS_IMAGE, $row->img_name );
 66+ $file = new self( $title, $repo );
 67+ $file->loadFromRow( $row );
 68+ return $file;
 69+ }
 70+
 71+ /**
 72+ * Constructor.
 73+ * Do not call this except from inside a repo class.
 74+ */
 75+ function __construct( $title, $repo ) {
 76+ if( !is_object( $title ) ) {
 77+ throw new MWException( __CLASS__.' constructor given bogus title.' );
 78+ }
 79+ parent::__construct( $title, $repo );
 80+ $this->metadata = '';
 81+ $this->historyLine = 0;
 82+ $this->dataLoaded = false;
 83+ }
 84+
 85+ /**
 86+ * Get the memcached key
 87+ */
 88+ function getCacheKey() {
 89+ $hashedName = md5($this->getName());
 90+ return wfMemcKey( 'file', $hashedName );
 91+ }
 92+
 93+ /**
 94+ * Try to load file metadata from memcached. Returns true on success.
 95+ */
 96+ function loadFromCache() {
 97+ global $wgMemc;
 98+ wfProfileIn( __METHOD__ );
 99+ $this->dataLoaded = false;
 100+ $key = $this->getCacheKey();
 101+ if ( !$key ) {
 102+ return false;
 103+ }
 104+ $cachedValues = $wgMemc->get( $key );
 105+
 106+ // Check if the key existed and belongs to this version of MediaWiki
 107+ if ( isset($cachedValues['version']) && ( $cachedValues['version'] == MW_FILE_VERSION ) ) {
 108+ wfDebug( "Pulling file metadata from cache key $key\n" );
 109+ $this->fileExists = $cachedValues['fileExists'];
 110+ if ( $this->fileExists ) {
 111+ unset( $cachedValues['version'] );
 112+ unset( $cachedValues['fileExists'] );
 113+ foreach ( $cachedValues as $name => $value ) {
 114+ $this->$name = $value;
 115+ }
 116+ }
 117+ }
 118+ if ( $this->dataLoaded ) {
 119+ wfIncrStats( 'image_cache_hit' );
 120+ } else {
 121+ wfIncrStats( 'image_cache_miss' );
 122+ }
 123+
 124+ wfProfileOut( __METHOD__ );
 125+ return $this->dataLoaded;
 126+ }
 127+
 128+ /**
 129+ * Save the file metadata to memcached
 130+ */
 131+ function saveToCache() {
 132+ global $wgMemc;
 133+ $this->load();
 134+ $key = $this->getCacheKey();
 135+ if ( !$key ) {
 136+ return;
 137+ }
 138+ $fields = $this->getCacheFields( '' );
 139+ $cache = array( 'version' => MW_FILE_VERSION );
 140+ $cache['fileExists'] = $this->fileExists;
 141+ if ( $this->fileExists ) {
 142+ foreach ( $fields as $field ) {
 143+ $cache[$field] = $this->$field;
 144+ }
 145+ }
 146+
 147+ $wgMemc->set( $key, $cache, 60 * 60 * 24 * 7 ); // A week
 148+ }
 149+
 150+ /**
 151+ * Load metadata from the file itself
 152+ */
 153+ function loadFromFile() {
 154+ wfProfileIn( __METHOD__ );
 155+ $path = $this->getPath();
 156+ $this->fileExists = file_exists( $path );
 157+ $gis = array();
 158+
 159+ if ( $this->fileExists ) {
 160+ $magic=& MimeMagic::singleton();
 161+
 162+ $this->mime = $magic->guessMimeType($path,true);
 163+ list( $this->major_mime, $this->minor_mime ) = self::splitMime( $this->mime );
 164+ $this->media_type = $magic->getMediaType($path,$this->mime);
 165+ $handler = MediaHandler::getHandler( $this->mime );
 166+
 167+ # Get size in bytes
 168+ $this->size = filesize( $path );
 169+
 170+ # Height, width and metadata
 171+ if ( $handler ) {
 172+ $gis = $handler->getImageSize( $this, $path );
 173+ $this->metadata = $handler->getMetadata( $this, $path );
 174+ } else {
 175+ $gis = false;
 176+ $this->metadata = '';
 177+ }
 178+
 179+ wfDebug(__METHOD__.": $path loaded, {$this->size} bytes, {$this->mime}.\n");
 180+ } else {
 181+ $this->mime = NULL;
 182+ $this->media_type = MEDIATYPE_UNKNOWN;
 183+ $this->metadata = '';
 184+ wfDebug(__METHOD__.": $path NOT FOUND!\n");
 185+ }
 186+
 187+ if( $gis ) {
 188+ $this->width = $gis[0];
 189+ $this->height = $gis[1];
 190+ } else {
 191+ $this->width = 0;
 192+ $this->height = 0;
 193+ }
 194+
 195+ #NOTE: $gis[2] contains a code for the image type. This is no longer used.
 196+
 197+ #NOTE: we have to set this flag early to avoid load() to be called
 198+ # be some of the functions below. This may lead to recursion or other bad things!
 199+ # as ther's only one thread of execution, this should be safe anyway.
 200+ $this->dataLoaded = true;
 201+
 202+ if ( isset( $gis['bits'] ) ) $this->bits = $gis['bits'];
 203+ else $this->bits = 0;
 204+
 205+ wfProfileOut( __METHOD__ );
 206+ }
 207+
 208+ function getCacheFields( $prefix = 'img_' ) {
 209+ static $fields = array( 'size', 'width', 'height', 'bits', 'media_type',
 210+ 'major_mime', 'minor_mime', 'metadata', 'timestamp' );
 211+ static $results = array();
 212+ if ( $prefix == '' ) {
 213+ return $fields;
 214+ }
 215+ if ( !isset( $results[$prefix] ) ) {
 216+ $prefixedFields = array();
 217+ foreach ( $fields as $field ) {
 218+ $prefixedFields[] = $prefix . $field;
 219+ }
 220+ $results[$prefix] = $prefixedFields;
 221+ }
 222+ return $results[$prefix];
 223+ }
 224+
 225+ /**
 226+ * Load file metadata from the DB
 227+ */
 228+ function loadFromDB() {
 229+ wfProfileIn( __METHOD__ );
 230+
 231+ # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
 232+ $this->dataLoaded = true;
 233+
 234+ $dbr = $this->repo->getSlaveDB();
 235+
 236+ $row = $dbr->selectRow( 'image', $this->getCacheFields( 'img_' ),
 237+ array( 'img_name' => $this->getName() ), __METHOD__ );
 238+ if ( $row ) {
 239+ $this->loadFromRow( $row );
 240+ } else {
 241+ $this->fileExists = false;
 242+ }
 243+
 244+ wfProfileOut( __METHOD__ );
 245+ }
 246+
 247+ /**
 248+ * Decode a row from the database (either object or array) to an array
 249+ * with timestamps and MIME types decoded, and the field prefix removed.
 250+ */
 251+ function decodeRow( $row, $prefix = 'img_' ) {
 252+ $array = (array)$row;
 253+ $prefixLength = strlen( $prefix );
 254+ // Sanity check prefix once
 255+ if ( substr( key( $array ), 0, $prefixLength ) !== $prefix ) {
 256+ throw new MWException( __METHOD__. ': incorrect $prefix parameter' );
 257+ }
 258+ $decoded = array();
 259+ foreach ( $array as $name => $value ) {
 260+ $deprefixedName = substr( $name, $prefixLength );
 261+ $decoded[substr( $name, $prefixLength )] = $value;
 262+ }
 263+ $decoded['timestamp'] = wfTimestamp( TS_MW, $decoded['timestamp'] );
 264+ if ( empty( $decoded['major_mime'] ) ) {
 265+ $decoded['mime'] = "unknown/unknown";
 266+ } else {
 267+ if (!$decoded['minor_mime']) {
 268+ $decoded['minor_mime'] = "unknown";
 269+ }
 270+ $decoded['mime'] = $decoded['major_mime'].'/'.$decoded['minor_mime'];
 271+ }
 272+ return $decoded;
 273+ }
 274+
 275+ /*
 276+ * Load file metadata from a DB result row
 277+ */
 278+ function loadFromRow( $row, $prefix = 'img_' ) {
 279+ $array = $this->decodeRow( $row, $prefix );
 280+ foreach ( $array as $name => $value ) {
 281+ $this->$name = $value;
 282+ }
 283+ $this->fileExists = true;
 284+ // Check for rows from a previous schema, quietly upgrade them
 285+ $this->maybeUpgradeRow();
 286+ }
 287+
 288+ /**
 289+ * Load file metadata from cache or DB, unless already loaded
 290+ */
 291+ function load() {
 292+ if ( !$this->dataLoaded ) {
 293+ if ( !$this->loadFromCache() ) {
 294+ $this->loadFromDB();
 295+ $this->saveToCache();
 296+ }
 297+ $this->dataLoaded = true;
 298+ }
 299+ }
 300+
 301+ /**
 302+ * Upgrade a row if it needs it
 303+ */
 304+ function maybeUpgradeRow() {
 305+ if ( wfReadOnly() ) {
 306+ return;
 307+ }
 308+ if ( is_null($this->media_type) || $this->mime == 'image/svg' ) {
 309+ $this->upgradeRow();
 310+ $this->upgraded = true;
 311+ } else {
 312+ $handler = $this->getHandler();
 313+ if ( $handler && !$handler->isMetadataValid( $this, $this->metadata ) ) {
 314+ $this->upgradeRow();
 315+ $this->upgraded = true;
 316+ }
 317+ }
 318+ }
 319+
 320+ function getUpgraded() {
 321+ return $this->upgraded;
 322+ }
 323+
 324+ /**
 325+ * Fix assorted version-related problems with the image row by reloading it from the file
 326+ */
 327+ function upgradeRow() {
 328+ wfProfileIn( __METHOD__ );
 329+
 330+ $this->loadFromFile();
 331+
 332+ $dbw = $this->repo->getMasterDB();
 333+ list( $major, $minor ) = self::splitMime( $this->mime );
 334+
 335+ wfDebug(__METHOD__.': upgrading '.$this->getName()." to the current schema\n");
 336+
 337+ $dbw->update( 'image',
 338+ array(
 339+ 'img_width' => $this->width,
 340+ 'img_height' => $this->height,
 341+ 'img_bits' => $this->bits,
 342+ 'img_media_type' => $this->media_type,
 343+ 'img_major_mime' => $major,
 344+ 'img_minor_mime' => $minor,
 345+ 'img_metadata' => $this->metadata,
 346+ ), array( 'img_name' => $this->getName() ),
 347+ __METHOD__
 348+ );
 349+ $this->saveToCache();
 350+ wfProfileOut( __METHOD__ );
 351+ }
 352+
 353+ /** splitMime inherited */
 354+ /** getName inherited */
 355+ /** getTitle inherited */
 356+ /** getURL inherited */
 357+ /** getViewURL inherited */
 358+ /** getPath inherited */
 359+
 360+ /**
 361+ * Return the width of the image
 362+ *
 363+ * Returns false on error
 364+ * @public
 365+ */
 366+ function getWidth( $page = 1 ) {
 367+ $this->load();
 368+ if ( $this->isMultipage() ) {
 369+ $dim = $this->getHandler()->getPageDimensions( $this, $page );
 370+ if ( $dim ) {
 371+ return $dim['width'];
 372+ } else {
 373+ return false;
 374+ }
 375+ } else {
 376+ return $this->width;
 377+ }
 378+ }
 379+
 380+ /**
 381+ * Return the height of the image
 382+ *
 383+ * Returns false on error
 384+ * @public
 385+ */
 386+ function getHeight( $page = 1 ) {
 387+ $this->load();
 388+ if ( $this->isMultipage() ) {
 389+ $dim = $this->getHandler()->getPageDimensions( $this, $page );
 390+ if ( $dim ) {
 391+ return $dim['height'];
 392+ } else {
 393+ return false;
 394+ }
 395+ } else {
 396+ return $this->height;
 397+ }
 398+ }
 399+
 400+ /**
 401+ * Get handler-specific metadata
 402+ */
 403+ function getMetadata() {
 404+ $this->load();
 405+ return $this->metadata;
 406+ }
 407+
 408+ /**
 409+ * Return the size of the image file, in bytes
 410+ * @public
 411+ */
 412+ function getSize() {
 413+ $this->load();
 414+ return $this->size;
 415+ }
 416+
 417+ /**
 418+ * Returns the mime type of the file.
 419+ */
 420+ function getMimeType() {
 421+ $this->load();
 422+ return $this->mime;
 423+ }
 424+
 425+ /**
 426+ * Return the type of the media in the file.
 427+ * Use the value returned by this function with the MEDIATYPE_xxx constants.
 428+ */
 429+ function getMediaType() {
 430+ $this->load();
 431+ return $this->media_type;
 432+ }
 433+
 434+ /** canRender inherited */
 435+ /** mustRender inherited */
 436+ /** allowInlineDisplay inherited */
 437+ /** isSafeFile inherited */
 438+ /** isTrustedFile inherited */
 439+
 440+ /**
 441+ * Returns true if the file file exists on disk.
 442+ * @return boolean Whether file file exist on disk.
 443+ * @public
 444+ */
 445+ function exists() {
 446+ $this->load();
 447+ return $this->fileExists;
 448+ }
 449+
 450+ /** getTransformScript inherited */
 451+ /** getUnscaledThumb inherited */
 452+ /** thumbName inherited */
 453+ /** createThumb inherited */
 454+ /** getThumbnail inherited */
 455+ /** transform inherited */
 456+
 457+ /**
 458+ * Fix thumbnail files from 1.4 or before, with extreme prejudice
 459+ */
 460+ function migrateThumbFile( $thumbName ) {
 461+ $thumbDir = $this->getThumbPath();
 462+ $thumbPath = "$thumbDir/$thumbName";
 463+ if ( is_dir( $thumbPath ) ) {
 464+ // Directory where file should be
 465+ // This happened occasionally due to broken migration code in 1.5
 466+ // Rename to broken-*
 467+ for ( $i = 0; $i < 100 ; $i++ ) {
 468+ $broken = $this->repo->getZonePath('public') . "/broken-$i-$thumbName";
 469+ if ( !file_exists( $broken ) ) {
 470+ rename( $thumbPath, $broken );
 471+ break;
 472+ }
 473+ }
 474+ // Doesn't exist anymore
 475+ clearstatcache();
 476+ }
 477+ if ( is_file( $thumbDir ) ) {
 478+ // File where directory should be
 479+ unlink( $thumbDir );
 480+ // Doesn't exist anymore
 481+ clearstatcache();
 482+ }
 483+ }
 484+
 485+ /** getHandler inherited */
 486+ /** iconThumb inherited */
 487+ /** getLastError inherited */
 488+
 489+ /**
 490+ * Get all thumbnail names previously generated for this file
 491+ */
 492+ function getThumbnails() {
 493+ if ( $this->isHashed() ) {
 494+ $this->load();
 495+ $files = array();
 496+ $dir = $this->getThumbPath();
 497+
 498+ if ( is_dir( $dir ) ) {
 499+ $handle = opendir( $dir );
 500+
 501+ if ( $handle ) {
 502+ while ( false !== ( $file = readdir($handle) ) ) {
 503+ if ( $file{0} != '.' ) {
 504+ $files[] = $file;
 505+ }
 506+ }
 507+ closedir( $handle );
 508+ }
 509+ }
 510+ } else {
 511+ $files = array();
 512+ }
 513+
 514+ return $files;
 515+ }
 516+
 517+ /**
 518+ * Refresh metadata in memcached, but don't touch thumbnails or squid
 519+ */
 520+ function purgeMetadataCache() {
 521+ clearstatcache();
 522+ $this->loadFromFile();
 523+ $this->saveToCache();
 524+ }
 525+
 526+ /**
 527+ * Delete all previously generated thumbnails, refresh metadata in memcached and purge the squid
 528+ */
 529+ function purgeCache( $archiveFiles = array() ) {
 530+ global $wgUseSquid;
 531+
 532+ // Refresh metadata cache
 533+ $this->purgeMetadataCache();
 534+
 535+ // Delete thumbnails
 536+ $files = $this->getThumbnails();
 537+ $dir = $this->getThumbPath();
 538+ $urls = array();
 539+ foreach ( $files as $file ) {
 540+ $m = array();
 541+ # Check that the base file name is part of the thumb name
 542+ # This is a basic sanity check to avoid erasing unrelated directories
 543+ if ( strpos( $file, $this->getName() ) !== false ) {
 544+ $url = $this->getThumbUrl( $file );
 545+ $urls[] = $url;
 546+ @unlink( "$dir/$file" );
 547+ }
 548+ }
 549+
 550+ // Purge the squid
 551+ if ( $wgUseSquid ) {
 552+ $urls[] = $this->getURL();
 553+ foreach ( $archiveFiles as $file ) {
 554+ $urls[] = $this->getArchiveUrl( $file );
 555+ }
 556+ wfPurgeSquidServers( $urls );
 557+ }
 558+ }
 559+
 560+ /** purgeDescription inherited */
 561+ /** purgeEverything inherited */
 562+
 563+ /**
 564+ * Return the history of this file, line by line.
 565+ * starts with current version, then old versions.
 566+ * uses $this->historyLine to check which line to return:
 567+ * 0 return line for current version
 568+ * 1 query for old versions, return first one
 569+ * 2, ... return next old version from above query
 570+ *
 571+ * @public
 572+ */
 573+ function nextHistoryLine() {
 574+ $dbr = $this->repo->getSlaveDB();
 575+
 576+ if ( $this->historyLine == 0 ) {// called for the first time, return line from cur
 577+ $this->historyRes = $dbr->select( 'image',
 578+ array(
 579+ 'img_size',
 580+ 'img_description',
 581+ 'img_user','img_user_text',
 582+ 'img_timestamp',
 583+ 'img_width',
 584+ 'img_height',
 585+ "'' AS oi_archive_name"
 586+ ),
 587+ array( 'img_name' => $this->title->getDBkey() ),
 588+ __METHOD__
 589+ );
 590+ if ( 0 == $dbr->numRows( $this->historyRes ) ) {
 591+ return FALSE;
 592+ }
 593+ } else if ( $this->historyLine == 1 ) {
 594+ $this->historyRes = $dbr->select( 'oldimage',
 595+ array(
 596+ 'oi_size AS img_size',
 597+ 'oi_description AS img_description',
 598+ 'oi_user AS img_user',
 599+ 'oi_user_text AS img_user_text',
 600+ 'oi_timestamp AS img_timestamp',
 601+ 'oi_width as img_width',
 602+ 'oi_height as img_height',
 603+ 'oi_archive_name'
 604+ ),
 605+ array( 'oi_name' => $this->title->getDBkey() ),
 606+ __METHOD__,
 607+ array( 'ORDER BY' => 'oi_timestamp DESC' )
 608+ );
 609+ }
 610+ $this->historyLine ++;
 611+
 612+ return $dbr->fetchObject( $this->historyRes );
 613+ }
 614+
 615+ /**
 616+ * Reset the history pointer to the first element of the history
 617+ * @public
 618+ */
 619+ function resetHistory() {
 620+ $this->historyLine = 0;
 621+ }
 622+
 623+ /** getFullPath inherited */
 624+ /** getHashPath inherited */
 625+ /** getRel inherited */
 626+ /** getUrlRel inherited */
 627+ /** getArchivePath inherited */
 628+ /** getThumbPath inherited */
 629+ /** getArchiveUrl inherited */
 630+ /** getThumbUrl inherited */
 631+ /** getArchiveVirtualUrl inherited */
 632+ /** getThumbVirtualUrl inherited */
 633+ /** isHashed inherited */
 634+
 635+ /**
 636+ * Record a file upload in the upload log and the image table
 637+ */
 638+ function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '',
 639+ $watch = false, $timestamp = false )
 640+ {
 641+ global $wgUser, $wgUseCopyrightUpload;
 642+
 643+ $dbw = $this->repo->getMasterDB();
 644+
 645+ // Delete thumbnails and refresh the metadata cache
 646+ $this->purgeCache();
 647+
 648+ // Fail now if the file isn't there
 649+ if ( !$this->fileExists ) {
 650+ wfDebug( __METHOD__.": File ".$this->getPath()." went missing!\n" );
 651+ return false;
 652+ }
 653+
 654+ if ( $wgUseCopyrightUpload ) {
 655+ if ( $license != '' ) {
 656+ $licensetxt = '== ' . wfMsgForContent( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
 657+ }
 658+ $textdesc = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $desc . "\n" .
 659+ '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" .
 660+ "$licensetxt" .
 661+ '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ;
 662+ } else {
 663+ if ( $license != '' ) {
 664+ $filedesc = $desc == '' ? '' : '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $desc . "\n";
 665+ $textdesc = $filedesc .
 666+ '== ' . wfMsgForContent ( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
 667+ } else {
 668+ $textdesc = $desc;
 669+ }
 670+ }
 671+
 672+ if ( $timestamp === false ) {
 673+ $timestamp = $dbw->timestamp();
 674+ }
 675+
 676+ #split mime type
 677+ if (strpos($this->mime,'/')!==false) {
 678+ list($major,$minor)= explode('/',$this->mime,2);
 679+ }
 680+ else {
 681+ $major= $this->mime;
 682+ $minor= "unknown";
 683+ }
 684+
 685+ # Test to see if the row exists using INSERT IGNORE
 686+ # This avoids race conditions by locking the row until the commit, and also
 687+ # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
 688+ $dbw->insert( 'image',
 689+ array(
 690+ 'img_name' => $this->getName(),
 691+ 'img_size'=> $this->size,
 692+ 'img_width' => intval( $this->width ),
 693+ 'img_height' => intval( $this->height ),
 694+ 'img_bits' => $this->bits,
 695+ 'img_media_type' => $this->media_type,
 696+ 'img_major_mime' => $major,
 697+ 'img_minor_mime' => $minor,
 698+ 'img_timestamp' => $timestamp,
 699+ 'img_description' => $desc,
 700+ 'img_user' => $wgUser->getID(),
 701+ 'img_user_text' => $wgUser->getName(),
 702+ 'img_metadata' => $this->metadata,
 703+ ),
 704+ __METHOD__,
 705+ 'IGNORE'
 706+ );
 707+
 708+ if( $dbw->affectedRows() == 0 ) {
 709+ # Collision, this is an update of a file
 710+ # Insert previous contents into oldimage
 711+ $dbw->insertSelect( 'oldimage', 'image',
 712+ array(
 713+ 'oi_name' => 'img_name',
 714+ 'oi_archive_name' => $dbw->addQuotes( $oldver ),
 715+ 'oi_size' => 'img_size',
 716+ 'oi_width' => 'img_width',
 717+ 'oi_height' => 'img_height',
 718+ 'oi_bits' => 'img_bits',
 719+ 'oi_timestamp' => 'img_timestamp',
 720+ 'oi_description' => 'img_description',
 721+ 'oi_user' => 'img_user',
 722+ 'oi_user_text' => 'img_user_text',
 723+ ), array( 'img_name' => $this->getName() ), __METHOD__
 724+ );
 725+
 726+ # Update the current image row
 727+ $dbw->update( 'image',
 728+ array( /* SET */
 729+ 'img_size' => $this->size,
 730+ 'img_width' => intval( $this->width ),
 731+ 'img_height' => intval( $this->height ),
 732+ 'img_bits' => $this->bits,
 733+ 'img_media_type' => $this->media_type,
 734+ 'img_major_mime' => $major,
 735+ 'img_minor_mime' => $minor,
 736+ 'img_timestamp' => $timestamp,
 737+ 'img_description' => $desc,
 738+ 'img_user' => $wgUser->getID(),
 739+ 'img_user_text' => $wgUser->getName(),
 740+ 'img_metadata' => $this->metadata,
 741+ ), array( /* WHERE */
 742+ 'img_name' => $this->getName()
 743+ ), __METHOD__
 744+ );
 745+ } else {
 746+ # This is a new file
 747+ # Update the image count
 748+ $site_stats = $dbw->tableName( 'site_stats' );
 749+ $dbw->query( "UPDATE $site_stats SET ss_images=ss_images+1", __METHOD__ );
 750+ }
 751+
 752+ $descTitle = $this->getTitle();
 753+ $article = new Article( $descTitle );
 754+ $minor = false;
 755+ $watch = $watch || $wgUser->isWatched( $descTitle );
 756+ $suppressRC = true; // There's already a log entry, so don't double the RC load
 757+
 758+ if( $descTitle->exists() ) {
 759+ // TODO: insert a null revision into the page history for this update.
 760+ if( $watch ) {
 761+ $wgUser->addWatch( $descTitle );
 762+ }
 763+
 764+ # Invalidate the cache for the description page
 765+ $descTitle->invalidateCache();
 766+ $descTitle->purgeSquid();
 767+ } else {
 768+ // New file; create the description page.
 769+ $article->insertNewArticle( $textdesc, $desc, $minor, $watch, $suppressRC );
 770+ }
 771+
 772+ # Hooks, hooks, the magic of hooks...
 773+ wfRunHooks( 'FileUpload', array( $this ) );
 774+
 775+ # Add the log entry
 776+ $log = new LogPage( 'upload' );
 777+ $log->addEntry( 'upload', $descTitle, $desc );
 778+
 779+ # Commit the transaction now, in case something goes wrong later
 780+ # The most important thing is that files don't get lost, especially archives
 781+ $dbw->immediateCommit();
 782+
 783+ # Invalidate cache for all pages using this file
 784+ $update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' );
 785+ $update->doUpdate();
 786+
 787+ return true;
 788+ }
 789+
 790+ /**
 791+ * Move or copy a file to its public location. If a file exists at the
 792+ * destination, move it to an archive. Returns the archive name on success
 793+ * or an empty string if it was a new file, and a wikitext-formatted
 794+ * WikiError object on failure.
 795+ *
 796+ * The archive name should be passed through to recordUpload for database
 797+ * registration.
 798+ *
 799+ * @param string $sourcePath Local filesystem path to the source image
 800+ * @param integer $flags A bitwise combination of:
 801+ * File::DELETE_SOURCE Delete the source file, i.e. move
 802+ * rather than copy
 803+ * @return The archive name on success or an empty string if it was a new
 804+ * file, and a wikitext-formatted WikiError object on failure.
 805+ */
 806+ function publish( $srcPath, $flags = 0 ) {
 807+ $dstPath = $this->getFullPath();
 808+ $archiveName = gmdate( 'YmdHis' ) . '!'. $this->getName();
 809+ $archivePath = $this->getArchivePath( $archiveName );
 810+ $flags = $flags & File::DELETE_SOURCE ? LocalRepo::DELETE_SOURCE : 0;
 811+ $status = $this->repo->publish( $srcPath, $dstPath, $archivePath, $flags );
 812+ if ( WikiError::isError( $status ) ) {
 813+ return $status;
 814+ } elseif ( $status == 'new' ) {
 815+ return '';
 816+ } else {
 817+ return $archiveName;
 818+ }
 819+ }
 820+
 821+ /** getLinksTo inherited */
 822+ /** getExifData inherited */
 823+ /** isLocal inherited */
 824+ /** wasDeleted inherited */
 825+
 826+ /**
 827+ * Delete all versions of the file.
 828+ *
 829+ * Moves the files into an archive directory (or deletes them)
 830+ * and removes the database rows.
 831+ *
 832+ * Cache purging is done; logging is caller's responsibility.
 833+ *
 834+ * @param $reason
 835+ * @return true on success, false on some kind of failure
 836+ */
 837+ function delete( $reason, $suppress=false ) {
 838+ $transaction = new FSTransaction();
 839+ $urlArr = array( $this->getURL() );
 840+
 841+ if( !FileStore::lock() ) {
 842+ wfDebug( __METHOD__.": failed to acquire file store lock, aborting\n" );
 843+ return false;
 844+ }
 845+
 846+ try {
 847+ $dbw = $this->repo->getMasterDB();
 848+ $dbw->begin();
 849+
 850+ // Delete old versions
 851+ $result = $dbw->select( 'oldimage',
 852+ array( 'oi_archive_name' ),
 853+ array( 'oi_name' => $this->getName() ) );
 854+
 855+ while( $row = $dbw->fetchObject( $result ) ) {
 856+ $oldName = $row->oi_archive_name;
 857+
 858+ $transaction->add( $this->prepareDeleteOld( $oldName, $reason, $suppress ) );
 859+
 860+ // We'll need to purge this URL from caches...
 861+ $urlArr[] = $this->getArchiveUrl( $oldName );
 862+ }
 863+ $dbw->freeResult( $result );
 864+
 865+ // And the current version...
 866+ $transaction->add( $this->prepareDeleteCurrent( $reason, $suppress ) );
 867+
 868+ $dbw->immediateCommit();
 869+ } catch( MWException $e ) {
 870+ wfDebug( __METHOD__.": db error, rolling back file transactions\n" );
 871+ $transaction->rollback();
 872+ FileStore::unlock();
 873+ throw $e;
 874+ }
 875+
 876+ wfDebug( __METHOD__.": deleted db items, applying file transactions\n" );
 877+ $transaction->commit();
 878+ FileStore::unlock();
 879+
 880+
 881+ // Update site_stats
 882+ $site_stats = $dbw->tableName( 'site_stats' );
 883+ $dbw->query( "UPDATE $site_stats SET ss_images=ss_images-1", __METHOD__ );
 884+
 885+ $this->purgeEverything( $urlArr );
 886+
 887+ return true;
 888+ }
 889+
 890+
 891+ /**
 892+ * Delete an old version of the file.
 893+ *
 894+ * Moves the file into an archive directory (or deletes it)
 895+ * and removes the database row.
 896+ *
 897+ * Cache purging is done; logging is caller's responsibility.
 898+ *
 899+ * @param $reason
 900+ * @throws MWException or FSException on database or filestore failure
 901+ * @return true on success, false on some kind of failure
 902+ */
 903+ function deleteOld( $archiveName, $reason, $suppress=false ) {
 904+ $transaction = new FSTransaction();
 905+ $urlArr = array();
 906+
 907+ if( !FileStore::lock() ) {
 908+ wfDebug( __METHOD__.": failed to acquire file store lock, aborting\n" );
 909+ return false;
 910+ }
 911+
 912+ $transaction = new FSTransaction();
 913+ try {
 914+ $dbw = $this->repo->getMasterDB();
 915+ $dbw->begin();
 916+ $transaction->add( $this->prepareDeleteOld( $archiveName, $reason, $suppress ) );
 917+ $dbw->immediateCommit();
 918+ } catch( MWException $e ) {
 919+ wfDebug( __METHOD__.": db error, rolling back file transaction\n" );
 920+ $transaction->rollback();
 921+ FileStore::unlock();
 922+ throw $e;
 923+ }
 924+
 925+ wfDebug( __METHOD__.": deleted db items, applying file transaction\n" );
 926+ $transaction->commit();
 927+ FileStore::unlock();
 928+
 929+ $this->purgeDescription();
 930+
 931+ // Squid purging
 932+ global $wgUseSquid;
 933+ if ( $wgUseSquid ) {
 934+ $urlArr = array(
 935+ $this->getArchiveUrl( $archiveName ),
 936+ );
 937+ wfPurgeSquidServers( $urlArr );
 938+ }
 939+ return true;
 940+ }
 941+
 942+ /**
 943+ * Delete the current version of a file.
 944+ * May throw a database error.
 945+ * @return true on success, false on failure
 946+ */
 947+ private function prepareDeleteCurrent( $reason, $suppress=false ) {
 948+ return $this->prepareDeleteVersion(
 949+ $this->getFullPath(),
 950+ $reason,
 951+ 'image',
 952+ array(
 953+ 'fa_name' => 'img_name',
 954+ 'fa_archive_name' => 'NULL',
 955+ 'fa_size' => 'img_size',
 956+ 'fa_width' => 'img_width',
 957+ 'fa_height' => 'img_height',
 958+ 'fa_metadata' => 'img_metadata',
 959+ 'fa_bits' => 'img_bits',
 960+ 'fa_media_type' => 'img_media_type',
 961+ 'fa_major_mime' => 'img_major_mime',
 962+ 'fa_minor_mime' => 'img_minor_mime',
 963+ 'fa_description' => 'img_description',
 964+ 'fa_user' => 'img_user',
 965+ 'fa_user_text' => 'img_user_text',
 966+ 'fa_timestamp' => 'img_timestamp' ),
 967+ array( 'img_name' => $this->getName() ),
 968+ $suppress,
 969+ __METHOD__ );
 970+ }
 971+
 972+ /**
 973+ * Delete a given older version of a file.
 974+ * May throw a database error.
 975+ * @return true on success, false on failure
 976+ */
 977+ private function prepareDeleteOld( $archiveName, $reason, $suppress=false ) {
 978+ $oldpath = $this->getArchivePath() .
 979+ DIRECTORY_SEPARATOR . $archiveName;
 980+ return $this->prepareDeleteVersion(
 981+ $oldpath,
 982+ $reason,
 983+ 'oldimage',
 984+ array(
 985+ 'fa_name' => 'oi_name',
 986+ 'fa_archive_name' => 'oi_archive_name',
 987+ 'fa_size' => 'oi_size',
 988+ 'fa_width' => 'oi_width',
 989+ 'fa_height' => 'oi_height',
 990+ 'fa_metadata' => 'NULL',
 991+ 'fa_bits' => 'oi_bits',
 992+ 'fa_media_type' => 'NULL',
 993+ 'fa_major_mime' => 'NULL',
 994+ 'fa_minor_mime' => 'NULL',
 995+ 'fa_description' => 'oi_description',
 996+ 'fa_user' => 'oi_user',
 997+ 'fa_user_text' => 'oi_user_text',
 998+ 'fa_timestamp' => 'oi_timestamp' ),
 999+ array(
 1000+ 'oi_name' => $this->getName(),
 1001+ 'oi_archive_name' => $archiveName ),
 1002+ $suppress,
 1003+ __METHOD__ );
 1004+ }
 1005+
 1006+ /**
 1007+ * Do the dirty work of backing up an image row and its file
 1008+ * (if $wgSaveDeletedFiles is on) and removing the originals.
 1009+ *
 1010+ * Must be run while the file store is locked and a database
 1011+ * transaction is open to avoid race conditions.
 1012+ *
 1013+ * @return FSTransaction
 1014+ */
 1015+ private function prepareDeleteVersion( $path, $reason, $table, $fieldMap, $where, $suppress=false, $fname ) {
 1016+ global $wgUser, $wgSaveDeletedFiles;
 1017+
 1018+ // Dupe the file into the file store
 1019+ if( file_exists( $path ) ) {
 1020+ if( $wgSaveDeletedFiles ) {
 1021+ $group = 'deleted';
 1022+
 1023+ $store = FileStore::get( $group );
 1024+ $key = FileStore::calculateKey( $path, $this->getExtension() );
 1025+ $transaction = $store->insert( $key, $path,
 1026+ FileStore::DELETE_ORIGINAL );
 1027+ } else {
 1028+ $group = null;
 1029+ $key = null;
 1030+ $transaction = FileStore::deleteFile( $path );
 1031+ }
 1032+ } else {
 1033+ wfDebug( __METHOD__." deleting already-missing '$path'; moving on to database\n" );
 1034+ $group = null;
 1035+ $key = null;
 1036+ $transaction = new FSTransaction(); // empty
 1037+ }
 1038+
 1039+ if( $transaction === false ) {
 1040+ // Fail to restore?
 1041+ wfDebug( __METHOD__.": import to file store failed, aborting\n" );
 1042+ throw new MWException( "Could not archive and delete file $path" );
 1043+ return false;
 1044+ }
 1045+
 1046+ // Bitfields to further supress the file content
 1047+ // Note that currently, live files are stored elsewhere
 1048+ // and cannot be partially deleted
 1049+ $bitfield = 0;
 1050+ if ( $suppress ) {
 1051+ $bitfield |= self::DELETED_FILE;
 1052+ $bitfield |= self::DELETED_COMMENT;
 1053+ $bitfield |= self::DELETED_USER;
 1054+ $bitfield |= self::DELETED_RESTRICTED;
 1055+ }
 1056+
 1057+ $dbw = $this->repo->getMasterDB();
 1058+ $storageMap = array(
 1059+ 'fa_storage_group' => $dbw->addQuotes( $group ),
 1060+ 'fa_storage_key' => $dbw->addQuotes( $key ),
 1061+
 1062+ 'fa_deleted_user' => $dbw->addQuotes( $wgUser->getId() ),
 1063+ 'fa_deleted_timestamp' => $dbw->timestamp(),
 1064+ 'fa_deleted_reason' => $dbw->addQuotes( $reason ),
 1065+ 'fa_deleted' => $bitfield);
 1066+ $allFields = array_merge( $storageMap, $fieldMap );
 1067+
 1068+ try {
 1069+ if( $wgSaveDeletedFiles ) {
 1070+ $dbw->insertSelect( 'filearchive', $table, $allFields, $where, $fname );
 1071+ }
 1072+ $dbw->delete( $table, $where, $fname );
 1073+ } catch( DBQueryError $e ) {
 1074+ // Something went horribly wrong!
 1075+ // Leave the file as it was...
 1076+ wfDebug( __METHOD__.": database error, rolling back file transaction\n" );
 1077+ $transaction->rollback();
 1078+ throw $e;
 1079+ }
 1080+
 1081+ return $transaction;
 1082+ }
 1083+
 1084+ /**
 1085+ * Restore all or specified deleted revisions to the given file.
 1086+ * Permissions and logging are left to the caller.
 1087+ *
 1088+ * May throw database exceptions on error.
 1089+ *
 1090+ * @param $versions set of record ids of deleted items to restore,
 1091+ * or empty to restore all revisions.
 1092+ * @return the number of file revisions restored if successful,
 1093+ * or false on failure
 1094+ */
 1095+ function restore( $versions=array(), $Unsuppress=false ) {
 1096+ global $wgUser;
 1097+
 1098+ if( !FileStore::lock() ) {
 1099+ wfDebug( __METHOD__." could not acquire filestore lock\n" );
 1100+ return false;
 1101+ }
 1102+
 1103+ $transaction = new FSTransaction();
 1104+ try {
 1105+ $dbw = $this->repo->getMasterDB();
 1106+ $dbw->begin();
 1107+
 1108+ // Re-confirm whether this file presently exists;
 1109+ // if no we'll need to create an file record for the
 1110+ // first item we restore.
 1111+ $exists = $dbw->selectField( 'image', '1',
 1112+ array( 'img_name' => $this->getName() ),
 1113+ __METHOD__ );
 1114+
 1115+ // Fetch all or selected archived revisions for the file,
 1116+ // sorted from the most recent to the oldest.
 1117+ $conditions = array( 'fa_name' => $this->getName() );
 1118+ if( $versions ) {
 1119+ $conditions['fa_id'] = $versions;
 1120+ }
 1121+
 1122+ $result = $dbw->select( 'filearchive', '*',
 1123+ $conditions,
 1124+ __METHOD__,
 1125+ array( 'ORDER BY' => 'fa_timestamp DESC' ) );
 1126+
 1127+ if( $dbw->numRows( $result ) < count( $versions ) ) {
 1128+ // There's some kind of conflict or confusion;
 1129+ // we can't restore everything we were asked to.
 1130+ wfDebug( __METHOD__.": couldn't find requested items\n" );
 1131+ $dbw->rollback();
 1132+ FileStore::unlock();
 1133+ return false;
 1134+ }
 1135+
 1136+ if( $dbw->numRows( $result ) == 0 ) {
 1137+ // Nothing to do.
 1138+ wfDebug( __METHOD__.": nothing to do\n" );
 1139+ $dbw->rollback();
 1140+ FileStore::unlock();
 1141+ return true;
 1142+ }
 1143+
 1144+ $revisions = 0;
 1145+ while( $row = $dbw->fetchObject( $result ) ) {
 1146+ if ( $Unsuppress ) {
 1147+ // Currently, fa_deleted flags fall off upon restore, lets be careful about this
 1148+ } else if ( ($row->fa_deleted & Revision::DELETED_RESTRICTED) && !$wgUser->isAllowed('hiderevision') ) {
 1149+ // Skip restoring file revisions that the user cannot restore
 1150+ continue;
 1151+ }
 1152+ $revisions++;
 1153+ $store = FileStore::get( $row->fa_storage_group );
 1154+ if( !$store ) {
 1155+ wfDebug( __METHOD__.": skipping row with no file.\n" );
 1156+ continue;
 1157+ }
 1158+
 1159+ $restoredImage = new self( Title::makeTitle( NS_IMAGE, $row->fa_name ), $this->repo );
 1160+
 1161+ if( $revisions == 1 && !$exists ) {
 1162+ $destPath = $restoredImage->getFullPath();
 1163+ $destDir = dirname( $destPath );
 1164+ if ( !is_dir( $destDir ) ) {
 1165+ wfMkdirParents( $destDir );
 1166+ }
 1167+
 1168+ // We may have to fill in data if this was originally
 1169+ // an archived file revision.
 1170+ if( is_null( $row->fa_metadata ) ) {
 1171+ $tempFile = $store->filePath( $row->fa_storage_key );
 1172+
 1173+ $magic = MimeMagic::singleton();
 1174+ $mime = $magic->guessMimeType( $tempFile, true );
 1175+ $media_type = $magic->getMediaType( $tempFile, $mime );
 1176+ list( $major_mime, $minor_mime ) = self::splitMime( $mime );
 1177+ $handler = MediaHandler::getHandler( $mime );
 1178+ if ( $handler ) {
 1179+ $metadata = $handler->getMetadata( false, $tempFile );
 1180+ } else {
 1181+ $metadata = '';
 1182+ }
 1183+ } else {
 1184+ $metadata = $row->fa_metadata;
 1185+ $major_mime = $row->fa_major_mime;
 1186+ $minor_mime = $row->fa_minor_mime;
 1187+ $media_type = $row->fa_media_type;
 1188+ }
 1189+
 1190+ $table = 'image';
 1191+ $fields = array(
 1192+ 'img_name' => $row->fa_name,
 1193+ 'img_size' => $row->fa_size,
 1194+ 'img_width' => $row->fa_width,
 1195+ 'img_height' => $row->fa_height,
 1196+ 'img_metadata' => $metadata,
 1197+ 'img_bits' => $row->fa_bits,
 1198+ 'img_media_type' => $media_type,
 1199+ 'img_major_mime' => $major_mime,
 1200+ 'img_minor_mime' => $minor_mime,
 1201+ 'img_description' => $row->fa_description,
 1202+ 'img_user' => $row->fa_user,
 1203+ 'img_user_text' => $row->fa_user_text,
 1204+ 'img_timestamp' => $row->fa_timestamp );
 1205+ } else {
 1206+ $archiveName = $row->fa_archive_name;
 1207+ if( $archiveName == '' ) {
 1208+ // This was originally a current version; we
 1209+ // have to devise a new archive name for it.
 1210+ // Format is <timestamp of archiving>!<name>
 1211+ $archiveName =
 1212+ wfTimestamp( TS_MW, $row->fa_deleted_timestamp ) .
 1213+ '!' . $row->fa_name;
 1214+ }
 1215+ $restoredImage = new self( $row->fa_name, $this->repo );
 1216+ $destDir = $restoredImage->getArchivePath();
 1217+ if ( !is_dir( $destDir ) ) {
 1218+ wfMkdirParents( $destDir );
 1219+ }
 1220+ $destPath = $destDir . DIRECTORY_SEPARATOR . $archiveName;
 1221+
 1222+ $table = 'oldimage';
 1223+ $fields = array(
 1224+ 'oi_name' => $row->fa_name,
 1225+ 'oi_archive_name' => $archiveName,
 1226+ 'oi_size' => $row->fa_size,
 1227+ 'oi_width' => $row->fa_width,
 1228+ 'oi_height' => $row->fa_height,
 1229+ 'oi_bits' => $row->fa_bits,
 1230+ 'oi_description' => $row->fa_description,
 1231+ 'oi_user' => $row->fa_user,
 1232+ 'oi_user_text' => $row->fa_user_text,
 1233+ 'oi_timestamp' => $row->fa_timestamp );
 1234+ }
 1235+
 1236+ $dbw->insert( $table, $fields, __METHOD__ );
 1237+ // @todo this delete is not totally safe, potentially
 1238+ $dbw->delete( 'filearchive',
 1239+ array( 'fa_id' => $row->fa_id ),
 1240+ __METHOD__ );
 1241+
 1242+ // Check if any other stored revisions use this file;
 1243+ // if so, we shouldn't remove the file from the deletion
 1244+ // archives so they will still work.
 1245+ $useCount = $dbw->selectField( 'filearchive',
 1246+ 'COUNT(*)',
 1247+ array(
 1248+ 'fa_storage_group' => $row->fa_storage_group,
 1249+ 'fa_storage_key' => $row->fa_storage_key ),
 1250+ __METHOD__ );
 1251+ if( $useCount == 0 ) {
 1252+ wfDebug( __METHOD__.": nothing else using {$row->fa_storage_key}, will deleting after\n" );
 1253+ $flags = FileStore::DELETE_ORIGINAL;
 1254+ } else {
 1255+ $flags = 0;
 1256+ }
 1257+
 1258+ $transaction->add( $store->export( $row->fa_storage_key,
 1259+ $destPath, $flags ) );
 1260+ }
 1261+
 1262+ $dbw->immediateCommit();
 1263+ } catch( MWException $e ) {
 1264+ wfDebug( __METHOD__." caught error, aborting\n" );
 1265+ $transaction->rollback();
 1266+ throw $e;
 1267+ }
 1268+
 1269+ $transaction->commit();
 1270+ FileStore::unlock();
 1271+
 1272+ if( $revisions > 0 ) {
 1273+ if( !$exists ) {
 1274+ wfDebug( __METHOD__." restored $revisions items, creating a new current\n" );
 1275+
 1276+ // Update site_stats
 1277+ $site_stats = $dbw->tableName( 'site_stats' );
 1278+ $dbw->query( "UPDATE $site_stats SET ss_images=ss_images+1", __METHOD__ );
 1279+
 1280+ $this->purgeEverything();
 1281+ } else {
 1282+ wfDebug( __METHOD__." restored $revisions as archived versions\n" );
 1283+ $this->purgeDescription();
 1284+ }
 1285+ }
 1286+
 1287+ return $revisions;
 1288+ }
 1289+
 1290+ /** isMultipage inherited */
 1291+ /** pageCount inherited */
 1292+ /** scaleHeight inherited */
 1293+ /** getImageSize inherited */
 1294+
 1295+ /**
 1296+ * Get the URL of the file description page.
 1297+ */
 1298+ function getDescriptionUrl() {
 1299+ return $this->title->getLocalUrl();
 1300+ }
 1301+
 1302+ /**
 1303+ * Get the HTML text of the description page
 1304+ * This is not used by ImagePage for local files, since (among other things)
 1305+ * it skips the parser cache.
 1306+ */
 1307+ function getDescriptionText() {
 1308+ global $wgParser;
 1309+ $revision = Revision::newFromTitle( $this->title );
 1310+ if ( !$revision ) return false;
 1311+ $text = $revision->getText();
 1312+ if ( !$text ) return false;
 1313+ $html = $wgParser->parse( $text, new ParserOptions );
 1314+ return $html;
 1315+ }
 1316+
 1317+ function getTimestamp() {
 1318+ $this->load();
 1319+ return $this->timestamp;
 1320+ }
 1321+} // LocalFile class
 1322+
 1323+/**
 1324+ * Backwards compatibility class
 1325+ */
 1326+class Image extends LocalFile {
 1327+ function __construct( $title ) {
 1328+ $repo = FileRepoGroup::singleton()->getLocalRepo();
 1329+ parent::__construct( $title, $repo );
 1330+ }
 1331+
 1332+ /**
 1333+ * Wrapper for wfFindFile(), for backwards-compatibility only
 1334+ * Do not use in core code.
 1335+ */
 1336+ function newFromTitle( $title, $time = false ) {
 1337+ $img = wfFindFile( $title, $time );
 1338+ if ( !$img ) {
 1339+ $img = wfLocalFile( $title );
 1340+ }
 1341+ return $img;
 1342+ }
 1343+}
 1344+
 1345+/**
 1346+ * Aliases for backwards compatibility with 1.6
 1347+ */
 1348+define( 'MW_IMG_DELETED_FILE', File::DELETED_FILE );
 1349+define( 'MW_IMG_DELETED_COMMENT', File::DELETED_COMMENT );
 1350+define( 'MW_IMG_DELETED_USER', File::DELETED_USER );
 1351+define( 'MW_IMG_DELETED_RESTRICTED', File::DELETED_RESTRICTED );
 1352+
 1353+?>
Property changes on: branches/liquidthreads/includes/filerepo/LocalFile.php
___________________________________________________________________
Added: svn:eol-style
11354 + native
Index: branches/liquidthreads/includes/filerepo/UnregisteredLocalFile.php
@@ -0,0 +1,109 @@
 2+<?php
 3+
 4+/**
 5+ * A file object referring to either a standalone local file, or a file in a
 6+ * local repository with no database, for example an FSRepo repository.
 7+ *
 8+ * Read-only.
 9+ *
 10+ * TODO: Currently it doesn't really work in the repository role, there are
 11+ * lots of functions missing. It is used by the WebStore extension in the
 12+ * standalone role.
 13+ */
 14+class UnregisteredLocalFile extends File {
 15+ var $title, $path, $mime, $handler, $dims;
 16+
 17+ function newFromPath( $path, $mime ) {
 18+ return new UnregisteredLocalFile( false, false, $path, $mime );
 19+ }
 20+
 21+ function newFromTitle( $title, $repo ) {
 22+ return new UnregisteredLocalFile( $title, $repo, false, false );
 23+ }
 24+
 25+ function __construct( $title = false, $repo = false, $path = false, $mime = false ) {
 26+ if ( !( $title && $repo ) && !$path ) {
 27+ throw new MWException( __METHOD__.': not enough parameters, must specify title and repo, or a full path' );
 28+ }
 29+ if ( $title ) {
 30+ $this->title = $title;
 31+ $this->name = $title->getDBkey();
 32+ } else {
 33+ $this->name = basename( $path );
 34+ $this->title = Title::makeTitleSafe( NS_IMAGE, $this->name );
 35+ }
 36+ $this->repo = $repo;
 37+ if ( $path ) {
 38+ $this->path = $path;
 39+ } else {
 40+ $this->path = $repo->getRootDirectory() . '/' . $repo->getHashPath( $this->name ) . $this->name;
 41+ }
 42+ if ( $mime ) {
 43+ $this->mime = $mime;
 44+ }
 45+ $this->dims = array();
 46+ }
 47+
 48+ function getPageDimensions( $page = 1 ) {
 49+ if ( !isset( $this->dims[$page] ) ) {
 50+ if ( !$this->getHandler() ) {
 51+ return false;
 52+ }
 53+ $this->dims[$page] = $this->handler->getPageDimensions( $this, $page );
 54+ }
 55+ return $this->dims[$page];
 56+ }
 57+
 58+ function getWidth( $page = 1 ) {
 59+ $dim = $this->getPageDimensions( $page );
 60+ return $dim['width'];
 61+ }
 62+
 63+ function getHeight( $page = 1 ) {
 64+ $dim = $this->getPageDimensions( $page );
 65+ return $dim['height'];
 66+ }
 67+
 68+ function getMimeType() {
 69+ if ( !isset( $this->mime ) ) {
 70+ $magic = MimeMagic::singleton();
 71+ $this->mime = $magic->guessMimeType( $this->path );
 72+ }
 73+ return $this->mime;
 74+ }
 75+
 76+ function getImageSize() {
 77+ if ( !$this->getHandler() ) {
 78+ return false;
 79+ }
 80+ return $this->handler->getImageSize( $this, $this->getPath() );
 81+ }
 82+
 83+ function getMetadata() {
 84+ if ( !isset( $this->metadata ) ) {
 85+ if ( !$this->getHandler() ) {
 86+ $this->metadata = false;
 87+ } else {
 88+ $this->metadata = $this->handler->getMetadata( $this, $this->getPath() );
 89+ }
 90+ }
 91+ return $this->metadata;
 92+ }
 93+
 94+ function getURL() {
 95+ if ( $this->repo ) {
 96+ return $this->repo->getZoneUrl( 'public' ) . $this->repo->getHashPath( $this->name ) . urlencode( $this->name );
 97+ } else {
 98+ return false;
 99+ }
 100+ }
 101+
 102+ function getSize() {
 103+ if ( file_exists( $this->path ) ) {
 104+ return filesize( $this->path );
 105+ } else {
 106+ return false;
 107+ }
 108+ }
 109+}
 110+?>
Property changes on: branches/liquidthreads/includes/filerepo/UnregisteredLocalFile.php
___________________________________________________________________
Added: svn:eol-style
1111 + native
Index: branches/liquidthreads/includes/ImagePage.php
@@ -18,6 +18,14 @@
1919 /* private */ var $img; // Image object this page is shown for
2020 var $mExtraDescription = false;
2121
 22+ function __construct( $title ) {
 23+ parent::__construct( $title );
 24+ $this->img = wfFindFile( $this->mTitle );
 25+ if ( !$this->img ) {
 26+ $this->img = wfLocalFile( $this->mTitle );
 27+ }
 28+ }
 29+
2230 /**
2331 * Handler for action=render
2432 * Include body text only; none of the image extras
@@ -31,8 +39,6 @@
3240 function view() {
3341 global $wgOut, $wgShowEXIF, $wgRequest, $wgUser;
3442
35 - $this->img = new Image( $this->mTitle );
36 -
3743 $diff = $wgRequest->getVal( 'diff' );
3844 $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
3945
@@ -160,7 +166,7 @@
161167 * shared upload server if possible.
162168 */
163169 function getContent() {
164 - if( $this->img && $this->img->fromSharedDirectory && 0 == $this->getID() ) {
 170+ if( $this->img && !$this->img->isLocal() && 0 == $this->getID() ) {
165171 return '';
166172 }
167173 return Article::getContent();
@@ -332,26 +338,26 @@
333339 $dirmark = $wgContLang->getDirMark();
334340 if (!$this->img->isSafeFile()) {
335341 $warning = wfMsg( 'mediawarning' );
336 - $wgOut->addWikiText( <<<END
 342+ $wgOut->addWikiText( <<<EOT
337343 <div class="fullMedia">$infores
338344 <span class="dangerousLink">[[Media:$filename|$filename]]</span>$dirmark
339345 <span class="fileInfo"> $info</span>
340346 </div>
341347
342348 <div class="mediaWarning">$warning</div>
343 -END
 349+EOT
344350 );
345351 } else {
346 - $wgOut->addWikiText( <<<END
 352+ $wgOut->addWikiText( <<<EOT
347353 <div class="fullMedia">$infores
348354 [[Media:$filename|$filename]]$dirmark <span class="fileInfo"> $info</span>
349355 </div>
350 -END
 356+EOT
351357 );
352358 }
353359 }
354360
355 - if($this->img->fromSharedDirectory) {
 361+ if(!$this->img->isLocal()) {
356362 $this->printSharedImageText();
357363 }
358364 } else {
@@ -365,27 +371,21 @@
366372 }
367373
368374 function printSharedImageText() {
369 - global $wgRepositoryBaseUrl, $wgFetchCommonsDescriptions, $wgOut, $wgUser;
 375+ global $wgOut, $wgUser;
370376
371 - $url = $wgRepositoryBaseUrl . urlencode($this->mTitle->getDBkey());
372 - $sharedtext = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml("sharedupload");
373 - if ($wgRepositoryBaseUrl && !$wgFetchCommonsDescriptions) {
374 -
 377+ $descUrl = $this->img->getDescriptionUrl();
 378+ $descText = $this->img->getDescriptionText();
 379+ $s = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml("sharedupload");
 380+ if ( $descUrl && !$descText) {
375381 $sk = $wgUser->getSkin();
376 - $title = SpecialPage::getTitleFor( 'Upload' );
377 - $link = $sk->makeKnownLinkObj($title, wfMsgHtml('shareduploadwiki-linktext'),
378 - array( 'wpDestFile' => urlencode( $this->img->getName() )));
379 - $sharedtext .= " " . wfMsgWikiHtml('shareduploadwiki', $link);
 382+ $link = $sk->makeExternalLink( $descUrl, wfMsg('shareduploadwiki-linktext') );
 383+ $s .= " " . wfMsgWikiHtml('shareduploadwiki', $link);
380384 }
381 - $sharedtext .= "</div>";
382 - $wgOut->addHTML($sharedtext);
 385+ $s .= "</div>";
 386+ $wgOut->addHTML($s);
383387
384 - if ($wgRepositoryBaseUrl && $wgFetchCommonsDescriptions) {
385 - $renderUrl = wfAppendQuery( $url, 'action=render' );
386 - wfDebug( "Fetching shared description from $renderUrl\n" );
387 - $text = Http::get( $renderUrl );
388 - if ($text)
389 - $this->mExtraDescription = $text;
 388+ if ( $descText ) {
 389+ $this->mExtraDescription = $descText;
390390 }
391391 }
392392
@@ -402,7 +402,7 @@
403403 function uploadLinksBox() {
404404 global $wgUser, $wgOut;
405405
406 - if( $this->img->fromSharedDirectory )
 406+ if( !$this->img->isLocal() )
407407 return;
408408
409409 $sk = $wgUser->getSkin();
@@ -441,7 +441,7 @@
442442 $line = $this->img->nextHistoryLine();
443443
444444 if ( $line ) {
445 - $list = new ImageHistoryList( $sk );
 445+ $list = new ImageHistoryList( $sk, $this->img );
446446 $s = $list->beginImageHistoryList() .
447447 $list->imageHistoryLine( true, wfTimestamp(TS_MW, $line->img_timestamp),
448448 $this->mTitle->getDBkey(), $line->img_user,
@@ -530,8 +530,6 @@
531531 return;
532532 }
533533
534 - $this->img = new Image( $this->mTitle );
535 -
536534 # Deleting old images doesn't require confirmation
537535 if ( !is_null( $oldimage ) || $confirm ) {
538536 if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
@@ -655,39 +653,23 @@
656654 $wgOut->showErrorPage( 'internalerror', 'sessionfailure' );
657655 return;
658656 }
659 - $name = substr( $oldimage, 15 );
660657
661 - $dest = wfImageDir( $name );
662 - $archive = wfImageArchiveDir( $name );
663 - $curfile = "{$dest}/{$name}";
 658+ $sourcePath = $this->img->getArchiveVirtualUrl( $oldimage );
 659+ $result = $this->img->publish( $sourcePath );
664660
665 - if ( !is_dir( $dest ) ) wfMkdirParents( $dest );
666 - if ( !is_dir( $archive ) ) wfMkdirParents( $archive );
667 -
668 - if ( ! is_file( $curfile ) ) {
669 - $wgOut->showFileNotFoundError( htmlspecialchars( $curfile ) );
 661+ if ( WikiError::isError( $result ) ) {
 662+ $this->showError( $result );
670663 return;
671664 }
672 - $oldver = wfTimestampNow() . "!{$name}";
673665
674 - if ( ! rename( $curfile, "${archive}/{$oldver}" ) ) {
675 - $wgOut->showFileRenameError( $curfile, "${archive}/{$oldver}" );
676 - return;
677 - }
678 - if ( ! copy( "{$archive}/{$oldimage}", $curfile ) ) {
679 - $wgOut->showFileCopyError( "${archive}/{$oldimage}", $curfile );
680 - return;
681 - }
682 -
683666 # Record upload and update metadata cache
684 - $img = Image::newFromName( $name );
685 - $img->recordUpload( $oldver, wfMsg( "reverted" ) );
 667+ $this->img->recordUpload( $result, wfMsg( "reverted" ) );
686668
687669 $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
688670 $wgOut->setRobotpolicy( 'noindex,nofollow' );
689671 $wgOut->addHTML( wfMsg( 'imagereverted' ) );
690672
691 - $descTitle = $img->getTitle();
 673+ $descTitle = $this->img->getTitle();
692674 $wgOut->returnToMain( false, $descTitle->getPrefixedText() );
693675 }
694676
@@ -695,7 +677,6 @@
696678 * Override handling of action=purge
697679 */
698680 function doPurge() {
699 - $this->img = new Image( $this->mTitle );
700681 if( $this->img->exists() ) {
701682 wfDebug( "ImagePage::doPurge purging " . $this->img->getName() . "\n" );
702683 $update = new HTMLCacheUpdate( $this->mTitle, 'imagelinks' );
@@ -708,6 +689,18 @@
709690 parent::doPurge();
710691 }
711692
 693+ /**
 694+ * Display an error from a wikitext-formatted WikiError object
 695+ */
 696+ function showError( WikiError $error ) {
 697+ global $wgOut;
 698+ $wgOut->setPageTitle( wfMsg( "internalerror" ) );
 699+ $wgOut->setRobotpolicy( "noindex,nofollow" );
 700+ $wgOut->setArticleRelated( false );
 701+ $wgOut->enableClientCache( false );
 702+ $wgOut->addWikiText( $error->getMessage() );
 703+ }
 704+
712705 }
713706
714707 /**
@@ -715,8 +708,10 @@
716709 * @addtogroup Media
717710 */
718711 class ImageHistoryList {
719 - function ImageHistoryList( &$skin ) {
720 - $this->skin =& $skin;
 712+ var $img, $skin;
 713+ function ImageHistoryList( $skin, $img ) {
 714+ $this->skin = $skin;
 715+ $this->img = $img;
721716 }
722717
723718 function beginImageHistoryList() {
@@ -738,11 +733,12 @@
739734 $del = wfMsgHtml( 'deleteimg' );
740735 $delall = wfMsgHtml( 'deleteimgcompletely' );
741736 $cur = wfMsgHtml( 'cur' );
 737+ $local = $this->img->isLocal();
742738
743739 if ( $iscur ) {
744 - $url = Image::imageUrl( $img );
 740+ $url = htmlspecialchars( $this->img->getURL() );
745741 $rlink = $cur;
746 - if ( $wgUser->isAllowed('delete') ) {
 742+ if ( $local && $wgUser->isAllowed('delete') ) {
747743 $link = $wgTitle->escapeLocalURL( 'image=' . $wgTitle->getPartialURL() .
748744 '&action=delete' );
749745 $style = $this->skin->getInternalLinkAttributes( $link, $delall );
@@ -752,8 +748,8 @@
753749 $dlink = $del;
754750 }
755751 } else {
756 - $url = htmlspecialchars( wfImageArchiveUrl( $img ) );
757 - if( $wgUser->getID() != 0 && $wgTitle->userCan( 'edit' ) ) {
 752+ $url = htmlspecialchars( $this->img->getArchiveUrl( $img ) );
 753+ if( $local && $wgUser->getID() != 0 && $wgTitle->userCan( 'edit' ) ) {
758754 $token = urlencode( $wgUser->editToken( $img ) );
759755 $rlink = $this->skin->makeKnownLinkObj( $wgTitle,
760756 wfMsgHtml( 'revertimg' ), 'action=revert&oldimage=' .
@@ -769,8 +765,12 @@
770766 $dlink = $del;
771767 }
772768 }
773 -
774 - $userlink = $this->skin->userLink( $user, $usertext ) . $this->skin->userToolLinks( $user, $usertext );
 769+
 770+ if ( $local ) {
 771+ $userlink = $this->skin->userLink( $user, $usertext ) . $this->skin->userToolLinks( $user, $usertext );
 772+ } else {
 773+ $userlink = htmlspecialchars( $usertext );
 774+ }
775775 $nbytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
776776 $wgLang->formatNum( $size ) );
777777 $widthheight = wfMsgHtml( 'widthheight', $width, $height );
@@ -782,7 +782,6 @@
783783 $s .= "</li>\n";
784784 return $s;
785785 }
786 -
787786 }
788787
789788
Index: branches/liquidthreads/includes/StreamFile.php
@@ -31,6 +31,9 @@
3232 header('Content-type: application/x-wiki');
3333 }
3434
 35+ global $wgContLanguageCode;
 36+ header( "Content-Disposition: inline;filename*=utf-8'$wgContLanguageCode'" . urlencode( basename( $fname ) ) );
 37+
3538 if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
3639 $modsince = preg_replace( '/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
3740 $sinceTime = strtotime( $modsince );
Index: branches/liquidthreads/includes/SpecialUndelete.php
@@ -269,7 +269,7 @@
270270 $restoreFiles = $restoreAll || !empty( $fileVersions );
271271
272272 if( $restoreFiles && $this->title->getNamespace() == NS_IMAGE ) {
273 - $img = new Image( $this->title );
 273+ $img = wfLocalFile( $this->title );
274274 $filesRestored = $img->restore( $fileVersions );
275275 } else {
276276 $filesRestored = 0;
Index: branches/liquidthreads/includes/Linker.php
@@ -440,13 +440,14 @@
441441 * @return string
442442 */
443443 function makeImageLinkObj( $nt, $label, $alt, $align = '', $params = array(), $framed = false,
444 - $thumb = false, $manual_thumb = '', $valign = '' )
 444+ $thumb = false, $manual_thumb = '', $valign = '', $time = false )
445445 {
446446 global $wgContLang, $wgUser, $wgThumbLimits, $wgThumbUpright;
447447
448 - $img = new Image( $nt );
 448+ $img = wfFindFile( $nt, $time );
449449
450 - if ( !$img->allowInlineDisplay() && $img->exists() ) {
 450+ if ( $img && !$img->allowInlineDisplay() ) {
 451+ wfDebug( __METHOD__.': '.$nt->getPrefixedDBkey()." does not allow inline display\n" );
451452 return $this->makeKnownLinkObj( $nt );
452453 }
453454
@@ -459,7 +460,7 @@
460461 $postfix = '</div>';
461462 $align = 'none';
462463 }
463 - if ( !isset( $params['width'] ) ) {
 464+ if ( $img && !isset( $params['width'] ) ) {
464465 $params['width'] = $img->getWidth( $page );
465466 if( $thumb || $framed || isset( $params['frameless'] ) ) {
466467 $wopt = $wgUser->getOption( 'thumbsize' );
@@ -490,10 +491,10 @@
491492 if ( $align == '' ) {
492493 $align = $wgContLang->isRTL() ? 'left' : 'right';
493494 }
494 - return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $params, $framed, $manual_thumb ).$postfix;
 495+ return $prefix.$this->makeThumbLinkObj( $nt, $img, $label, $alt, $align, $params, $framed, $manual_thumb ).$postfix;
495496 }
496497
497 - if ( $params['width'] && $img->exists() ) {
 498+ if ( $img && $params['width'] ) {
498499 # Create a resized image, without the additional thumbnail features
499500 $thumb = $img->transform( $params );
500501 } else {
@@ -524,7 +525,7 @@
525526 );
526527
527528 if ( !$thumb ) {
528 - $s = $this->makeBrokenImageLinkObj( $img->getTitle() );
 529+ $s = $this->makeBrokenImageLinkObj( $nt );
529530 } else {
530531 $s = $thumb->toHtml( $imgAttribs, $linkAttribs );
531532 }
@@ -536,10 +537,12 @@
537538
538539 /**
539540 * Make HTML for a thumbnail including image, border and caption
540 - * $img is an Image object
 541+ * @param Title $nt
 542+ * @param Image $img Image object or false if it doesn't exist
541543 */
542 - function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manual_thumb = "" ) {
 544+ function makeThumbLinkObj( Title $nt, $img, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manual_thumb = "" ) {
543545 global $wgStylePath, $wgContLang;
 546+ $exists = $img && $img->exists();
544547
545548 $page = isset( $params['page'] ) ? $params['page'] : false;
546549
@@ -548,46 +551,55 @@
549552 $params['width'] = isset( $params['upright'] ) ? 130 : 180;
550553 }
551554 $thumb = false;
552 - if ( $manual_thumb != '' ) {
553 - # Use manually specified thumbnail
554 - $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb );
555 - if( $manual_title ) {
556 - $manual_img = new Image( $manual_title );
557 - $thumb = $manual_img->getUnscaledThumb();
558 - }
559 - } elseif ( $framed ) {
560 - // Use image dimensions, don't scale
561 - $thumb = $img->getUnscaledThumb( $page );
 555+
 556+ if ( !$exists ) {
 557+ $outerWidth = $params['width'] + 2;
562558 } else {
563 - # Do not present an image bigger than the source, for bitmap-style images
564 - # This is a hack to maintain compatibility with arbitrary pre-1.10 behaviour
565 - $srcWidth = $img->getWidth( $page );
566 - if ( $srcWidth && !$img->mustRender() && $params['width'] > $srcWidth ) {
567 - $params['width'] = $srcWidth;
 559+ if ( $manual_thumb != '' ) {
 560+ # Use manually specified thumbnail
 561+ $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb );
 562+ if( $manual_title ) {
 563+ $manual_img = wfFindFile( $manual_title );
 564+ if ( $manual_img ) {
 565+ $thumb = $manual_img->getUnscaledThumb();
 566+ } else {
 567+ $exists = false;
 568+ }
 569+ }
 570+ } elseif ( $framed ) {
 571+ // Use image dimensions, don't scale
 572+ $thumb = $img->getUnscaledThumb( $page );
 573+ } else {
 574+ # Do not present an image bigger than the source, for bitmap-style images
 575+ # This is a hack to maintain compatibility with arbitrary pre-1.10 behaviour
 576+ $srcWidth = $img->getWidth( $page );
 577+ if ( $srcWidth && !$img->mustRender() && $params['width'] > $srcWidth ) {
 578+ $params['width'] = $srcWidth;
 579+ }
 580+ $thumb = $img->transform( $params );
568581 }
569 - $thumb = $img->transform( $params );
570 - }
571582
572 - if ( $thumb ) {
573 - $outerWidth = $thumb->getWidth() + 2;
574 - } else {
575 - $outerWidth = $params['width'] + 2;
 583+ if ( $thumb ) {
 584+ $outerWidth = $thumb->getWidth() + 2;
 585+ } else {
 586+ $outerWidth = $params['width'] + 2;
 587+ }
576588 }
577589
578590 $query = $page ? 'page=' . urlencode( $page ) : '';
579 - $u = $img->getTitle()->getLocalURL( $query );
 591+ $u = $nt->getLocalURL( $query );
580592
581593 $more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
582594 $magnifyalign = $wgContLang->isRTL() ? 'left' : 'right';
583595 $textalign = $wgContLang->isRTL() ? ' style="text-align:right"' : '';
584596
585597 $s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
586 - if ( !$thumb ) {
 598+ if( !$exists ) {
 599+ $s .= $this->makeBrokenImageLinkObj( $nt );
 600+ $zoomicon = '';
 601+ } elseif ( !$thumb ) {
587602 $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
588603 $zoomicon = '';
589 - } elseif( !$img->exists() ) {
590 - $s .= $this->makeBrokenImageLinkObj( $img->getTitle() );
591 - $zoomicon = '';
592604 } else {
593605 $imgAttribs = array(
594606 'alt' => $alt,
@@ -645,10 +657,10 @@
646658 return $s;
647659 }
648660
649 - /** @todo document */
650 - function makeMediaLink( $name, /* wtf?! */ $url, $alt = '' ) {
 661+ /** @deprecated use Linker::makeMediaLinkObj() */
 662+ function makeMediaLink( $name, $unused = '', $text = '' ) {
651663 $nt = Title::makeTitleSafe( NS_IMAGE, $name );
652 - return $this->makeMediaLinkObj( $nt, $alt );
 664+ return $this->makeMediaLinkObj( $nt, $text );
653665 }
654666
655667 /**
@@ -666,13 +678,13 @@
667679 ### HOTFIX. Instead of breaking, return empty string.
668680 return $text;
669681 } else {
670 - $img = new Image( $title );
671 - if( $img->exists() ) {
 682+ $img = wfFindFile( $title );
 683+ if( $img ) {
672684 $url = $img->getURL();
673685 $class = 'internal';
674686 } else {
675687 $upload = SpecialPage::getTitleFor( 'Upload' );
676 - $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $img->getName() ) );
 688+ $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $title->getText() ) );
677689 $class = 'new';
678690 }
679691 $alt = htmlspecialchars( $title->getText() );
Index: branches/liquidthreads/includes/Parser.php
@@ -1806,8 +1806,8 @@
18071807 }
18081808 continue;
18091809 } elseif( $ns == NS_IMAGE ) {
1810 - $img = new Image( $nt );
1811 - if( $img->exists() ) {
 1810+ $img = wfFindFile( $nt );
 1811+ if( $img ) {
18121812 // Force a blue link if the file exists; may be a remote
18131813 // upload on the shared repository, and we want to see its
18141814 // auto-generated page.
@@ -4393,7 +4393,7 @@
43944394 );
43954395 $html = $pout->getText();
43964396
4397 - $ig->add( new Image( $nt ), $html );
 4397+ $ig->add( $nt, $html );
43984398
43994399 # Only add real images (bug #5586)
44004400 if ( $nt->getNamespace() == NS_IMAGE ) {
Index: branches/liquidthreads/includes/DefaultSettings.php
@@ -184,6 +184,49 @@
185185 $wgFileStore['deleted']['url'] = null; // Private
186186 $wgFileStore['deleted']['hash'] = 3; // 3-level subdirectory split
187187
 188+/**#@+
 189+ * File repository structures
 190+ *
 191+ * $wgLocalFileRepo is a single repository structure, and $wgForeignFileRepo is
 192+ * a an array of such structures. Each repository structure is an associative
 193+ * array of properties configuring the repository.
 194+ *
 195+ * Properties required for all repos:
 196+ * class The class name for the repository. May come from the core or an extension.
 197+ * The core repository classes are LocalRepo, ForeignDBRepo, FSRepo.
 198+ *
 199+ * name A unique name for the repository.
 200+ *
 201+ * For all core repos:
 202+ * url Base public URL
 203+ * hashLevels The number of directory levels for hash-based division of files
 204+ * thumbScriptUrl The URL for thumb.php (optional, not recommended)
 205+ * transformVia404 Whether to skip media file transformation on parse and rely on a 404
 206+ * handler instead.
 207+ *
 208+ * These settings describe a foreign MediaWiki installation. They are optional, and will be ignored
 209+ * for local repositories:
 210+ * descBaseUrl URL of image description pages, e.g. http://en.wikipedia.org/wiki/Image:
 211+ * scriptDirUrl URL of the MediaWiki installation, equivalent to $wgScriptPath, e.g.
 212+ * http://en.wikipedia.org/w
 213+ *
 214+ * articleUrl Equivalent to $wgArticlePath, e.g. http://en.wikipedia.org/wiki/$1
 215+ * fetchDescription Fetch the text of the remote file description page. Equivalent to
 216+ * $wgFetchCommonsDescriptions.
 217+ *
 218+ * ForeignDBRepo:
 219+ * dbType, dbServer, dbUser, dbPassword, dbName, dbFlags
 220+ * equivalent to the corresponding member of $wgDBservers
 221+ * tablePrefix Table prefix, the foreign wiki's $wgDBprefix
 222+ * hasSharedCache True if the wiki's shared cache is accessible via the local $wgMemc
 223+ *
 224+ * The default is to initialise these arrays from the MW<1.11 backwards compatible settings:
 225+ * $wgUploadPath, $wgThumbnailScriptPath, $wgSharedUploadDirectory, etc.
 226+ */
 227+$wgLocalFileRepo = false;
 228+$wgForeignFileRepos = array();
 229+/**#@-*/
 230+
188231 /**
189232 * Allowed title characters -- regex character class
190233 * Don't change this unless you know what you're doing
@@ -355,6 +398,10 @@
356399 * no file of the given name is found in the local repository (for [[Image:..]],
357400 * [[Media:..]] links). Thumbnails will also be looked for and generated in this
358401 * directory.
 402+ *
 403+ * Note that these configuration settings can now be defined on a per-
 404+ * repository basis for an arbitrary number of file repositories, using the
 405+ * $wgForeignFileRepos variable.
359406 */
360407 $wgUseSharedUploads = false;
361408 /** Full path on the web server where shared uploads can be found */
@@ -1358,6 +1405,13 @@
13591406 /**
13601407 * Show EXIF data, on by default if available.
13611408 * Requires PHP's EXIF extension: http://www.php.net/manual/en/ref.exif.php
 1409+ *
 1410+ * NOTE FOR WINDOWS USERS:
 1411+ * To enable EXIF functions, add the folloing lines to the
 1412+ * "Windows extensions" section of php.ini:
 1413+ *
 1414+ * extension=extensions/php_mbstring.dll
 1415+ * extension=extensions/php_exif.dll
13621416 */
13631417 $wgShowEXIF = function_exists( 'exif_read_data' );
13641418
@@ -2008,7 +2062,13 @@
20092063 * @link http://en.wikipedia.org/w/index.php?title=User%3A%C6var_Arnfj%F6r%F0_Bjarmason%2Ftestme&diff=12356041&oldid=12355864
20102064 * @link http://en.wikipedia.org/wiki/Template%3AOS9
20112065 */
2012 - '/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/'
 2066+ '/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/',
 2067+
 2068+ /**
 2069+ * Google wireless transcoder, seems to eat a lot of chars alive
 2070+ * http://it.wikipedia.org/w/index.php?title=Luciano_Ligabue&diff=prev&oldid=8857361
 2071+ */
 2072+ '/^Mozilla\/4\.0 \(compatible; MSIE 6.0; Windows NT 5.0; Google Wireless Transcoder;\)/'
20132073 );
20142074
20152075 /**
Index: branches/liquidthreads/includes/SpecialMIMEsearch.php
@@ -66,7 +66,7 @@
6767 $text = $wgContLang->convert( $nt->getText() );
6868 $plink = $skin->makeLink( $nt->getPrefixedText(), $text );
6969
70 - $download = $skin->makeMediaLink( $nt->getText(), 'fuck me!', wfMsgHtml( 'download' ) );
 70+ $download = $skin->makeMediaLinkObj( $nt, wfMsgHtml( 'download' ) );
7171 $bytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
7272 $wgLang->formatNum( $result->img_size ) );
7373 $dimensions = wfMsgHtml( 'widthheight', $wgLang->formatNum( $result->img_width ),
Index: branches/liquidthreads/includes/SpecialBlockip.php
@@ -196,7 +196,8 @@
197197 <td align=\"$alignRight\">{$mIpbreason}</td>
198198 <td>
199199 " . Xml::input( 'wpBlockReason', 45, $this->BlockReason,
200 - array( 'tabindex' => '5', 'id' => 'mw-bi-reason' ) ) . "
 200+ array( 'tabindex' => '5', 'id' => 'mw-bi-reason',
 201+ 'maxlength'=> '200' ) ) . "
201202 </td>
202203 </tr>
203204 <tr id='wpAnonOnlyRow'>
Index: branches/liquidthreads/includes/media/DjVu.php
@@ -69,7 +69,7 @@
7070 }
7171 $width = $params['width'];
7272 $height = $params['height'];
73 - $srcPath = $image->getImagePath();
 73+ $srcPath = $image->getPath();
7474 $page = $params['page'];
7575 if ( $page > $this->pageCount( $image ) ) {
7676 return new MediaTransformError( 'thumbnail_error', $width, $height, wfMsg( 'djvu_page_error' ) );
Index: branches/liquidthreads/includes/media/Bitmap.php
@@ -51,7 +51,7 @@
5252 $srcWidth = $image->getWidth();
5353 $srcHeight = $image->getHeight();
5454 $mimeType = $image->getMimeType();
55 - $srcPath = $image->getImagePath();
 55+ $srcPath = $image->getPath();
5656 $retval = 0;
5757 wfDebug( __METHOD__.": creating {$physicalWidth}x{$physicalHeight} thumbnail at $dstPath\n" );
5858
@@ -61,7 +61,10 @@
6262 return new ThumbnailImage( $image->getURL(), $clientWidth, $clientHeight, $srcPath );
6363 }
6464
65 - if ( $wgUseImageMagick ) {
 65+ if ( !$dstPath ) {
 66+ // No output path available, client side scaling only
 67+ $scaler = 'client';
 68+ } elseif ( $wgUseImageMagick ) {
6669 $scaler = 'im';
6770 } elseif ( $wgCustomConvertCommand ) {
6871 $scaler = 'custom';
Index: branches/liquidthreads/includes/media/Generic.php
@@ -152,7 +152,7 @@
153153 * Returns false if unknown or if the document is not multi-page.
154154 */
155155 function getPageDimensions( $image, $page ) {
156 - $gis = $this->getImageSize( $image, $image->getImagePath() );
 156+ $gis = $this->getImageSize( $image, $image->getPath() );
157157 return array(
158158 'width' => $gis[0],
159159 'height' => $gis[1]
@@ -220,7 +220,7 @@
221221 $params['width'] = wfFitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
222222 }
223223 }
224 - $params['height'] = Image::scaleHeight( $srcWidth, $srcHeight, $params['width'] );
 224+ $params['height'] = File::scaleHeight( $srcWidth, $srcHeight, $params['width'] );
225225 if ( !$this->validateThumbParams( $params['width'], $params['height'], $srcWidth, $srcHeight, $mimeType ) ) {
226226 return false;
227227 }
@@ -254,7 +254,7 @@
255255 return false;
256256 }
257257
258 - $height = Image::scaleHeight( $srcWidth, $srcHeight, $width );
 258+ $height = File::scaleHeight( $srcWidth, $srcHeight, $width );
259259 return true;
260260 }
261261
Index: branches/liquidthreads/includes/media/SVG.php
@@ -31,7 +31,7 @@
3232 $srcWidth = $image->getWidth( $params['page'] );
3333 $srcHeight = $image->getHeight( $params['page'] );
3434 $params['physicalWidth'] = $wgSVGMaxSize;
35 - $params['physicalHeight'] = Image::scaleHeight( $srcWidth, $srcHeight, $wgSVGMaxSize );
 35+ $params['physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight, $wgSVGMaxSize );
3636 }
3737 return true;
3838 }
@@ -46,7 +46,7 @@
4747 $clientHeight = $params['height'];
4848 $physicalWidth = $params['physicalWidth'];
4949 $physicalHeight = $params['physicalHeight'];
50 - $srcPath = $image->getImagePath();
 50+ $srcPath = $image->getPath();
5151
5252 if ( $flags & self::TRANSFORM_LATER ) {
5353 return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
Index: branches/liquidthreads/includes/normal/UtfNormal.php
@@ -29,59 +29,6 @@
3030 global $utfCompatibilityDecomp;
3131 $utfCompatibilityDecomp = NULL;
3232
33 -define( 'UNICODE_HANGUL_FIRST', 0xac00 );
34 -define( 'UNICODE_HANGUL_LAST', 0xd7a3 );
35 -
36 -define( 'UNICODE_HANGUL_LBASE', 0x1100 );
37 -define( 'UNICODE_HANGUL_VBASE', 0x1161 );
38 -define( 'UNICODE_HANGUL_TBASE', 0x11a7 );
39 -
40 -define( 'UNICODE_HANGUL_LCOUNT', 19 );
41 -define( 'UNICODE_HANGUL_VCOUNT', 21 );
42 -define( 'UNICODE_HANGUL_TCOUNT', 28 );
43 -define( 'UNICODE_HANGUL_NCOUNT', UNICODE_HANGUL_VCOUNT * UNICODE_HANGUL_TCOUNT );
44 -
45 -define( 'UNICODE_HANGUL_LEND', UNICODE_HANGUL_LBASE + UNICODE_HANGUL_LCOUNT - 1 );
46 -define( 'UNICODE_HANGUL_VEND', UNICODE_HANGUL_VBASE + UNICODE_HANGUL_VCOUNT - 1 );
47 -define( 'UNICODE_HANGUL_TEND', UNICODE_HANGUL_TBASE + UNICODE_HANGUL_TCOUNT - 1 );
48 -
49 -define( 'UNICODE_SURROGATE_FIRST', 0xd800 );
50 -define( 'UNICODE_SURROGATE_LAST', 0xdfff );
51 -define( 'UNICODE_MAX', 0x10ffff );
52 -define( 'UNICODE_REPLACEMENT', 0xfffd );
53 -
54 -
55 -define( 'UTF8_HANGUL_FIRST', "\xea\xb0\x80" /*codepointToUtf8( UNICODE_HANGUL_FIRST )*/ );
56 -define( 'UTF8_HANGUL_LAST', "\xed\x9e\xa3" /*codepointToUtf8( UNICODE_HANGUL_LAST )*/ );
57 -
58 -define( 'UTF8_HANGUL_LBASE', "\xe1\x84\x80" /*codepointToUtf8( UNICODE_HANGUL_LBASE )*/ );
59 -define( 'UTF8_HANGUL_VBASE', "\xe1\x85\xa1" /*codepointToUtf8( UNICODE_HANGUL_VBASE )*/ );
60 -define( 'UTF8_HANGUL_TBASE', "\xe1\x86\xa7" /*codepointToUtf8( UNICODE_HANGUL_TBASE )*/ );
61 -
62 -define( 'UTF8_HANGUL_LEND', "\xe1\x84\x92" /*codepointToUtf8( UNICODE_HANGUL_LEND )*/ );
63 -define( 'UTF8_HANGUL_VEND', "\xe1\x85\xb5" /*codepointToUtf8( UNICODE_HANGUL_VEND )*/ );
64 -define( 'UTF8_HANGUL_TEND', "\xe1\x87\x82" /*codepointToUtf8( UNICODE_HANGUL_TEND )*/ );
65 -
66 -define( 'UTF8_SURROGATE_FIRST', "\xed\xa0\x80" /*codepointToUtf8( UNICODE_SURROGATE_FIRST )*/ );
67 -define( 'UTF8_SURROGATE_LAST', "\xed\xbf\xbf" /*codepointToUtf8( UNICODE_SURROGATE_LAST )*/ );
68 -define( 'UTF8_MAX', "\xf4\x8f\xbf\xbf" /*codepointToUtf8( UNICODE_MAX )*/ );
69 -define( 'UTF8_REPLACEMENT', "\xef\xbf\xbd" /*codepointToUtf8( UNICODE_REPLACEMENT )*/ );
70 -#define( 'UTF8_REPLACEMENT', '!' );
71 -
72 -define( 'UTF8_OVERLONG_A', "\xc1\xbf" );
73 -define( 'UTF8_OVERLONG_B', "\xe0\x9f\xbf" );
74 -define( 'UTF8_OVERLONG_C', "\xf0\x8f\xbf\xbf" );
75 -
76 -# These two ranges are illegal
77 -define( 'UTF8_FDD0', "\xef\xb7\x90" /*codepointToUtf8( 0xfdd0 )*/ );
78 -define( 'UTF8_FDEF', "\xef\xb7\xaf" /*codepointToUtf8( 0xfdef )*/ );
79 -define( 'UTF8_FFFE', "\xef\xbf\xbe" /*codepointToUtf8( 0xfffe )*/ );
80 -define( 'UTF8_FFFF', "\xef\xbf\xbf" /*codepointToUtf8( 0xffff )*/ );
81 -
82 -define( 'UTF8_HEAD', false );
83 -define( 'UTF8_TAIL', true );
84 -
85 -
8633 /**
8734 * For using the ICU wrapper
8835 */
Index: branches/liquidthreads/includes/Skin.php
@@ -740,8 +740,8 @@
741741 if ( $wgOut->isArticleRelated() ) {
742742 if ( $wgTitle->getNamespace() == NS_IMAGE ) {
743743 $name = $wgTitle->getDBkey();
744 - $image = new Image( $wgTitle );
745 - if( $image->exists() ) {
 744+ $image = wfFindFile( $wgTitle );
 745+ if( $image ) {
746746 $link = htmlspecialchars( $image->getURL() );
747747 $style = $this->getInternalLinkAttributes( $link, $name );
748748 $s .= " | <a href=\"{$link}\"{$style}>{$name}</a>";
Index: branches/liquidthreads/includes/MediaTransformOutput.php
@@ -1,7 +1,7 @@
22 <?php
33
44 /**
5 - * Base class for the output of MediaHandler::doTransform() and Image::transform().
 5+ * Base class for the output of MediaHandler::doTransform() and File::transform().
66 *
77 * @addtogroup Media
88 */
Index: branches/liquidthreads/includes/Setup.php
@@ -54,6 +54,59 @@
5555 if( $wgReadOnlyFile === false ) $wgReadOnlyFile = "{$wgUploadDirectory}/lock_yBgMBwiR";
5656 if( $wgFileCacheDirectory === false ) $wgFileCacheDirectory = "{$wgUploadDirectory}/cache";
5757
 58+/**
 59+ * Initialise $wgLocalFileRepo from backwards-compatible settings
 60+ */
 61+if ( !$wgLocalFileRepo ) {
 62+ $wgLocalFileRepo = array(
 63+ 'class' => 'LocalRepo',
 64+ 'name' => 'local',
 65+ 'directory' => $wgUploadDirectory,
 66+ 'url' => $wgUploadBaseUrl ? $wgUploadBaseUrl . $wgUploadPath : $wgUploadPath,
 67+ 'hashLevels' => $wgHashedUploadDirectory ? 2 : 0,
 68+ 'thumbScriptUrl' => $wgThumbnailScriptPath,
 69+ 'transformVia404' => !$wgGenerateThumbnailOnParse,
 70+ );
 71+}
 72+/**
 73+ * Initialise shared repo from backwards-compatible settings
 74+ */
 75+if ( $wgUseSharedUploads ) {
 76+ if ( $wgSharedUploadDBname ) {
 77+ $wgForeignFileRepos[] = array(
 78+ 'class' => 'ForeignDBRepo',
 79+ 'name' => 'shared',
 80+ 'directory' => $wgSharedUploadDirectory,
 81+ 'url' => $wgSharedUploadPath,
 82+ 'hashLevels' => $wgHashedSharedUploadDirectory ? 2 : 0,
 83+ 'thumbScriptUrl' => $wgSharedThumbnailScriptPath,
 84+ 'transformVia404' => !$wgGenerateThumbnailOnParse,
 85+ 'dbType' => $wgDBtype,
 86+ 'dbServer' => $wgDBserver,
 87+ 'dbUser' => $wgDBuser,
 88+ 'dbPassword' => $wgDBpassword,
 89+ 'dbName' => $wgSharedUploadDBname,
 90+ 'dbFlags' => DBO_DEFAULT,
 91+ 'tablePrefix' => $wgSharedUploadDBprefix,
 92+ 'hasSharedCache' => $wgCacheSharedUploads,
 93+ 'descBaseUrl' => $wgRepositoryBaseUrl,
 94+ 'fetchDescription' => $wgFetchCommonsDescriptions,
 95+ );
 96+ } else {
 97+ $wgForeignFileRepos[] = array(
 98+ 'class' => 'FSRepo',
 99+ 'name' => 'shared',
 100+ 'directory' => $wgSharedUploadDirectory,
 101+ 'url' => $wgSharedUploadPath,
 102+ 'hashLevels' => $wgHashedSharedUploadDirectory ? 2 : 0,
 103+ 'thumbScriptUrl' => $wgSharedThumbnailScriptPath,
 104+ 'transformVia404' => !$wgGenerateThumbnailOnParse,
 105+ 'descBaseUrl' => $wgRepositoryBaseUrl,
 106+ 'fetchDescription' => $wgFetchCommonsDescriptions,
 107+ );
 108+ }
 109+}
 110+
58111 require_once( "$IP/includes/AutoLoader.php" );
59112
60113 wfProfileIn( $fname.'-exception' );
Index: branches/liquidthreads/includes/ImageGallery.php
@@ -127,22 +127,26 @@
128128 /**
129129 * Add an image to the gallery.
130130 *
131 - * @param $image Image object that is added to the gallery
 131+ * @param $title Title object of the image that is added to the gallery
132132 * @param $html String: additional HTML text to be shown. The name and size of the image are always shown.
133133 */
134 - function add( $image, $html='' ) {
135 - $this->mImages[] = array( &$image, $html );
136 - wfDebug( "ImageGallery::add " . $image->getName() . "\n" );
 134+ function add( $title, $html='' ) {
 135+ if ( $title instanceof File ) {
 136+ // Old calling convention
 137+ $title = $title->getTitle();
 138+ }
 139+ $this->mImages[] = array( $title, $html );
 140+ wfDebug( "ImageGallery::add " . $title->getText() . "\n" );
137141 }
138142
139143 /**
140144 * Add an image at the beginning of the gallery.
141145 *
142 - * @param $image Image object that is added to the gallery
 146+ * @param $title Title object of the image that is added to the gallery
143147 * @param $html String: Additional HTML text to be shown. The name and size of the image are always shown.
144148 */
145 - function insert( $image, $html='' ) {
146 - array_unshift( $this->mImages, array( &$image, $html ) );
 149+ function insert( $title, $html='' ) {
 150+ array_unshift( $this->mImages, array( &$title, $html ) );
147151 }
148152
149153
@@ -195,12 +199,12 @@
196200 $params = array( 'width' => $this->mWidths, 'height' => $this->mHeights );
197201 $i = 0;
198202 foreach ( $this->mImages as $pair ) {
199 - $img =& $pair[0];
 203+ $nt = $pair[0];
200204 $text = $pair[1];
201205
202 - $nt = $img->getTitle();
 206+ $img = wfFindFile( $nt );
203207
204 - if( $nt->getNamespace() != NS_IMAGE ) {
 208+ if( $nt->getNamespace() != NS_IMAGE || !$img ) {
205209 # We're dealing with a non-image, spit out the name and be done with it.
206210 $thumbhtml = "\n\t\t\t".'<div style="height: '.($this->mHeights*1.25+2).'px;">'
207211 . htmlspecialchars( $nt->getText() ) . '</div>';
@@ -222,7 +226,7 @@
223227 //$ul = $sk->makeLink( $wgContLang->getNsText( Namespace::getUser() ) . ":{$ut}", $ut );
224228
225229 if( $this->mShowBytes ) {
226 - if( $img->exists() ) {
 230+ if( $img ) {
227231 $nb = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
228232 $wgLang->formatNum( $img->getSize() ) );
229233 } else {
Index: branches/liquidthreads/includes/Defines.php
@@ -205,5 +205,61 @@
206206 define( 'LIST_NAMES', 3);
207207 define( 'LIST_OR', 4);
208208
 209+/**
 210+ * Unicode and normalisation related
 211+ */
 212+define( 'UNICODE_HANGUL_FIRST', 0xac00 );
 213+define( 'UNICODE_HANGUL_LAST', 0xd7a3 );
209214
 215+define( 'UNICODE_HANGUL_LBASE', 0x1100 );
 216+define( 'UNICODE_HANGUL_VBASE', 0x1161 );
 217+define( 'UNICODE_HANGUL_TBASE', 0x11a7 );
 218+
 219+define( 'UNICODE_HANGUL_LCOUNT', 19 );
 220+define( 'UNICODE_HANGUL_VCOUNT', 21 );
 221+define( 'UNICODE_HANGUL_TCOUNT', 28 );
 222+define( 'UNICODE_HANGUL_NCOUNT', UNICODE_HANGUL_VCOUNT * UNICODE_HANGUL_TCOUNT );
 223+
 224+define( 'UNICODE_HANGUL_LEND', UNICODE_HANGUL_LBASE + UNICODE_HANGUL_LCOUNT - 1 );
 225+define( 'UNICODE_HANGUL_VEND', UNICODE_HANGUL_VBASE + UNICODE_HANGUL_VCOUNT - 1 );
 226+define( 'UNICODE_HANGUL_TEND', UNICODE_HANGUL_TBASE + UNICODE_HANGUL_TCOUNT - 1 );
 227+
 228+define( 'UNICODE_SURROGATE_FIRST', 0xd800 );
 229+define( 'UNICODE_SURROGATE_LAST', 0xdfff );
 230+define( 'UNICODE_MAX', 0x10ffff );
 231+define( 'UNICODE_REPLACEMENT', 0xfffd );
 232+
 233+
 234+define( 'UTF8_HANGUL_FIRST', "\xea\xb0\x80" /*codepointToUtf8( UNICODE_HANGUL_FIRST )*/ );
 235+define( 'UTF8_HANGUL_LAST', "\xed\x9e\xa3" /*codepointToUtf8( UNICODE_HANGUL_LAST )*/ );
 236+
 237+define( 'UTF8_HANGUL_LBASE', "\xe1\x84\x80" /*codepointToUtf8( UNICODE_HANGUL_LBASE )*/ );
 238+define( 'UTF8_HANGUL_VBASE', "\xe1\x85\xa1" /*codepointToUtf8( UNICODE_HANGUL_VBASE )*/ );
 239+define( 'UTF8_HANGUL_TBASE', "\xe1\x86\xa7" /*codepointToUtf8( UNICODE_HANGUL_TBASE )*/ );
 240+
 241+define( 'UTF8_HANGUL_LEND', "\xe1\x84\x92" /*codepointToUtf8( UNICODE_HANGUL_LEND )*/ );
 242+define( 'UTF8_HANGUL_VEND', "\xe1\x85\xb5" /*codepointToUtf8( UNICODE_HANGUL_VEND )*/ );
 243+define( 'UTF8_HANGUL_TEND', "\xe1\x87\x82" /*codepointToUtf8( UNICODE_HANGUL_TEND )*/ );
 244+
 245+define( 'UTF8_SURROGATE_FIRST', "\xed\xa0\x80" /*codepointToUtf8( UNICODE_SURROGATE_FIRST )*/ );
 246+define( 'UTF8_SURROGATE_LAST', "\xed\xbf\xbf" /*codepointToUtf8( UNICODE_SURROGATE_LAST )*/ );
 247+define( 'UTF8_MAX', "\xf4\x8f\xbf\xbf" /*codepointToUtf8( UNICODE_MAX )*/ );
 248+define( 'UTF8_REPLACEMENT', "\xef\xbf\xbd" /*codepointToUtf8( UNICODE_REPLACEMENT )*/ );
 249+#define( 'UTF8_REPLACEMENT', '!' );
 250+
 251+define( 'UTF8_OVERLONG_A', "\xc1\xbf" );
 252+define( 'UTF8_OVERLONG_B', "\xe0\x9f\xbf" );
 253+define( 'UTF8_OVERLONG_C', "\xf0\x8f\xbf\xbf" );
 254+
 255+# These two ranges are illegal
 256+define( 'UTF8_FDD0', "\xef\xb7\x90" /*codepointToUtf8( 0xfdd0 )*/ );
 257+define( 'UTF8_FDEF', "\xef\xb7\xaf" /*codepointToUtf8( 0xfdef )*/ );
 258+define( 'UTF8_FFFE', "\xef\xbf\xbe" /*codepointToUtf8( 0xfffe )*/ );
 259+define( 'UTF8_FFFF', "\xef\xbf\xbf" /*codepointToUtf8( 0xffff )*/ );
 260+
 261+define( 'UTF8_HEAD', false );
 262+define( 'UTF8_TAIL', true );
 263+
 264+
 265+
210266 ?>
Index: branches/liquidthreads/includes/SpecialNewimages.php
@@ -135,10 +135,9 @@
136136 $ut = $s->img_user_text;
137137
138138 $nt = Title::newFromText( $name, NS_IMAGE );
139 - $img = new Image( $nt );
140139 $ul = $sk->makeLinkObj( Title::makeTitle( NS_USER, $ut ), $ut );
141140
142 - $gallery->add( $img, "$ul<br />\n<i>".$wgLang->timeanddate( $s->img_timestamp, true )."</i><br />\n" );
 141+ $gallery->add( $nt, "$ul<br />\n<i>".$wgLang->timeanddate( $s->img_timestamp, true )."</i><br />\n" );
143142
144143 $timestamp = wfTimestamp( TS_MW, $s->img_timestamp );
145144 if( empty( $firstTimestamp ) ) {
Index: branches/liquidthreads/includes/SpecialUpload.php
@@ -27,6 +27,7 @@
2828 var $mUploadCopyStatus, $mUploadSource, $mReUpload, $mAction, $mUpload;
2929 var $mOname, $mSessionKey, $mStashed, $mDestFile, $mRemoveTempFile, $mSourceType;
3030 var $mUploadTempFileSize = 0;
 31+ var $mImage;
3132
3233 # Placeholders for text injection by hooks (must be HTML)
3334 # extensions should take care to _append_ to the present value
@@ -412,13 +413,13 @@
413414
414415 global $wgUser;
415416 $sk = $wgUser->getSkin();
416 - $image = new Image( $nt );
 417+ $image = wfLocalFile( $nt );
417418
418419 // Check for uppercase extension. We allow these filenames but check if an image
419420 // with lowercase extension exists already
420421 if ( $finalExt != strtolower( $finalExt ) ) {
421422 $nt_lc = Title::newFromText( $partname . '.' . strtolower( $finalExt ) );
422 - $image_lc = new Image( $nt_lc );
 423+ $image_lc = wfLocalFile( $nt_lc );
423424 }
424425
425426 if( $image->exists() ) {
@@ -452,7 +453,7 @@
453454 } elseif ( ( substr( $partname , 3, 3 ) == 'px-' || substr( $partname , 2, 3 ) == 'px-' ) && ereg( "[0-9]{2}" , substr( $partname , 0, 2) ) ) {
454455 # Check for filenames like 50px- or 180px-, these are mostly thumbnails
455456 $nt_thb = Title::newFromText( substr( $partname , strpos( $partname , '-' ) +1 ) . '.' . $finalExt );
456 - $image_thb = new Image( $nt_thb );
 457+ $image_thb = wfLocalFile( $nt_thb );
457458 if ($image_thb->exists() ) {
458459 # Check if an image without leading '180px-' (or similiar) exists
459460 $dlink = $sk->makeKnownLinkObj( $nt_thb);
@@ -500,8 +501,8 @@
501502 * Update the upload log and create the description page
502503 * if it's a new file.
503504 */
504 - $img = Image::newFromName( $this->mUploadSaveName );
505 - $success = $img->recordUpload( $this->mUploadOldVersion,
 505+ $this->mImage = wfLocalFile( $this->mUploadSaveName );
 506+ $success = $this->mImage->recordUpload( $this->mUploadOldVersion,
506507 $this->mUploadDescription,
507508 $this->mLicense,
508509 $this->mUploadCopyStatus,
@@ -512,7 +513,7 @@
513514 $this->showSuccess();
514515 wfRunHooks( 'UploadComplete', array( &$img ) );
515516 } else {
516 - // Image::recordUpload() fails if the image went missing, which is
 517+ // File::recordUpload() fails if the image went missing, which is
517518 // unlikely, hence the lack of a specialised message
518519 $wgOut->showFileNotFoundError( $this->mUploadSaveName );
519520 }
@@ -534,48 +535,13 @@
535536 function saveUploadedFile( $saveName, $tempName, $useRename = false ) {
536537 global $wgOut, $wgAllowCopyUploads;
537538
538 - if ( !$useRename AND $wgAllowCopyUploads AND $this->mSourceType == 'web' ) $useRename = true;
539 -
540 - $fname= "SpecialUpload::saveUploadedFile";
541 -
542 - $dest = wfImageDir( $saveName );
543 - $archive = wfImageArchiveDir( $saveName );
544 - if ( !is_dir( $dest ) ) wfMkdirParents( $dest );
545 - if ( !is_dir( $archive ) ) wfMkdirParents( $archive );
546 -
547 - $this->mSavedFile = "{$dest}/{$saveName}";
548 -
549 - if( is_file( $this->mSavedFile ) ) {
550 - $this->mUploadOldVersion = gmdate( 'YmdHis' ) . "!{$saveName}";
551 - wfSuppressWarnings();
552 - $success = rename( $this->mSavedFile, "${archive}/{$this->mUploadOldVersion}" );
553 - wfRestoreWarnings();
554 -
555 - if( ! $success ) {
556 - $wgOut->showFileRenameError( $this->mSavedFile,
557 - "${archive}/{$this->mUploadOldVersion}" );
558 - return false;
559 - }
560 - else wfDebug("$fname: moved file ".$this->mSavedFile." to ${archive}/{$this->mUploadOldVersion}\n");
561 - }
562 - else {
563 - $this->mUploadOldVersion = '';
564 - }
565 -
566 - wfSuppressWarnings();
567 - $success = $useRename
568 - ? rename( $tempName, $this->mSavedFile )
569 - : move_uploaded_file( $tempName, $this->mSavedFile );
570 - wfRestoreWarnings();
571 -
572 - if( ! $success ) {
573 - $wgOut->showFileCopyError( $tempName, $this->mSavedFile );
 539+ $image = wfLocalFile( $saveName );
 540+ $archiveName = $image->publish( $tempName, File::DELETE_SOURCE );
 541+ if ( WikiError::isError( $archiveName ) ) {
 542+ $this->showError( $archiveName );
574543 return false;
575 - } else {
576 - wfDebug("$fname: wrote tempfile $tempName to ".$this->mSavedFile."\n");
577544 }
578 -
579 - chmod( $this->mSavedFile, 0644 );
 545+ $this->mUploadOldVersion = $archiveName;
580546 return true;
581547 }
582548
@@ -593,19 +559,14 @@
594560 */
595561 function saveTempUploadedFile( $saveName, $tempName ) {
596562 global $wgOut;
597 - $archive = wfImageArchiveDir( $saveName, 'temp' );
598 - if ( !is_dir ( $archive ) ) wfMkdirParents( $archive );
599 - $stash = $archive . '/' . gmdate( "YmdHis" ) . '!' . $saveName;
600 -
601 - $success = $this->mRemoveTempFile
602 - ? rename( $tempName, $stash )
603 - : move_uploaded_file( $tempName, $stash );
604 - if ( !$success ) {
605 - $wgOut->showFileCopyError( $tempName, $stash );
 563+ $repo = RepoGroup::singleton()->getLocalRepo();
 564+ $result = $repo->storeTemp( $saveName, $tempName );
 565+ if ( WikiError::isError( $result ) ) {
 566+ $this->showError( $result );
606567 return false;
 568+ } else {
 569+ return $result;
607570 }
608 -
609 - return $stash;
610571 }
611572
612573 /**
@@ -662,7 +623,7 @@
663624 global $wgUser, $wgOut, $wgContLang;
664625
665626 $sk = $wgUser->getSkin();
666 - $ilink = $sk->makeMediaLink( $this->mUploadSaveName, Image::imageUrl( $this->mUploadSaveName ) );
 627+ $ilink = $sk->makeMediaLinkObj( $this->mImage->getTitle() );
667628 $dname = $wgContLang->getNsText( NS_IMAGE ) . ':'.$this->mUploadSaveName;
668629 $dlink = $sk->makeKnownLink( $dname, $dname );
669630
@@ -1274,15 +1235,10 @@
12751236 * @access private
12761237 */
12771238 function checkOverwrite( $name ) {
1278 - $img = Image::newFromName( $name );
1279 - if( is_null( $img ) ) {
1280 - // Uh... this shouldn't happen ;)
1281 - // But if it does, fall through to previous behavior
1282 - return false;
1283 - }
 1239+ $img = wfFindFile( $name );
12841240
12851241 $error = '';
1286 - if( $img->exists() ) {
 1242+ if( $img ) {
12871243 global $wgUser, $wgOut;
12881244 if( $img->isLocal() ) {
12891245 if( !self::userCanReUpload( $wgUser, $img->name ) ) {
@@ -1328,5 +1284,17 @@
13291285
13301286 return $user->getID() == $row->img_user;
13311287 }
 1288+
 1289+ /**
 1290+ * Display an error from a wikitext-formatted WikiError object
 1291+ */
 1292+ function showError( WikiError $error ) {
 1293+ global $wgOut;
 1294+ $wgOut->setPageTitle( wfMsg( "internalerror" ) );
 1295+ $wgOut->setRobotpolicy( "noindex,nofollow" );
 1296+ $wgOut->setArticleRelated( false );
 1297+ $wgOut->enableClientCache( false );
 1298+ $wgOut->addWikiText( $error->getMessage() );
 1299+ }
13321300 }
13331301 ?>
Index: branches/liquidthreads/includes/GlobalFunctions.php
@@ -2272,4 +2272,26 @@
22732273 $ret = $wgLoadBalancer->getConnection( $db, true, $groups );
22742274 return $ret;
22752275 }
 2276+
 2277+/**
 2278+ * Find a file.
 2279+ * Shortcut for RepoGroup::singleton()->findFile()
 2280+ * @param mixed $title Title object or string. May be interwiki.
 2281+ * @param mixed $time Requested time for an archived image, or false for the
 2282+ * current version. An image object will be returned which
 2283+ * existed at or before the specified time.
 2284+ * @return File, or false if the file does not exist
 2285+ */
 2286+function wfFindFile( $title, $time = false ) {
 2287+ return RepoGroup::singleton()->findFile( $title, $time );
 2288+}
 2289+
 2290+/**
 2291+ * Get an object referring to a locally registered file.
 2292+ * Returns a valid placeholder object if the file does not exist.
 2293+ */
 2294+function wfLocalFile( $title ) {
 2295+ return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
 2296+}
 2297+
22762298 ?>
Index: branches/liquidthreads/includes/CategoryPage.php
@@ -147,7 +147,7 @@
148148 /**
149149 * Add a page in the image namespace
150150 */
151 - function addImage( $title, $sortkey, $pageLength, $isRedirect = false ) {
 151+ function addImage( Title $title, $sortkey, $pageLength, $isRedirect = false ) {
152152 if ( $this->showGallery ) {
153153 $image = new Image( $title );
154154 if( $this->flip ) {
@@ -222,7 +222,7 @@
223223
224224 if( $title->getNamespace() == NS_CATEGORY ) {
225225 $this->addSubcategory( $title, $x->cl_sortkey, $x->page_len );
226 - } elseif( $title->getNamespace() == NS_IMAGE ) {
 226+ } elseif( $this->showGallery && $title->getNamespace() == NS_IMAGE ) {
227227 $this->addImage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
228228 } else {
229229 $this->addPage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
Index: branches/liquidthreads/includes/ImageFunctions.php
@@ -1,114 +1,5 @@
22 <?php
3 -
43 /**
5 - * Returns the image directory of an image
6 - * The result is an absolute path.
7 - *
8 - * This function is called from thumb.php before Setup.php is included
9 - *
10 - * @param $fname String: file name of the image file.
11 - * @public
12 - */
13 -function wfImageDir( $fname ) {
14 - global $wgUploadDirectory, $wgHashedUploadDirectory;
15 -
16 - if (!$wgHashedUploadDirectory) { return $wgUploadDirectory; }
17 -
18 - $hash = md5( $fname );
19 - $dest = $wgUploadDirectory . '/' . $hash{0} . '/' . substr( $hash, 0, 2 );
20 -
21 - return $dest;
22 -}
23 -
24 -/**
25 - * Returns the image directory of an image's thumbnail
26 - * The result is an absolute path.
27 - *
28 - * This function is called from thumb.php before Setup.php is included
29 - *
30 - * @param $fname String: file name of the original image file
31 - * @param $shared Boolean: (optional) use the shared upload directory (default: 'false').
32 - * @public
33 - */
34 -function wfImageThumbDir( $fname, $shared = false ) {
35 - $base = wfImageArchiveDir( $fname, 'thumb', $shared );
36 - if ( Image::isHashed( $shared ) ) {
37 - $dir = "$base/$fname";
38 - } else {
39 - $dir = $base;
40 - }
41 -
42 - return $dir;
43 -}
44 -
45 -/**
46 - * Old thumbnail directory, kept for conversion
47 - */
48 -function wfDeprecatedThumbDir( $thumbName , $subdir='thumb', $shared=false) {
49 - return wfImageArchiveDir( $thumbName, $subdir, $shared );
50 -}
51 -
52 -/**
53 - * Returns the image directory of an image's old version
54 - * The result is an absolute path.
55 - *
56 - * This function is called from thumb.php before Setup.php is included
57 - *
58 - * @param $fname String: file name of the thumbnail file, including file size prefix.
59 - * @param $subdir String: subdirectory of the image upload directory that should be used for storing the old version. Default is 'archive'.
60 - * @param $shared Boolean use the shared upload directory (only relevant for other functions which call this one). Default is 'false'.
61 - * @public
62 - */
63 -function wfImageArchiveDir( $fname , $subdir='archive', $shared=false ) {
64 - global $wgUploadDirectory, $wgHashedUploadDirectory;
65 - global $wgSharedUploadDirectory, $wgHashedSharedUploadDirectory;
66 - $dir = $shared ? $wgSharedUploadDirectory : $wgUploadDirectory;
67 - $hashdir = $shared ? $wgHashedSharedUploadDirectory : $wgHashedUploadDirectory;
68 - if (!$hashdir) { return $dir.'/'.$subdir; }
69 - $hash = md5( $fname );
70 -
71 - return $dir.'/'.$subdir.'/'.$hash[0].'/'.substr( $hash, 0, 2 );
72 -}
73 -
74 -
75 -/*
76 - * Return the hash path component of an image path (URL or filesystem),
77 - * e.g. "/3/3c/", or just "/" if hashing is not used.
78 - *
79 - * @param $dbkey The filesystem / database name of the file
80 - * @param $fromSharedDirectory Use the shared file repository? It may
81 - * use different hash settings from the local one.
82 - */
83 -function wfGetHashPath ( $dbkey, $fromSharedDirectory = false ) {
84 - if( Image::isHashed( $fromSharedDirectory ) ) {
85 - $hash = md5($dbkey);
86 - return '/' . $hash{0} . '/' . substr( $hash, 0, 2 ) . '/';
87 - } else {
88 - return '/';
89 - }
90 -}
91 -
92 -/**
93 - * Returns the image URL of an image's old version
94 - *
95 - * @param $name String: file name of the image file
96 - * @param $subdir String: (optional) subdirectory of the image upload directory that is used by the old version. Default is 'archive'
97 - * @public
98 - */
99 -function wfImageArchiveUrl( $name, $subdir='archive' ) {
100 - global $wgUploadPath, $wgHashedUploadDirectory;
101 -
102 - if ($wgHashedUploadDirectory) {
103 - $hash = md5( substr( $name, 15) );
104 - $url = $wgUploadPath.'/'.$subdir.'/' . $hash{0} . '/' .
105 - substr( $hash, 0, 2 ) . '/'.$name;
106 - } else {
107 - $url = $wgUploadPath.'/'.$subdir.'/'.$name;
108 - }
109 - return wfUrlencode($url);
110 -}
111 -
112 -/**
1134 * Return a rounded pixel equivalent for a labeled CSS/SVG length.
1145 * http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers
1156 *
Index: branches/liquidthreads/includes/SpecialImagelist.php
@@ -115,7 +115,9 @@
116116 case 'img_name':
117117 $name = $this->mCurrentRow->img_name;
118118 $link = $this->getSkin()->makeKnownLinkObj( Title::makeTitle( NS_IMAGE, $name ), $value );
119 - $download = Xml::element('a', array( "href" => Image::imageUrl( $name ) ), $this->mMessages['imgfile'] );
 119+ $image = wfLocalFile( $value );
 120+ $url = $image->getURL();
 121+ $download = Xml::element('a', array( "href" => $url ), $this->mMessages['imgfile'] );
120122 return "$link ($download)";
121123 case 'img_user_text':
122124 if ( $this->mCurrentRow->img_user ) {
Index: branches/liquidthreads/StartProfiler.php
@@ -1,22 +1,24 @@
22 <?php
33
4 -require_once( dirname(__FILE__).'/includes/ProfilerStub.php' );
 4+#require_once( './includes/ProfilerStub.php' );
55
66 /**
77 * To use a profiler, delete the line above and add something like this:
88 *
9 - * require_once( dirname(__FILE__).'/includes/Profiler.php' );
 9+ * require_once( './includes/Profiler.php' );
1010 * $wgProfiler = new Profiler;
1111 *
1212 * Or for a sampling profiler:
1313 * if ( !mt_rand( 0, 100 ) ) {
14 - * require_once( dirname(__FILE__).'/includes/Profiler.php' );
 14+ * require_once( './includes/Profiler.php' );
1515 * $wgProfiler = new Profiler;
1616 * } else {
17 - * require_once( dirname(__FILE__).'/includes/ProfilerStub.php' );
 17+ * require_once( './includes/ProfilerStub.php' );
1818 * }
1919 *
2020 * Configuration of the profiler output can be done in LocalSettings.php
2121 */
 22+require_once( dirname(__FILE__).'/includes/Profiler.php' );
 23+$wgProfiler = new Profiler;
2224
2325 ?>
Index: branches/liquidthreads/thumb.php
@@ -2,20 +2,15 @@
33
44 /**
55 * PHP script to stream out an image thumbnail.
6 - * If the file exists, we make do with abridged MediaWiki initialisation.
76 */
8 -define( 'MW_NO_SETUP', 1 );
97 define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
108 require_once( './includes/WebStart.php' );
119 wfProfileIn( 'thumb.php' );
1210 wfProfileIn( 'thumb.php-start' );
13 -require_once( "$IP/includes/GlobalFunctions.php" );
14 -require_once( "$IP/includes/ImageFunctions.php" );
1511
1612 $wgTrivialMimeDetection = true; //don't use fancy mime detection, just check the file extension for jpg/gif/png.
1713
1814 require_once( "$IP/includes/StreamFile.php" );
19 -require_once( "$IP/includes/AutoLoader.php" );
2015
2116 // Get input parameters
2217 if ( get_magic_quotes_gpc() ) {
@@ -40,36 +35,30 @@
4136 // Some basic input validation
4237 $fileName = strtr( $fileName, '\\/', '__' );
4338
44 -// Work out paths, carefully avoiding constructing an Image object because that won't work yet
 39+// Stream the file if it exists already
4540 try {
46 - $handler = thumbGetHandler( $fileName );
47 - if ( $handler ) {
48 - $imagePath = wfImageDir( $fileName ) . '/' . $fileName;
49 - $thumbName = $handler->makeParamString( $params ) . "-$fileName";
50 - $thumbPath = wfImageThumbDir( $fileName ) . '/' . $thumbName;
 41+ $img = wfLocalFile( $fileName );
 42+ if ( $img && false != ( $thumbName = $img->thumbName( $params ) ) ) {
 43+ $thumbPath = $img->getThumbPath( $thumbName );
5144
52 - if ( is_file( $thumbPath ) && filemtime( $thumbPath ) >= filemtime( $imagePath ) ) {
 45+ if ( is_file( $thumbPath ) ) {
5346 wfStreamFile( $thumbPath );
54 - // Can't log profiling data with no Setup.php
 47+ wfLogProfilingData();
5548 exit;
5649 }
5750 }
5851 } catch ( MWException $e ) {
59 - require_once( './includes/Setup.php' );
6052 thumbInternalError( $e->getHTML() );
 53+ wfLogProfilingData();
6154 exit;
6255 }
6356
64 -
65 -// OK, no valid thumbnail, time to get out the heavy machinery
6657 wfProfileOut( 'thumb.php-start' );
67 -require_once( './includes/Setup.php' );
6858 wfProfileIn( 'thumb.php-render' );
6959
70 -$img = Image::newFromName( $fileName );
7160 try {
7261 if ( $img ) {
73 - $thumb = $img->transform( $params, Image::RENDER_NOW );
 62+ $thumb = $img->transform( $params, File::RENDER_NOW );
7463 } else {
7564 $thumb = false;
7665 }
@@ -80,9 +69,11 @@
8170
8271 if ( $thumb && $thumb->getPath() && file_exists( $thumb->getPath() ) ) {
8372 wfStreamFile( $thumb->getPath() );
84 -} elseif ( $img ) {
85 - if ( !$thumb ) {
86 - $msg = wfMsgHtml( 'thumbnail_error', 'Image::transform() returned false' );
 73+} else {
 74+ if ( !$img ) {
 75+ $msg = wfMsg( 'badtitletext' );
 76+ } elseif ( !$thumb ) {
 77+ $msg = wfMsgHtml( 'thumbnail_error', 'File::transform() returned false' );
8778 } elseif ( $thumb->isError() ) {
8879 $msg = $thumb->getHtmlMsg();
8980 } elseif ( !$thumb->getPath() ) {
@@ -91,19 +82,6 @@
9283 $msg = wfMsgHtml( 'thumbnail_error', 'Output file missing' );
9384 }
9485 thumbInternalError( $msg );
95 -} else {
96 - $badtitle = wfMsg( 'badtitle' );
97 - $badtitletext = wfMsg( 'badtitletext' );
98 - header( 'Cache-Control: no-cache' );
99 - header( 'Content-Type: text/html; charset=utf-8' );
100 - header( 'HTTP/1.1 500 Internal server error' );
101 - echo "<html><head>
102 - <title>$badtitle</title>
103 - <body>
104 -<h1>$badtitle</h1>
105 -<p>$badtitletext</p>
106 -</body></html>
107 -";
10886 }
10987
11088 wfProfileOut( 'thumb.php-render' );
@@ -112,17 +90,6 @@
11391
11492 //--------------------------------------------------------------------------
11593
116 -function thumbGetHandler( $fileName ) {
117 - // Determine type
118 - $magic = MimeMagic::singleton();
119 - $extPos = strrpos( $fileName, '.' );
120 - if ( $extPos === false ) {
121 - return false;
122 - }
123 - $mime = $magic->guessTypesForExtension( substr( $fileName, $extPos + 1 ) );
124 - return MediaHandler::getHandler( $mime );
125 -}
126 -
12794 function thumbInternalError( $msg ) {
12895 header( 'Cache-Control: no-cache' );
12996 header( 'Content-Type: text/html; charset=utf-8' );
Index: branches/liquidthreads/languages/messages/MessagesFiu_vro.php
@@ -390,7 +390,6 @@
391391 'yournick' => 'Suq kutsmisnimi (alakirotamisõs)',
392392 'badsig' => 'Seo alakirotus olõ-i masva.',
393393 'email' => 'e-posti aadrõs',
394 -'prefs-help-email-enotif' => 'Taad aadrõsi pääle saadõtasõq sullõ ka artiklidõ muutumisteedüseq, ku sa sääntseq säädmiseq käüki võtat.',
395394 'prefs-help-realname' => "* <strong>Peris nimi</strong> (piä-i kirotama): ku taa teedäq annat, sis pruugitas taad pruukjanime asõmõl lehekülgi tegijide nimekir'on.",
396395 'loginerror' => 'Sisseminemise viga',
397396 'prefs-help-email' => '* <strong>E-post</strong> (piä-i kirotama): tõõsõq pruukjaq saavaq sullõ kirotaq ilma su aadrõssit nägemäldäq. Taast om sis kah kassu, ku uma salasõna ärq johtut unõhtama.',
@@ -481,6 +480,19 @@
482481 Panõq tähele, et sa saa-i taalõ pruukjalõ sõnomit saataq, ku sa olõ-i kirjä pandnuq umma [[Special:Preferences|säädmislehe]] e-posti aadrõssit.
483482
484483 Suq puutri võrgoaadrõs om $3 ja kinnipandmistunnus om #$5. Panõq naaq kõiki perräküsümiisi manoq, midä tiit.",
 484+'autoblockedtext' => "Su puutri võrgoaadrõs peeti automaatsõhe kinniq, selle et taad om tarvitanuq kiäki pruukja, kink om kinniq pidänüq $1.
 485+Kinniqpidämise põhjus:
 486+
 487+:''$2''
 488+
 489+Kinniqpidämise aig: $6
 490+
 491+Taa kinniqpidämise kotsilõ perräküsümises ja taa arotamisõs võit kirotaq kõrraldajalõ $1 vai mõnõlõ
 492+[[{{MediaWiki:grouppage-sysop}}|tõõsõlõ kõrraldajalõ]].
 493+
 494+Rehkendäq tuud, et sa saa-i tõisilõ pruukjilõ e-kirjo saataq, ku sa olõ-i ummi [[Special:Preferences|säädmiisihe]] kirjä pandnuq suq hindä masvat e-postiaadrõssit.
 495+
 496+Suq kinniqpidämise tunnusnummõr om $5. Olõq hää, kirodaq taa nummõr egä perräküsümise mano, miä sa tiit.",
485497 'blockedoriginalsource' => "Lehe '''$1''' lättekuud:",
486498 'blockededitsource' => "Su tett toimõndus lehe '''$1''' pääl:",
487499 'whitelistedittitle' => 'Toimõndamisõs piät nimega sisse minemä',
@@ -744,6 +756,7 @@
745757 'userrights-groupsmember' => 'Kuulus rühmä:',
746758 'userrights-groupsavailable' => 'Või mano pandaq rühmihe:',
747759 'userrights-groupshelp' => 'Valiq rühmäq, minkast sa tahat pruukjat ärq võttaq vai kohe mano pandaq. Valimalda jätetüid rühmi muudõda-i. Rühmä valimist saa tühäs tetäq CTRL + hüä hiirevaotusõga.',
 760+'userrights-reason' => 'Muutmisõ põhjus:',
748761
749762 # Groups
750763 'group' => 'Rühm:',
@@ -1129,14 +1142,17 @@
11301143 'watching' => 'Pandas perräkaemisnimekirjä...',
11311144 'unwatching' => 'Võetas perräkaemisõ alt maaha...',
11321145
1133 -'enotif_mailer' => '{{SITENAME}} lehe muutumisteedüs',
1134 -'enotif_reset' => 'Märgiq kõik leheq ülekaetuis',
1135 -'enotif_newpagetext' => 'Taa om vahtsõnõ leht.',
1136 -'changed' => 'lehte muutnuq',
1137 -'created' => 'lehe loonuq',
1138 -'enotif_subject' => '$PAGEEDITOR om $CHANGEDORCREATED $PAGETITLE',
1139 -'enotif_lastvisited' => 'Lehel $1 ommaq kõik päält suq perämäst käümist tettüq muutmisõq.',
1140 -'enotif_body' => 'Hüä $WATCHINGUSERNAME,
 1146+'enotif_mailer' => '{{SITENAME}} lehe muutumisteedüs',
 1147+'enotif_reset' => 'Märgiq kõik leheq ülekaetuis',
 1148+'enotif_newpagetext' => 'Taa om vahtsõnõ leht.',
 1149+'enotif_impersonal_salutation' => '{{SITENAME}} pruukja',
 1150+'changed' => 'lehte muutnuq',
 1151+'created' => 'lehe loonuq',
 1152+'enotif_subject' => '$PAGEEDITOR om $CHANGEDORCREATED $PAGETITLE',
 1153+'enotif_lastvisited' => 'Lehel $1 ommaq kõik päält suq perämäst käümist tettüq muutmisõq.',
 1154+'enotif_lastdiff' => 'Taa muutusõ nägemises kaeq: $1.',
 1155+'enotif_anon_editor' => 'nimeldä pruukja $1',
 1156+'enotif_body' => 'Hüä $WATCHINGUSERNAME,
11411157
11421158 {{SITENAME}} lehte $PAGETITLE $CHANGEDORCREATED $PAGEEDITDATE $PAGEEDITOR, parhillast kujjo kaeq $PAGETITLE_URL.
11431159
@@ -1219,6 +1235,8 @@
12201236 'restriction-type' => 'Luba',
12211237 'restriction-level' => 'Piirdmisastõq',
12221238 'minimum-size' => 'Kõgõ vähämb maht (baidõn)',
 1239+'maximum-size' => 'Kõgõ suurõmb lubat suurus',
 1240+'pagesize' => '(baiti)',
12231241
12241242 # Restrictions (nouns)
12251243 'restriction-edit' => 'Toimõndus',
@@ -1267,7 +1285,7 @@
12681286 # Contributions
12691287 'contributions' => 'Pruukja kirotusõq',
12701288 'mycontris' => 'Mu kirotusõq',
1271 -'contribsub2' => 'Pruukja "$1 ($2)" kirotusõq',
 1289+'contribsub2' => 'Pruukja "$1 ($2)" kirotusõq',
12721290 'nocontribs' => 'Sääntsit muutmiisi es lövväq.',
12731291 'ucnote' => 'Näüdätäseq taa pruukja tettüid <b>$1</b> viimäst muutmist viimädse <b>$2</b> päävä seen.',
12741292 'uclinks' => 'Näütäq viimäst $1 muutmist; viimädse $2 päävä seen.',
@@ -1306,6 +1324,15 @@
13071325 'ipadressorusername' => 'Puutri võrgoaadrõs vai pruukjanimi',
13081326 'ipbexpiry' => 'Tähtaig',
13091327 'ipbreason' => 'Põhjus',
 1328+'ipbreasonotherlist' => 'Muu põhjus',
 1329+'ipbreason-dropdown' => "*Hariliguq kinniqpidämise põhjusõq
 1330+** Võlss teedüse kirotaminõ
 1331+** Lehti sisu ärqkistutaminõ
 1332+** Reklaamilinkõ pandminõ
 1333+** Mõttõlda jutu vai prahi pandminõ
 1334+** Segämine ja ts'urkminõ
 1335+** Mitmõ pruukjanime võlsspruukminõ
 1336+** Sündümäldäq pruukjanimi",
13101337 'ipbanononly' => 'Piäq kinniq õnnõ ilma nimeldä pruukjaq',
13111338 'ipbcreateaccount' => 'Lasku-i pruukjanimme luvvaq',
13121339 'ipbenableautoblock' => 'Piäq kinniq viimäne puutri võrgoaadrõs, kost pruukja om toimõnduisi tennüq, ja edespiten aadrõsiq, kost tä viil pruuv toimõnduisi tetäq.',
@@ -1313,11 +1340,13 @@
13141341 'ipbother' => 'Muu tähtaig',
13151342 'ipboptions' => '15 minotit:15 minutes,1 päiv:1 day,3 päivä:3 days,1 nätäl:1 week,2 nädälit:2 weeks,1 kuu:1 month,3 kuud:3 months,6 kuud:6 months,1 aastak:1 year,igävene:infinite',
13161343 'ipbotheroption' => 'Muu tähtaig',
 1344+'ipbotherreason' => 'Muu põhjus',
13171345 'ipbhidename' => 'Käkiq pruukjanimi vai puutri võrgoaadrõs ärq kinniqpidämis-, toimõndus-, ja pruukjanimekiräst',
13181346 'badipaddress' => 'Puutri võrgoaadrõs om võlssi kirotõt.',
13191347 'blockipsuccesssub' => 'Kinniqpidämine läts kõrda',
13201348 'blockipsuccesstext' => 'Puutri võrgoaadrõs "$1" om kinniq peet.
13211349 <br />Kõik parhilladsõq kinniqpidämiseq lövvät [[Special:Ipblocklist|kinniqpidämiisi nimekiräst]].',
 1350+'ipb-edit-dropdown' => 'Toimõndaq kinniqpidämise põhjuisi',
13221351 'ipb-unblock-addr' => 'Lõpõdaq pruukja $1 kinniqpidämine ärq',
13231352 'ipb-unblock' => 'Lõpõdaq pruukja vai puutri võrgoaadrõasi kinniqpidämine ärq',
13241353 'ipb-blocklist-addr' => 'Näütäq pruukja $1 kinniqpidämiisi',
@@ -1326,6 +1355,7 @@
13271356 'unblockiptext' => 'Täüdäq ärq taa vorm, et lõpõtaq ärq pruukja vai puutri võrgoaadrõsi kinniqpidämine',
13281357 'ipusubmit' => 'Lõpõdaq kinniqpidämine ärq',
13291358 'unblocked' => 'Pruukja [[User:$1|$1]] kinniqpidämine om ärq lõpõtõt',
 1359+'unblocked-id' => '$1 kinniqpidämine võeti maaha',
13301360 'ipblocklist' => 'Kinniqpeetüisi IP-aadrõssidõ nimekiri',
13311361 'ipblocklist-submit' => 'Otsiq',
13321362 'blocklistline' => '$1 — $2 om kinniq pidänüq pruukja $3 ($4)',
@@ -1334,7 +1364,8 @@
13351365 'anononlyblock' => 'õnnõ nimeldä pruukjaq',
13361366 'noautoblockblock' => 'automaatsõ kinniqpidämiseldä',
13371367 'createaccountblock' => 'pruukjanime luuminõ kinniq pant',
1338 -'ipblocklistempty' => 'Kinniqpidämiisi nimekiri om tühi (vai olõ-i otsitut aadrõssit vai pruukjanimme kinniq peet).',
 1368+'ipblocklist-empty' => 'Kinniqpidämiisi nimekiri om tühi.',
 1369+'ipblocklist-no-results' => 'Taa puutri võrgoaadrõss vai pruukjanimi olõ-i kinniq peet.',
13391370 'blocklink' => 'piäq kinniq',
13401371 'unblocklink' => 'võtaq kinniqpidämine maaha',
13411372 'contribslink' => 'kirotusõq',
@@ -1517,6 +1548,8 @@
15181549 'tooltip-t-emailuser' => 'Saadaq taalõ pruukjalõ e-kiri',
15191550 'tooltip-t-upload' => 'Panõq mano pilte vai meediäteedüstüid',
15201551 'tooltip-t-specialpages' => 'Näütäq tallituslehekülgi',
 1552+'tooltip-t-print' => 'Taa lehe trükükujo',
 1553+'tooltip-t-permalink' => 'Seo lehekujo püsülink',
15211554 'tooltip-ca-nstab-main' => 'Näütäq sisulehekülge',
15221555 'tooltip-ca-nstab-user' => 'Näütäq pruukjalehekülge',
15231556 'tooltip-ca-nstab-media' => 'Näütäq meediälehekülge',
@@ -2018,6 +2051,10 @@
20192052 'livepreview-error' => 'Ütistämine lää-s kõrda: $1 "$2"
20202053 Prooviq harilikku kaehust.',
20212054
 2055+# Friendlier slave lag warnings
 2056+'lag-warn-normal' => 'Muutmiisi, miä ommaq vahtsõmbaq ku $1 sekundit, pruugi-i taan nimekirän nätäq ollaq.',
 2057+'lag-warn-high' => 'Teedüskogoserveri aiglusõ peräst pruugi-i $1 sekundist värskimbit muutmiisi nimekirän nätäq ollaq.',
 2058+
20222059 );
20232060
20242061 ?>
Index: branches/liquidthreads/languages/messages/MessagesKk_cn.php
@@ -174,7 +174,7 @@
175175 'localmonth' => array( 1, 'جەرگٸلٸكتٸاي', 'LOCALMONTH' ),
176176 'localmonthname' => array( 1, 'جەرگٸلٸكتٸاياتاۋى', 'LOCALMONTHNAME' ),
177177 'localmonthnamegen' => array( 1, 'جەرگٸلٸكتٸايٸلٸكاتاۋى', 'LOCALMONTHNAMEGEN' ),
178 - 'localmonthabbrev' => array( 1, 'جەرگٸلٸكتٸايجيىر', 'جەرگٸلٸكتٸايقىسقا', 'LOCALMONTHABBREV' ),
 178+ 'localmonthabbrev' => array( 1, 'جەرگٸلٸكتٸايجيىر', 'جەرگٸلٸكتٸايقىسقاشا', 'جەرگٸلٸكتٸايقىسقا', 'LOCALMONTHABBREV' ),
179179 'localday' => array( 1, 'جەرگٸلٸكتٸكٷن', 'LOCALDAY' ),
180180 'localday2' => array( 1, 'جەرگٸلٸكتٸكٷن2', 'LOCALDAY2' ),
181181 'localdayname' => array( 1, 'جەرگٸلٸكتٸكٷناتاۋى', 'LOCALDAYNAME' ),
@@ -196,8 +196,8 @@
197197 'subjectspacee' => array( 1, 'تاقىرىپبەتٸ2', 'ماقالابەتٸ2', 'SUBJECTSPACEE', 'ARTICLESPACEE' ),
198198 'fullpagename' => array( 1, 'تولىقبەتاتاۋى', 'FULLPAGENAME' ),
199199 'fullpagenamee' => array( 1, 'تولىقبەتاتاۋى2', 'FULLPAGENAMEE' ),
200 - 'subpagename' => array( 1, 'استىڭعىبەتاتاۋى', 'SUBPAGENAME' ),
201 - 'subpagenamee' => array( 1, 'استىڭعىبەتاتاۋى2', 'SUBPAGENAMEE' ),
 200+ 'subpagename' => array( 1, 'بەتشەاتاۋى', 'استىڭعىبەتاتاۋى', 'SUBPAGENAME' ),
 201+ 'subpagenamee' => array( 1, 'بەتشەاتاۋى2', 'استىڭعىبەتاتاۋى2', 'SUBPAGENAMEE' ),
202202 'basepagename' => array( 1, 'نەگٸزگٸبەتاتاۋى', 'BASEPAGENAME' ),
203203 'basepagenamee' => array( 1, 'نەگٸزگٸبەتاتاۋى2', 'BASEPAGENAMEE' ),
204204 'talkpagename' => array( 1, 'تالقىلاۋبەتاتاۋى', 'TALKPAGENAME' ),
@@ -215,6 +215,7 @@
216216 'img_width' => array( 1, '$1 px', '$1px' ),
217217 'img_center' => array( 1, 'ورتاعا', 'ورتا', 'center', 'centre' ),
218218 'img_framed' => array( 1, 'سٷرمەلٸ', 'framed', 'enframed', 'frame' ),
 219+ 'img_frameless' => array( 1, 'سٷرمەسٸز', 'frameless' ),
219220 'img_page' => array( 1, 'بەت=$1', 'بەت $1', 'page=$1', 'page $1' ),
220221 'img_upright' => array( 1, 'تٸكتٸ', 'تٸكتٸك=$1', 'تٸكتٸك $1' ),
221222 'img_border' => array( 1, 'شەكتٸ' ),
@@ -228,18 +229,18 @@
229230 'img_text-bottom' => array( 1, 'مٵتٸن-استىندا', 'text-bottom' ),
230231 'int' => array( 0, 'ٸشكٸ:', 'INT:' ),
231232 'sitename' => array( 1, 'توراپاتاۋى', 'SITENAME' ),
232 - 'ns' => array( 0, 'ەا:', 'NS:' ),
 233+ 'ns' => array( 0, 'ەا:', 'ەسٸمايا:', 'NS:' ),
233234 'localurl' => array( 0, 'جەرگٸلٸكتٸجاي:', 'LOCALURL:' ),
234235 'localurle' => array( 0, 'جەرگٸلٸكتٸجاي2:', 'LOCALURLE:' ),
235236 'server' => array( 0, 'سەرۆەر', 'SERVER' ),
236237 'servername' => array( 0, 'سەرۆەراتاۋى', 'SERVERNAME' ),
237238 'scriptpath' => array( 0, 'ٵمٸرجولى', 'SCRIPTPATH' ),
238 - 'grammar' => array( 0, 'سەپتٸك:', 'GRAMMAR:' ),
 239+ 'grammar' => array( 0, 'سەپتٸگٸ:', 'سەپتٸك:', 'GRAMMAR:' ),
239240 'notitleconvert' => array( 0, '__اتاۋالماستىرعىزباۋ__', '__ااباۋ__', '__NOTITLECONVERT__', '__NOTC__' ),
240241 'nocontentconvert' => array( 0, '__ماعلۇماتالماستىرعىزباۋ__', '__ماباۋ__', '__NOCONTENTCONVERT__', '__NOCC__' ),
241 - 'currentweek' => array( 1, 'اعىمداعىاپتا', 'CURRENTWEEK' ),
 242+ 'currentweek' => array( 1, 'اعىمداعىاپتاسى', 'اعىمداعىاپتا', 'CURRENTWEEK' ),
242243 'currentdow' => array( 1, 'اعىمداعىاپتاكٷنٸ', 'CURRENTDOW' ),
243 - 'localweek' => array( 1, 'جەرگٸلٸكتٸاپتا', 'LOCALWEEK' ),
 244+ 'localweek' => array( 1, 'جەرگٸلٸكتٸاپتاسى', 'جەرگٸلٸكتٸاپتا', 'LOCALWEEK' ),
244245 'localdow' => array( 1, 'جەرگٸلٸكتٸاپتاكٷنٸ', 'LOCALDOW' ),
245246 'revisionid' => array( 1, 'نۇسقانٶمٸرٸ', 'REVISIONID' ),
246247 'revisionday' => array( 1, 'نۇسقاكٷنٸ' , 'REVISIONDAY' ),
@@ -247,13 +248,13 @@
248249 'revisionmonth' => array( 1, 'نۇسقاايى', 'REVISIONMONTH' ),
249250 'revisionyear' => array( 1, 'نۇسقاجىلى', 'REVISIONYEAR' ),
250251 'revisiontimestamp' => array( 1, 'نۇسقاۋاقىتتٷيٸندەمەسٸ', 'REVISIONTIMESTAMP' ),
251 - 'plural' => array( 0, 'كٶپشە:', 'PLURAL:' ),
252 - 'fullurl' => array( 0, 'تولىقجاي:', 'FULLURL:' ),
253 - 'fullurle' => array( 0, 'تولىقجاي2:', 'FULLURLE:' ),
254 - 'lcfirst' => array( 0, 'كٵ1:', 'LCFIRST:' ),
255 - 'ucfirst' => array( 0, 'بٵ1:', 'UCFIRST:' ),
256 - 'lc' => array( 0, 'كٵ:', 'LC:' ),
257 - 'uc' => array( 0, 'بٵ:', 'UC:' ),
 252+ 'plural' => array( 0, 'كٶپشەتٷرٸ:','كٶپشە:', 'PLURAL:' ),
 253+ 'fullurl' => array( 0, 'تولىقجايى:', 'تولىقجاي:', 'FULLURL:' ),
 254+ 'fullurle' => array( 0, 'تولىقجايى2:', 'تولىقجاي2:', 'FULLURLE:' ),
 255+ 'lcfirst' => array( 0, 'كٵ1:', 'كٸشٸٵرٸپپەن1:', 'LCFIRST:' ),
 256+ 'ucfirst' => array( 0, 'بٵ1:', 'باسٵرٸپپەن1:', 'UCFIRST:' ),
 257+ 'lc' => array( 0, 'كٵ:', 'كٸشٸٵرٸپپەن:', 'LC:' ),
 258+ 'uc' => array( 0, 'بٵ:', 'باسٵرٸپپەن:', 'UC:' ),
258259 'raw' => array( 0, 'قام:', 'RAW:' ),
259260 'displaytitle' => array( 1, 'كٶرسەتٸلەتٸناتاۋ', 'DISPLAYTITLE' ),
260261 'rawsuffix' => array( 1, 'ق', 'R' ),
@@ -269,8 +270,8 @@
270271 'pagesinnamespace' => array( 1, 'ەسٸمايابەتسانى:', 'ەابەتسانى:', 'ايابەتسانى:', 'PAGESINNAMESPACE:', 'PAGESINNS:' ),
271272 'numberofadmins' => array( 1, 'ٵكٸمشٸسانى', 'NUMBEROFADMINS' ),
272273 'formatnum' => array( 0, 'سانپٸشٸمٸ', 'FORMATNUM' ),
273 - 'padleft' => array( 0, 'سولىعىس', 'PADLEFT' ),
274 - 'padright' => array( 0, 'وڭىعىس', 'PADRIGHT' ),
 274+ 'padleft' => array( 0, 'سولعاىعىس', 'سولىعىس', 'PADLEFT' ),
 275+ 'padright' => array( 0, 'وڭعاىعىس', 'وڭىعىس', 'PADRIGHT' ),
275276 'special' => array( 0, 'ارنايى', 'special', ),
276277 'defaultsort' => array( 1, 'ٵدەپكٸسۇرىپتاۋ:', 'ٵدەپكٸسۇرىپ:', 'DEFAULTSORT:' ),
277278 );
@@ -355,7 +356,7 @@
356357 $messages = array(
357358 # User preference toggles
358359 'tog-underline' => 'سٸلتەمەنٸ استىنان سىز:',
359 -'tog-highlightbroken' => 'جوقتالعان سٸلتەمەلەردٸ <a href="" class="new">بىلاي</a> پٸشٸمدە (باسقاشا: بىلاي <a href="" class="internal">؟</a> سيياقتى).',
 360+'tog-highlightbroken' => 'جارامسىز سٸلتەمەلەردٸ <a href="" class="new">بىلاي</a> پٸشٸمدە (بالاماسى: بىلاي <a href="" class="internal">؟</a> سيياقتى).',
360361 'tog-justify' => 'ەجەلەردٸ ەنٸ بويىنشا تۋرالاۋ',
361362 'tog-hideminor' => 'جۋىقتاعى ٶزگەرٸستەردە شاعىن تٷزەتۋدٸ جاسىر',
362363 'tog-extendwatchlist' => 'باقىلاۋ تٸزٸمدٸ ۇلعايت (بارلىق جارامدى ٶزگەرٸستەردٸ كٶرسەت)',
@@ -456,7 +457,7 @@
457458 'categories' => 'بارلىق سانات تٸزٸمٸ',
458459 'pagecategories' => '{{PLURAL:$1|سانات|ساناتتار}}',
459460 'category_header' => '«$1» ساناتىنداعى بەتتەر',
460 -'subcategories' => 'تٶمەنگٸ ساناتتار',
 461+'subcategories' => 'ساناتشالار',
461462 'category-media-header' => '«$1» ساناتىنداعى تاسپالار',
462463
463464 'linkprefix' => '/^(.*?)([a-zäçéğıïñöşüýа-яёәіңғүұқөһA-ZÄÇÉĞİÏÑÖŞÜÝА-ЯЁӘІҢҒҮҰҚӨҺʺʹ«„]+)$/sDu',
@@ -895,7 +896,7 @@
896897 'nonunicodebrowser' => '<strong>اڭعارتپا: شولعىشىڭىز Unicode بەلگٸلەۋٸنە ٷيلەسٸمدٸ ەمەس, سوندىقتان لاتىن ەمەس ٵرٸپتەرٸ بار بەتتەردٸ ٶڭدەۋ زٸل بولۋ مٷمكٸن. جۇمىس ٸستەۋگە ىقتيمالدىق بەرۋ ٷشٸن, تٶمەنگٸ ٶڭدەۋ اۋماعىندا ASCII ەمەس ٵرٸپتەر ونالتىلىق سانىمەن كٶرسەتٸلەدٸ</strong>.',
897898 'editingold' => '<strong>اڭعارتپا: وسى بەتتٸڭ ەرتەرەك نۇسقاسىن
898899 ٶڭدەپ جاتىرسىز.
899 -بۇنى ساقتاساڭىز, وسى نۋسقادان سوڭعى بارلىق تٷزەتۋلەر جويىلادى.</strong>',
 900+بۇنى ساقتاساڭىز, وسى نۋسقادان سوڭعى بارلىق ٶزگەرٸستەر جويىلادى.</strong>',
900901 'yourdiff' => 'ايىرمالار',
901902 'copyrightwarning' => '{{SITENAME}} جوباسىنا قوسىلعان بٷكٸل ٷلەس $2 (كٶبٸرەك اقپارات ٷشٸن: $1) قۇجاتىنا ساي جٸبەرٸلگەن بولىپ سانالادى. ەگەر جازۋىڭىزدىڭ ەركٸن كٶشٸرٸلٸپ تٷزەتٸلۋٸن قالاماساڭىز, مىندا ۇسىنباۋىڭىز جٶن.<br />
902903 تاعى, قوسقان ٷلەسٸڭٸز - ٶزٸڭٸزدٸڭ جازعانىعىز, نە اشىق اقپارات كٶزدەرٸنەن الىنعان ماعلۇمات بولعانىن ۋٵدە ەتەسٸز.<br />
@@ -1076,7 +1077,7 @@
10771078 'qbsettings-fixedright' => 'وڭعا بەكٸتٸلگەن',
10781079 'qbsettings-floatingleft' => 'سولعا قالقىعان',
10791080 'qbsettings-floatingright' => 'وڭعا قالقىعان',
1080 -'changepassword' => 'قۇپييا سٶز ٶزگەرتۋ',
 1081+'changepassword' => 'قۇپييا سٶزدٸ اۋىستىرۋ',
10811082 'skin' => 'بەزەندٸرۋ',
10821083 'math' => 'ماتەماتيكا',
10831084 'dateformat' => 'كٷن-اي پٸشٸمٸ',
@@ -1158,7 +1159,7 @@
11591160 'rightsnone' => '(ەشقانداي)',
11601161
11611162 # Recent changes
1162 -'nchanges' => '{{PLURAL:$1|بٸر تٷزەتۋ|$1 تٷزەتۋ}}',
 1163+'nchanges' => '{{PLURAL:$1|بٸر ٶزگەرٸس|$1 ٶزگەرٸس}}',
11631164 'recentchanges' => 'جۋىقتاعى ٶزگەرٸستەر',
11641165 'recentchangestext' => 'بۇل بەتتە وسى ۋيكيدەگٸ بولعان جۋىقتاعى ٶزگەرٸستەر بايقالادى.',
11651166 'recentchanges-feed-description' => 'بۇل ارنامەنەن ۋيكيدەگٸ ەڭ سوڭعى ٶزگەرٸستەر قاداعالانادى.',
@@ -1185,9 +1186,9 @@
11861187 'rc_categories_any' => 'قايسىبٸر',
11871188
11881189 # Recent changes linked
1189 -'recentchangeslinked' => 'قاتىستى تٷزەتۋلەر',
 1190+'recentchangeslinked' => 'قاتىستى ٶزگەرٸستەر',
11901191 'recentchangeslinked-noresult' => 'سٸلتەگەن بەتتەردە ايتىلمىش مەرزٸمدە ەشقانداي ٶزگەرٸس بولماعان.',
1191 -'recentchangeslinked-summary' => "بۇل ارنايى بەتتە سٸلتەگەن بەتتەردەگٸ جۋىقتاعى ٶزگەرٸستەر تٸزٸمٸ بەرٸلەدٸ. باقىلاۋ تٸزٸمٸڭٸزدەگٸ بەتتەر '''جۋان''' ٵرپٸمەن بەلگٸلەنەدٸ.",
 1192+'recentchangeslinked-summary' => "بۇل ارنايى بەتتە سٸلتەگەن بەتتەردەگٸ جۋىقتاعى ٶزگەرٸستەر تٸزٸمٸ بەرٸلەدٸ. باقىلاۋ تٸزٸمٸڭٸزدەگٸ بەتتەر '''جۋان''' ٵربٸمەن بەلگٸلەنەدٸ.",
11921193
11931194 # Upload
11941195 'upload' => 'فايل قوتارۋ',
@@ -1225,8 +1226,8 @@
12261227 'filetype-missing' => 'بۇل فايلدىڭ («.jpg» سيياقتى) كەڭەيتٸمٸ جوق.',
12271228 'large-file' => 'فايلدى $1 مٶلشەردەن اسپاۋىنا تىرىسىڭىز; بۇل فايل مٶلشەرٸ — $2.',
12281229 'largefileserver' => 'وسى فايلدىڭ مٶلشەرٸ سەرۆەردٸڭ قالاۋىنان اسىپ كەتكەن.',
1229 -'emptyfile' => 'قوتارىلعان فايلىڭىز بوس سيياقتى. بۇل فايل اتاۋى جانساق ەنگٸزٸلگەنٸنەن بولۋى مٷمكٸن. قوتارعىڭىز كەلگەن فايل شىنىندا دا وسى فايل بولعانىن تەكسەرٸپ الىڭىز.',
1230 -'fileexists' => 'وسىنداي اتاۋلى فايل بار تٷگە. قايتا جازۋدىڭ الدىنان $1 تەكسەرٸپ شىعىڭىز.',
 1230+'emptyfile' => 'قوتارىلعان فايلىڭىز بوس سيياقتى. بۇل فايل اتاۋىندا قاتە بولۋى مٷمكٸن. وسى فايلدى شىنايى قوتارعىڭىز كەلەتٸن تەكسەرٸپ شىعىڭىز.',
 1231+'fileexists' => 'وسىنداي اتاۋلى فايل بار تٷگە, ەگەر بۇنى ٶزگەرتۋگە سەنٸمٸڭٸز جوق بولسا <strong><tt>$1</tt></strong> دەگەندٸ تەكسەرٸپ شىعىڭىز.',
12311232 'fileexists-extension' => 'ۇقساستى فايل اتاۋى بار تٷگە:<br />
12321233 قوتارىلاتىن فايل اتاۋى: <strong><tt>$1</tt></strong><br />
12331234 بار بولعان فايل اتاۋى: <strong><tt>$2</tt></strong><br />
@@ -1313,7 +1314,7 @@
13141315
13151316 # MIME search
13161317 'mimesearch' => 'فايلدى MIME تٷرٸمەن ٸزدەۋ',
1317 -'mimesearch-summary' => 'بۇل بەت فايلداردى MIME تٷرٸمەن سٷزگٸلەۋ مٷمكٸندٸگٸن بەرەدٸ. كٸرٸسٸ: «ماعلۇمات تٷرٸ»/«تاراۋ تٷرٸ», مىسالى <tt>image/jpeg</tt>.',
 1318+'mimesearch-summary' => 'بۇل بەت فايلداردى MIME تٷرٸمەن سٷزگٸلەۋ مٷمكٸندٸگٸن بەرەدٸ. كٸرٸسٸ: «ماعلۇمات تٷرٸ»/«تٷر تاراۋى», مىسالى <tt>image/jpeg</tt>.',
13181319 'mimetype' => 'MIME تٷرٸ:',
13191320 'download' => 'جٷكتەۋ',
13201321
@@ -1351,7 +1352,7 @@
13521353
13531354 اعىمدىق [http://meta.wikimedia.org/wiki/Help:Job_queue تاپسىرىم كەزەگٸ] ۇزىندىلىعى: '''$7'''.",
13541355 'userstatstext' => "مىندا {{PLURAL:$1|'''1''' تٸركەلگەن قاتىسۋشى|'''$1''' تٸركەلگەن قاتىسۋشى}} بار, سونىڭ ٸشٸندە
1355 - {{PLURAL:$2|'''1''' قاتىسۋشىدا|'''$2''' قاتىسۋشىدا}} (نەمەسە '''$4 ٪''') $5 قۇقىقتارى بار",
 1356+ {{PLURAL:$2|'''1''' قاتىسۋشىدا|'''$2''' قاتىسۋشىدا}} (نەمەسە '''$4 %''') $5 قۇقىقتارى بار",
13561357 'statistics-mostpopular' => 'ەڭ كٶپ قارالعان بەتتەر',
13571358
13581359 'disambiguations' => 'ايرىقتى بەتتەر',
@@ -1527,7 +1528,7 @@
15281529 * [[{{ns:special}}:Watchlist/edit|بٷكٸل تٸزٸمدٸ قاراۋ جٵنە ٶزگەرتۋ]].
15291530 * [[{{ns:special}}:Watchlist/clear|تٸزٸمدەگٸ بارلىق دانا الاستاتۋ]].",
15301531 'wlheader-enotif' => '* ەسكەرتۋ حات جٸبەرۋٸ ەندٸرٸلگەن.',
1531 -'wlheader-showupdated' => "* سوڭعى كٸرگەنٸمنەن بەرٸ تٷزەتٸلگەن بەتتەردٸ '''جۋان''' مٵتٸنمەن كٶرسەت",
 1532+'wlheader-showupdated' => "* سوڭعى كٸرگەنٸمنەن بەرٸ ٶزگەرتٸلگەن بەتتەردٸ '''جۋان''' ٵربٸمەن كٶرسەت",
15321533 'watchmethod-recent' => 'باقىلاۋلى بەتتەردٸڭ جۋىقتاعى ٶزگەرٸستەرٸن تەكسەرۋ',
15331534 'watchmethod-list' => 'جۋىقتاعى ٶزگەرٸستەردە باقىلاۋلى بەتتەردٸ تەكسەرۋ',
15341535 'removechecked' => 'بەلگٸلەنگەندٸ باقىلاۋ تٸزٸمٸنەن الاستاتۋ',
@@ -1628,7 +1629,7 @@
16291630 «ارتقا» تٷيمەسٸن باسىڭىز, جٵنە بەتتٸ كەرٸ جٷكتەڭٸز, سوسىن قايتالاپ كٶرٸڭٸز.',
16301631 'protectlogpage' => 'قورعاۋ_جۋرنالى',
16311632 'protectlogtext' => 'تٶمەندە بەتتەردٸڭ قورعاۋ/قورعاماۋ تٸزٸمٸ بەرٸلگەن. اعىمداعى قورعاۋ ٵرەكتتەر بار بەتتەر ٷشٸن [[{{ns:special}}:Protectedpages|قورعالعان بەت تٸزٸمٸن]] قاراڭىز.',
1632 -'protectedarticle' => '«$1» قورعالدى',
 1633+'protectedarticle' => '«[[$1]]» قورعالدى',
16331634 'unprotectedarticle' => '«[[$1]]» قورعالمادى',
16341635 'protectsub' => '(«$1» قورعاۋدا)',
16351636 'confirmprotecttext' => 'وسى بەتتٸ راسىندا دا قورعاۋ قاجەت پە؟',
@@ -1658,7 +1659,7 @@
16591660 'protect-expiring' => 'بٸتۋٸ: $1 (UTC)',
16601661 'protect-cascade' => 'باۋلى قورعاۋ — بۇل بەتكە كٸرٸستٸرٸلگەن ٵرقايسى بەتتەردٸ قورعاۋ.',
16611662 'restriction-type' => 'رۇقساتى:',
1662 -'restriction-level' => 'رۇقسات دەڭگەيٸ:',
 1663+'restriction-level' => 'رۇقسات شەكتەۋ دەڭگەيٸ:',
16631664 'minimum-size' => 'ەڭ از مٶلشەرٸ',
16641665 'maximum-size' => 'ەڭ كٶپ مٶلشەرٸ',
16651666 'pagesize' => '(بايت)',
@@ -1702,7 +1703,7 @@
17031704 'undeletedarticle' => '«[[$1]]» قايتاردى',
17041705 'undeletedrevisions' => '{{PLURAL:$1|نۇسقانى|$1 نۇسقانى}} قايتاردى',
17051706 'undeletedrevisions-files' => '{{PLURAL:$1|نۇسقانى|$1 نۇسقانى}} جٵنە {{PLURAL:$2|فايلدى|$2 فايلدى}} قايتاردى',
1706 -'undeletedfiles' => '{{PLURAL:$1|1 فايل|$1 فايل}} قايتاردى',
 1707+'undeletedfiles' => '{{PLURAL:$1|1 فايلدى|$1 فايلدى}} قايتاردى',
17071708 'cannotundelete' => 'قايتارۋ سٵتسٸز بٸتتٸ; تاعى بٸرەۋ سٸزدەن بۇرىن سول بەتتٸ قايتارعان بولار.',
17081709 'undeletedpage' => "<big>'''$1 قايتارىلدى'''</big>
17091710
@@ -1722,8 +1723,8 @@
17231724 'mycontris' => 'ٷلەسٸم',
17241725 'contribsub2' => '$1 ($2) ٷلەسٸ',
17251726 'nocontribs' => 'وسى ٸزدەۋ شارتىنا سٵيكەس ٶزگەرٸستەر تابىلعان جوق.',
1726 -'ucnote' => 'تٶمەندە وسى قاتىسۋشىنىڭ سوڭعى <b>$2</b> كٷندەگٸ, سوڭعى <b>$1</b> ٶزگەرٸسٸ كٶرسەتلەدٸ.',
1727 -'uclinks' => 'سوڭعى $2 كٷندەگٸ, سوڭعى $1 ٶزگەرٸسٸن قاراۋ.',
 1727+'ucnote' => 'تٶمەندە وسى قاتىسۋشى جاساعان سوڭعى <b>$2</b> كٷندەگٸ, سوڭعى <b>$1</b> ٶزگەرٸسٸ كٶرسەتلەدٸ.',
 1728+'uclinks' => 'سوڭعى $2 كٷندەگٸ, سوڭعى جاسالعان $1 ٶزگەرٸسٸن قاراۋ.',
17281729 'uctop' => ' (ٷستٸ)',
17291730
17301731 'sp-contributions-newest' => 'ەڭ جاڭاسىنا',
@@ -1753,6 +1754,7 @@
17541755 'istemplate' => 'كٸرٸكتٸرۋ',
17551756 'whatlinkshere-prev' => '{{PLURAL:$1|الدىڭعى|الدىڭعى $1}}',
17561757 'whatlinkshere-next' => '{{PLURAL:$1|كەلەسٸ|كەلەسٸ $1}}',
 1758+'whatlinkshere-links' => '← سٸلتەمەلەر',
17571759
17581760 # Block/unblock
17591761 'blockip' => 'پايدالانۋشىنى بۇعاتتاۋ',
@@ -1873,7 +1875,7 @@
18741876 بارلىق تاريحىن جاڭا اتاۋعا جىلجىتادى.
18751877 بۇرىنعى بەت اتاۋى جاڭا اتاۋعا ايداتاتىن بەت بولادى.
18761878 ەسكٸ اتاۋىنا سٸلتەيتٸن سٸلتەمەلەر ٶزگەرتٸلمەيدٸ; جىلجىتۋدان سوڭ
1877 -شىنجىرلى ايداتۋلار بار-جوعىن تەكسەرٸڭٸز.
 1879+شىنجىرلى نە جارامسىز ايداتۋلار بار-جوعىن تەكسەرٸپ شىعىڭىز.
18781880 سٸلتەمەلەر بۇرىنعى جولداۋىمەن بىلايعى ٶتۋٸن تەكسەرۋٸنە
18791881 سٸز مٸندەتتٸ بولاسىز.
18801882
@@ -1884,7 +1886,7 @@
18851887 بٸراق بار بەتتٸڭ ٷستٸنە جازۋعا بولمايدى.
18861888
18871889 <b>نازار سالىڭىز!</b>
1888 -بۇل دٵرٸپتٸ بەتكە قاتاڭ جٵنە كەنەت ٶزگەرٸس جاساۋعا مٷمكٸن;
 1890+بۇل ٵيگٸلٸ بەتكە قاتاڭ جٵنە كەنەت ٶزگەرٸس جاساۋعا مٷمكٸن;
18891891 ٵرەكەتتٸڭ الدىنان وسىنىڭ زارداپتارىن تٷسٸنگەنٸڭٸزگە باتىل
18901892 بولىڭىز.",
18911893 'movepagetalktext' => "كەلەسٸ سەبەپتەر '''بولعانشا''' دەيٸن, تالقىلاۋ بەتٸ ٶزدٸكتٸك بٸرگە جىلجىتىلادى:
@@ -2049,8 +2051,8 @@
20502052 'tooltip-ca-nstab-help' => 'انىقتىما بەتٸن قاراۋ',
20512053 'tooltip-ca-nstab-category' => 'سانات بەتٸن قاراۋ',
20522054 'tooltip-minoredit' => 'وسىنى شاعىن تٷزەتۋ دەپ بەلگٸلەۋ',
2053 -'tooltip-save' => 'تٷزەتۋٸڭٸزدٸ ساقتاۋ',
2054 -'tooltip-preview' => 'ساقتاۋدىڭ الدىنان تٷزەتۋٸڭٸزدٸ قاراپ شىعىڭىز!',
 2055+'tooltip-save' => 'جاساعان ٶزگەرٸستەرٸڭٸزدٸ ساقتاۋ',
 2056+'tooltip-preview' => 'ساقتاۋدىڭ الدىنان جاساعان ٶزگەرٸستەرٸڭٸزدٸ قاراپ شىعىڭىز!',
20552057 'tooltip-diff' => 'مٵتٸنگە قانداي ٶزگەرٸستەردٸ جاساعانىڭىزدى قاراۋ.',
20562058 'tooltip-compareselectedversions' => 'بەتتٸڭ ەكٸ نۇسقاسىنىڭ ايىرماسىن قاراۋ.',
20572059 'tooltip-watch' => 'بۇل بەتتٸ باقىلاۋ تٸزٸمٸڭٸزگە ٷستەۋ',
@@ -2142,7 +2144,7 @@
21432145 'spamprotectiontitle' => '«سپام»-نان قورعايتىن سٷزگٸ',
21442146 'spamprotectiontext' => 'بۇل بەتتٸڭ ساقتاۋىن «سپام» سٷزگٸسٸ بۇعاتتادى. بۇنىڭ سەبەبٸ سىرتقى توراپ سٸلتەمەسٸنەن بولۋى مٷمكٸن.',
21452147 'spamprotectionmatch' => 'كەلەسٸ «سپام» مٵتٸنٸ سٷزگٸلەنگەن: $1',
2146 -'subcategorycount' => 'بۇل ساناتتا {{PLURAL:$1|بٸر|$1}} تٶمەنگٸ سانات بار.',
 2148+'subcategorycount' => 'بۇل ساناتتا {{PLURAL:$1|بٸر|$1}} ساناتشا بار.',
21472149 'categoryarticlecount' => 'بۇل ساناتتا {{PLURAL:$1|بٸر|$1}} بەت بار.',
21482150 'category-media-count' => 'بۇل ساناتتا {{PLURAL:$1|بٸر|$1}} فايل بار.',
21492151 'listingcontinuesabbrev' => ' (جالع.)',
@@ -2175,7 +2177,7 @@
21762178 'rcpatroldisabledtext' => 'جۋىقتاعى ٶزگەرٸستەر كٷزەتٸ قاسيەتٸ اعىمدا ٶشٸرٸلگەن.',
21772179 'markedaspatrollederror' => 'كٷزەتتە دەپ بەلگٸلەنبەيدٸ',
21782180 'markedaspatrollederrortext' => 'كٷزەتتە دەپ بەلگٸلەۋ ٷشٸن نۇسقاسىن ەنگٸزٸڭٸز.',
2179 -'markedaspatrollederror-noautopatrol' => 'ٶزٸڭٸزدٸڭ ٶزگەرٸستەرٸڭٸزدٸ كٷزەتكە قويا المايسىز.',
 2181+'markedaspatrollederror-noautopatrol' => 'ٶزٸڭٸز جاساعان ٶزگەرٸستەرٸڭٸزدٸ كٷزەتكە قويا المايسىز.',
21802182
21812183 # Patrol log
21822184 'patrol-log-page' => 'كٷزەت جۋرنالى',
@@ -2192,7 +2194,7 @@
21932195
21942196 # Media information
21952197 'mediawarning' => "'''نازار سالىڭىز''': بۇل فايل تٷرٸندە قاسكٷنەمدٸ ٵمٸردٸڭ بار بولۋى ىقتيمال; فايلدى جەگٸپ جٷيەڭٸزگە زييان كەلتٸرۋٸڭٸز مٷمكٸن.<hr />",
2196 -'imagemaxsize' => 'سۋرەت تٷيٸندەمە بەتٸندەگٸ سۋرەتتٸڭ مٶلشەرٸن شەكتەۋٸ:',
 2198+'imagemaxsize' => 'سيپاتتاماسى بەتٸندەگٸ سۋرەتتٸڭ مٶلشەرٸن شەكتەۋٸ:',
21972199 'thumbsize' => 'نوباي مٶلشەرٸ:',
21982200 'widthheight' => '$1 × $2',
21992201 'file-info' => 'فايل مٶلشەرٸ: $1, MIME تٷرٸ: $2',
@@ -2498,7 +2500,7 @@
24992501 # Pseudotags used for GPSSpeedRef and GPSDestDistanceRef
25002502 'exif-gpsspeed-k' => 'km/h',
25012503 'exif-gpsspeed-m' => 'mil/h',
2502 -'exif-gpsspeed-n' => 'ج. تٷيٸن',
 2504+'exif-gpsspeed-n' => 'knot',
25032505
25042506 # Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef
25052507 'exif-gpsdirection-t' => 'شىن باعىت',
Index: branches/liquidthreads/languages/messages/MessagesEn.php
@@ -1958,7 +1958,7 @@
19591959 'istemplate' => 'inclusion',
19601960 'whatlinkshere-prev' => '{{PLURAL:$1|previous|previous $1}}',
19611961 'whatlinkshere-next' => '{{PLURAL:$1|next|next $1}}',
1962 -'whatlinkshere-links' => '(← links)',
 1962+'whatlinkshere-links' => '← links',
19631963
19641964 # Block/unblock IP
19651965 #
Index: branches/liquidthreads/languages/messages/MessagesKk_kz.php
@@ -166,7 +166,7 @@
167167 'localmonth' => array( 1, 'ЖЕРГІЛІКТІАЙ', 'LOCALMONTH' ),
168168 'localmonthname' => array( 1, 'ЖЕРГІЛІКТІАЙАТАУЫ', 'LOCALMONTHNAME' ),
169169 'localmonthnamegen' => array( 1, 'ЖЕРГІЛІКТІАЙІЛІКАТАУЫ', 'LOCALMONTHNAMEGEN' ),
170 - 'localmonthabbrev' => array( 1, 'ЖЕРГІЛІКТІАЙЖИЫР', 'ЖЕРГІЛІКТІАЙҚЫСҚА', 'LOCALMONTHABBREV' ),
 170+ 'localmonthabbrev' => array( 1, 'ЖЕРГІЛІКТІАЙЖИЫР', 'ЖЕРГІЛІКТІАЙҚЫСҚАША', 'ЖЕРГІЛІКТІАЙҚЫСҚА', 'LOCALMONTHABBREV' ),
171171 'localday' => array( 1, 'ЖЕРГІЛІКТІКҮН', 'LOCALDAY' ),
172172 'localday2' => array( 1, 'ЖЕРГІЛІКТІКҮН2', 'LOCALDAY2' ),
173173 'localdayname' => array( 1, 'ЖЕРГІЛІКТІКҮНАТАУЫ', 'LOCALDAYNAME' ),
@@ -188,8 +188,8 @@
189189 'subjectspacee' => array( 1, 'ТАҚЫРЫПБЕТІ2', 'МАҚАЛАБЕТІ2', 'SUBJECTSPACEE', 'ARTICLESPACEE' ),
190190 'fullpagename' => array( 1, 'ТОЛЫҚБЕТАТАУЫ', 'FULLPAGENAME' ),
191191 'fullpagenamee' => array( 1, 'ТОЛЫҚБЕТАТАУЫ2', 'FULLPAGENAMEE' ),
192 - 'subpagename' => array( 1, 'АСТЫҢҒЫБЕТАТАУЫ', 'SUBPAGENAME' ),
193 - 'subpagenamee' => array( 1, 'АСТЫҢҒЫБЕТАТАУЫ2', 'SUBPAGENAMEE' ),
 192+ 'subpagename' => array( 1, 'БЕТШЕАТАУЫ', 'АСТЫҢҒЫБЕТАТАУЫ', 'SUBPAGENAME' ),
 193+ 'subpagenamee' => array( 1, 'БЕТШЕАТАУЫ2', 'АСТЫҢҒЫБЕТАТАУЫ2', 'SUBPAGENAMEE' ),
194194 'basepagename' => array( 1, 'НЕГІЗГІБЕТАТАУЫ', 'BASEPAGENAME' ),
195195 'basepagenamee' => array( 1, 'НЕГІЗГІБЕТАТАУЫ2', 'BASEPAGENAMEE' ),
196196 'talkpagename' => array( 1, 'ТАЛҚЫЛАУБЕТАТАУЫ', 'TALKPAGENAME' ),
@@ -207,9 +207,10 @@
208208 'img_width' => array( 1, '$1 px', '$1px' ),
209209 'img_center' => array( 1, 'ортаға', 'орта', 'center', 'centre' ),
210210 'img_framed' => array( 1, 'сүрмелі', 'framed', 'enframed', 'frame' ),
 211+ 'img_frameless' => array( 1, 'сүрмесіз', 'frameless' ),
211212 'img_page' => array( 1, 'бет=$1', 'бет $1', 'page=$1', 'page $1' ),
212213 'img_upright' => array( 1, 'тікті', 'тіктік=$1', 'тіктік $1' ),
213 - 'img_border' => array( 1, 'шекті' ),
 214+ 'img_border' => array( 1, 'шекті' ),
214215 'img_baseline' => array( 1, 'негізжол', 'baseline' ),
215216 'img_sub' => array( 1, 'астылығы', 'аст', 'sub'),
216217 'img_super' => array( 1, 'үстілігі', 'үст', 'sup', 'super', 'sup' ),
@@ -220,18 +221,18 @@
221222 'img_text-bottom' => array( 1, 'мәтін-астында', 'text-bottom' ),
222223 'int' => array( 0, 'ІШКІ:', 'INT:' ),
223224 'sitename' => array( 1, 'ТОРАПАТАУЫ', 'SITENAME' ),
224 - 'ns' => array( 0, 'ЕА:', 'NS:' ),
 225+ 'ns' => array( 0, 'ЕА:', 'ЕСІМАЯ:', 'NS:' ),
225226 'localurl' => array( 0, 'ЖЕРГІЛІКТІЖАЙ:', 'LOCALURL:' ),
226227 'localurle' => array( 0, 'ЖЕРГІЛІКТІЖАЙ2:', 'LOCALURLE:' ),
227228 'server' => array( 0, 'СЕРВЕР', 'SERVER' ),
228229 'servername' => array( 0, 'СЕРВЕРАТАУЫ', 'SERVERNAME' ),
229230 'scriptpath' => array( 0, 'ӘМІРЖОЛЫ', 'SCRIPTPATH' ),
230 - 'grammar' => array( 0, 'СЕПТІК:', 'GRAMMAR:' ),
 231+ 'grammar' => array( 0, 'СЕПТІГІ:', 'СЕПТІК:', 'GRAMMAR:' ),
231232 'notitleconvert' => array( 0, '__АТАУАЛМАСТЫРҒЫЗБАУ__', '__ААБАУ__', '__NOTITLECONVERT__', '__NOTC__' ),
232233 'nocontentconvert' => array( 0, '__МАҒЛҰМАТАЛМАСТЫРҒЫЗБАУ__', '__МАБАУ__', '__NOCONTENTCONVERT__', '__NOCC__' ),
233 - 'currentweek' => array( 1, 'АҒЫМДАҒЫАПТА', 'CURRENTWEEK' ),
 234+ 'currentweek' => array( 1, 'АҒЫМДАҒЫАПТАСЫ', 'АҒЫМДАҒЫАПТА', 'CURRENTWEEK' ),
234235 'currentdow' => array( 1, 'АҒЫМДАҒЫАПТАКҮНІ', 'CURRENTDOW' ),
235 - 'localweek' => array( 1, 'ЖЕРГІЛІКТІАПТА', 'LOCALWEEK' ),
 236+ 'localweek' => array( 1, 'ЖЕРГІЛІКТІАПТАСЫ', 'ЖЕРГІЛІКТІАПТА', 'LOCALWEEK' ),
236237 'localdow' => array( 1, 'ЖЕРГІЛІКТІАПТАКҮНІ', 'LOCALDOW' ),
237238 'revisionid' => array( 1, 'НҰСҚАНӨМІРІ', 'REVISIONID' ),
238239 'revisionday' => array( 1, 'НҰСҚАКҮНІ' , 'REVISIONDAY' ),
@@ -239,13 +240,13 @@
240241 'revisionmonth' => array( 1, 'НҰСҚААЙЫ', 'REVISIONMONTH' ),
241242 'revisionyear' => array( 1, 'НҰСҚАЖЫЛЫ', 'REVISIONYEAR' ),
242243 'revisiontimestamp' => array( 1, 'НҰСҚАУАҚЫТТҮЙІНДЕМЕСІ', 'REVISIONTIMESTAMP' ),
243 - 'plural' => array( 0, 'КӨПШЕ:', 'PLURAL:' ),
244 - 'fullurl' => array( 0, 'ТОЛЫҚЖАЙ:', 'FULLURL:' ),
245 - 'fullurle' => array( 0, 'ТОЛЫҚЖАЙ2:', 'FULLURLE:' ),
246 - 'lcfirst' => array( 0, 'КӘ1:', 'LCFIRST:' ),
247 - 'ucfirst' => array( 0, 'БӘ1:', 'UCFIRST:' ),
248 - 'lc' => array( 0, 'КӘ:', 'LC:' ),
249 - 'uc' => array( 0, 'БӘ:', 'UC:' ),
 244+ 'plural' => array( 0, 'КӨПШЕТҮРІ:','КӨПШЕ:', 'PLURAL:' ),
 245+ 'fullurl' => array( 0, 'ТОЛЫҚЖАЙЫ:', 'ТОЛЫҚЖАЙ:', 'FULLURL:' ),
 246+ 'fullurle' => array( 0, 'ТОЛЫҚЖАЙЫ2:', 'ТОЛЫҚЖАЙ2:', 'FULLURLE:' ),
 247+ 'lcfirst' => array( 0, 'КӘ1:', 'КІШІӘРІППЕН1:', 'LCFIRST:' ),
 248+ 'ucfirst' => array( 0, 'БӘ1:', 'БАСӘРІППЕН1:', 'UCFIRST:' ),
 249+ 'lc' => array( 0, 'КӘ:', 'КІШІӘРІППЕН:', 'LC:' ),
 250+ 'uc' => array( 0, 'БӘ:', 'БАСӘРІППЕН:', 'UC:' ),
250251 'raw' => array( 0, 'ҚАМ:', 'RAW:' ),
251252 'displaytitle' => array( 1, 'КӨРСЕТІЛЕТІНАТАУ', 'DISPLAYTITLE' ),
252253 'rawsuffix' => array( 1, 'Қ', 'R' ),
@@ -261,9 +262,9 @@
262263 'pagesinnamespace' => array( 1, 'ЕСІМАЯБЕТСАНЫ:', 'ЕАБЕТСАНЫ:', 'АЯБЕТСАНЫ:', 'PAGESINNAMESPACE:', 'PAGESINNS:' ),
263264 'numberofadmins' => array( 1, 'ӘКІМШІСАНЫ', 'NUMBEROFADMINS' ),
264265 'formatnum' => array( 0, 'САНПІШІМІ', 'FORMATNUM' ),
265 - 'padleft' => array( 0, 'СОЛЫҒЫС', 'PADLEFT' ),
266 - 'padright' => array( 0, 'ОҢЫҒЫС', 'PADRIGHT' ),
267 - 'special' => array( 0, 'арнайы', 'special', ),
 266+ 'padleft' => array( 0, 'СОЛҒАЫҒЫС', 'СОЛЫҒЫС', 'PADLEFT' ),
 267+ 'padright' => array( 0, 'ОҢҒАЫҒЫС', 'ОҢЫҒЫС', 'PADRIGHT' ),
 268+ 'special' => array( 0, 'арнайы', 'special', ),
268269 'defaultsort' => array( 1, 'ӘДЕПКІСҰРЫПТАУ:', 'ӘДЕПКІСҰРЫП:', 'DEFAULTSORT:' ),
269270 );
270271
@@ -347,7 +348,7 @@
348349 $messages = array(
349350 # User preference toggles
350351 'tog-underline' => 'Сілтемені астынан сыз:',
351 -'tog-highlightbroken' => 'Жоқталған сілтемелерді <a href="" class="new">былай</a> пішімде (басқаша: былай <a href="" class="internal">?</a> сияқты).',
 352+'tog-highlightbroken' => 'Жарамсыз сілтемелерді <a href="" class="new">былай</a> пішімде (баламасы: былай <a href="" class="internal">?</a> сияқты).',
352353 'tog-justify' => 'Ежелерді ені бойынша туралау',
353354 'tog-hideminor' => 'Жуықтағы өзгерістерде шағын түзетуді жасыр',
354355 'tog-extendwatchlist' => 'Бақылау тізімді ұлғайт (барлық жарамды өзгерістерді көрсет)',
@@ -448,7 +449,7 @@
449450 'categories' => 'Барлық санат тізімі',
450451 'pagecategories' => '{{PLURAL:$1|Санат|Санаттар}}',
451452 'category_header' => '«$1» санатындағы беттер',
452 -'subcategories' => 'Төменгі санаттар',
 453+'subcategories' => 'Санатшалар',
453454 'category-media-header' => '«$1» санатындағы таспалар',
454455
455456 'linkprefix' => '/^(.*?)([a-zäçéğıïñöşüýа-яёәіңғүұқөһA-ZÄÇÉĞİÏÑÖŞÜÝА-ЯЁӘІҢҒҮҰҚӨҺʺʹ«„]+)$/sDu',
@@ -887,7 +888,7 @@
888889 'nonunicodebrowser' => '<strong>АҢҒАРТПА: Шолғышыңыз Unicode белгілеуіне үйлесімді емес, сондықтан латын емес әріптері бар беттерді өңдеу зіл болу мүмкін. Жұмыс істеуге ықтималдық беру үшін, төменгі өңдеу аумағында ASCII емес әріптер оналтылық санымен көрсетіледі</strong>.',
889890 'editingold' => '<strong>АҢҒАРТПА: Осы беттің ертерек нұсқасын
890891 өңдеп жатырсыз.
891 -Бұны сақтасаңыз, осы нусқадан соңғы барлық түзетулер жойылады.</strong>',
 892+Бұны сақтасаңыз, осы нусқадан соңғы барлық өзгерістер жойылады.</strong>',
892893 'yourdiff' => 'Айырмалар',
893894 'copyrightwarning' => '{{SITENAME}} жобасына қосылған бүкіл үлес $2 (көбірек ақпарат үшін: $1) құжатына сай жіберілген болып саналады. Егер жазуыңыздың еркін көшіріліп түзетілуін қаламасаңыз, мында ұсынбауыңыз жөн.<br />
894895 Тағы, қосқан үлесіңіз - өзіңіздің жазғанығыз, не ашық ақпарат көздерінен алынған мағлұмат болғанын уәде етесіз.<br />
@@ -1068,7 +1069,7 @@
10691070 'qbsettings-fixedright' => 'Оңға бекітілген',
10701071 'qbsettings-floatingleft' => 'Солға қалқыған',
10711072 'qbsettings-floatingright' => 'Оңға қалқыған',
1072 -'changepassword' => 'Құпия сөз өзгерту',
 1073+'changepassword' => 'Құпия сөзді ауыстыру',
10731074 'skin' => 'Безендіру',
10741075 'math' => 'Математика',
10751076 'dateformat' => 'Күн-ай пішімі',
@@ -1150,7 +1151,7 @@
11511152 'rightsnone' => '(ешқандай)',
11521153
11531154 # Recent changes
1154 -'nchanges' => '{{PLURAL:$1|бір түзету|$1 түзету}}',
 1155+'nchanges' => '{{PLURAL:$1|бір өзгеріс|$1 өзгеріс}}',
11551156 'recentchanges' => 'Жуықтағы өзгерістер',
11561157 'recentchangestext' => 'Бұл бетте осы уикидегі болған жуықтағы өзгерістер байқалады.',
11571158 'recentchanges-feed-description' => 'Бұл арнаменен уикидегі ең соңғы өзгерістер қадағаланады.',
@@ -1177,9 +1178,9 @@
11781179 'rc_categories_any' => 'Қайсыбір',
11791180
11801181 # Recent changes linked
1181 -'recentchangeslinked' => 'Қатысты түзетулер',
 1182+'recentchangeslinked' => 'Қатысты өзгерістер',
11821183 'recentchangeslinked-noresult' => 'Сілтеген беттерде айтылмыш мерзімде ешқандай өзгеріс болмаған.',
1183 -'recentchangeslinked-summary' => "Бұл арнайы бетте сілтеген беттердегі жуықтағы өзгерістер тізімі беріледі. Бақылау тізіміңіздегі беттер '''жуан''' әрпімен белгіленеді.",
 1184+'recentchangeslinked-summary' => "Бұл арнайы бетте сілтеген беттердегі жуықтағы өзгерістер тізімі беріледі. Бақылау тізіміңіздегі беттер '''жуан''' әрбімен белгіленеді.",
11841185
11851186 # Upload
11861187 'upload' => 'Файл қотару',
@@ -1217,8 +1218,8 @@
12181219 'filetype-missing' => 'Бұл файлдың («.jpg» сияқты) кеңейтімі жоқ.',
12191220 'large-file' => 'Файлды $1 мөлшерден аспауына тырысыңыз; бұл файл мөлшері — $2.',
12201221 'largefileserver' => 'Осы файлдың мөлшері сервердің қалауынан асып кеткен.',
1221 -'emptyfile' => 'Қотарылған файлыңыз бос сияқты. Бұл файл атауы жансақ енгізілгенінен болуы мүмкін. Қотарғыңыз келген файл шынында да осы файл болғанын тексеріп алыңыз.',
1222 -'fileexists' => 'Осындай атаулы файл бар түге. Қайта жазудың алдынан $1 тексеріп шығыңыз.',
 1222+'emptyfile' => 'Қотарылған файлыңыз бос сияқты. Бұл файл атауында қате болуы мүмкін. Осы файлды шынайы қотарғыңыз келетін тексеріп шығыңыз.',
 1223+'fileexists' => 'Осындай атаулы файл бар түге, егер бұны өзгертуге сеніміңіз жоқ болса <strong><tt>$1</tt></strong> дегенді тексеріп шығыңыз.',
12231224 'fileexists-extension' => 'Ұқсасты файл атауы бар түге:<br />
12241225 Қотарылатын файл атауы: <strong><tt>$1</tt></strong><br />
12251226 Бар болған файл атауы: <strong><tt>$2</tt></strong><br />
@@ -1305,7 +1306,7 @@
13061307
13071308 # MIME search
13081309 'mimesearch' => 'Файлды MIME түрімен іздеу',
1309 -'mimesearch-summary' => 'Бұл бет файлдарды MIME түрімен сүзгілеу мүмкіндігін береді. Кірісі: «мағлұмат түрі»/«тарау түрі», мысалы <tt>image/jpeg</tt>.',
 1310+'mimesearch-summary' => 'Бұл бет файлдарды MIME түрімен сүзгілеу мүмкіндігін береді. Кірісі: «мағлұмат түрі»/«түр тарауы», мысалы <tt>image/jpeg</tt>.',
13101311 'mimetype' => 'MIME түрі:',
13111312 'download' => 'жүктеу',
13121313
@@ -1519,7 +1520,7 @@
15201521 * [[{{ns:special}}:Watchlist/edit|Бүкіл тізімді қарау және өзгерту]].
15211522 * [[{{ns:special}}:Watchlist/clear|Тізімдегі барлық дана аластату]].",
15221523 'wlheader-enotif' => '* Ескерту хат жіберуі ендірілген.',
1523 -'wlheader-showupdated' => "* Соңғы кіргенімнен бері түзетілген беттерді '''жуан''' мәтінмен көрсет",
 1524+'wlheader-showupdated' => "* Соңғы кіргенімнен бері өзгертілген беттерді '''жуан''' әрбімен көрсет",
15241525 'watchmethod-recent' => 'бақылаулы беттердің жуықтағы өзгерістерін тексеру',
15251526 'watchmethod-list' => 'жуықтағы өзгерістерде бақылаулы беттерді тексеру',
15261527 'removechecked' => 'Белгіленгенді бақылау тізімінен аластату',
@@ -1620,7 +1621,7 @@
16211622 «Артқа» түймесін басыңыз, және бетті кері жүктеңіз, сосын қайталап көріңіз.',
16221623 'protectlogpage' => 'Қорғау_журналы',
16231624 'protectlogtext' => 'Төменде беттердің қорғау/қорғамау тізімі берілген. Ағымдағы қорғау әректтер бар беттер үшін [[{{ns:special}}:Protectedpages|қорғалған бет тізімін]] қараңыз.',
1624 -'protectedarticle' => '«$1» қорғалды',
 1625+'protectedarticle' => '«[[$1]]» қорғалды',
16251626 'unprotectedarticle' => '«[[$1]]» қорғалмады',
16261627 'protectsub' => '(«$1» қорғауда)',
16271628 'confirmprotecttext' => 'Осы бетті расында да қорғау қажет пе?',
@@ -1650,7 +1651,7 @@
16511652 'protect-expiring' => 'бітуі: $1 (UTC)',
16521653 'protect-cascade' => 'Баулы қорғау — бұл бетке кірістірілген әрқайсы беттерді қорғау.',
16531654 'restriction-type' => 'Рұқсаты:',
1654 -'restriction-level' => 'Рұқсат деңгейі:',
 1655+'restriction-level' => 'Рұқсат шектеу деңгейі:',
16551656 'minimum-size' => 'Ең аз мөлшері',
16561657 'maximum-size' => 'Ең көп мөлшері',
16571658 'pagesize' => '(байт)',
@@ -1694,7 +1695,7 @@
16951696 'undeletedarticle' => '«[[$1]]» қайтарды',
16961697 'undeletedrevisions' => '{{PLURAL:$1|Нұсқаны|$1 нұсқаны}} қайтарды',
16971698 'undeletedrevisions-files' => '{{PLURAL:$1|Нұсқаны|$1 нұсқаны}} және {{PLURAL:$2|файлды|$2 файлды}} қайтарды',
1698 -'undeletedfiles' => '{{PLURAL:$1|1 файл|$1 файл}} қайтарды',
 1699+'undeletedfiles' => '{{PLURAL:$1|1 файлды|$1 файлды}} қайтарды',
16991700 'cannotundelete' => 'Қайтару сәтсіз бітті; тағы біреу сізден бұрын сол бетті қайтарған болар.',
17001701 'undeletedpage' => "<big>'''$1 қайтарылды'''</big>
17011702
@@ -1714,8 +1715,8 @@
17151716 'mycontris' => 'Үлесім',
17161717 'contribsub2' => '$1 ($2) үлесі',
17171718 'nocontribs' => 'Осы іздеу шартына сәйкес өзгерістер табылған жоқ.',
1718 -'ucnote' => 'Төменде осы қатысушының соңғы <b>$2</b> күндегі, соңғы <b>$1</b> өзгерісі көрсетледі.',
1719 -'uclinks' => 'Соңғы $2 күндегі, соңғы $1 өзгерісін қарау.',
 1719+'ucnote' => 'Төменде осы қатысушы жасаған соңғы <b>$2</b> күндегі, соңғы <b>$1</b> өзгерісі көрсетледі.',
 1720+'uclinks' => 'Соңғы $2 күндегі, соңғы жасалған $1 өзгерісін қарау.',
17201721 'uctop' => ' (үсті)',
17211722
17221723 'sp-contributions-newest' => 'Ең жаңасына',
@@ -1745,6 +1746,7 @@
17461747 'istemplate' => 'кіріктіру',
17471748 'whatlinkshere-prev' => '{{PLURAL:$1|алдыңғы|алдыңғы $1}}',
17481749 'whatlinkshere-next' => '{{PLURAL:$1|келесі|келесі $1}}',
 1750+'whatlinkshere-links' => '← сілтемелер',
17491751
17501752 # Block/unblock
17511753 'blockip' => 'Пайдаланушыны бұғаттау',
@@ -1864,8 +1866,8 @@
18651867 'movepagetext' => "Төмендегі үлгітті қолданып беттерді қайта атайды,
18661868 барлық тарихын жаңа атауға жылжытады.
18671869 Бұрынғы бет атауы жаңа атауға айдататын бет болады.
1868 -Ескі атауына сілтейтін сілтемелер өзгертілмейді; жылжытудан соң
1869 -шынжырлы айдатулар бар-жоғын тексеріңіз.
 1870+Ескі атауына сілтейтін сілтемелер өзгертілмейді; жылжытудан соң
 1871+шынжырлы не жарамсыз айдатулар бар-жоғын тексеріп шығыңыз.
18701872 Сілтемелер бұрынғы жолдауымен былайғы өтуін тексеруіне
18711873 сіз міндетті боласыз.
18721874
@@ -1876,7 +1878,7 @@
18771879 бірақ бар беттің үстіне жазуға болмайды.
18781880
18791881 <b>НАЗАР САЛЫҢЫЗ!</b>
1880 -Бұл дәріпті бетке қатаң және кенет өзгеріс жасауға мүмкін;
 1882+Бұл әйгілі бетке қатаң және кенет өзгеріс жасауға мүмкін;
18811883 әрекеттің алдынан осының зардаптарын түсінгеніңізге батыл
18821884 болыңыз.",
18831885 'movepagetalktext' => "Келесі себептер '''болғанша''' дейін, талқылау беті өздіктік бірге жылжытылады:
@@ -2041,8 +2043,8 @@
20422044 'tooltip-ca-nstab-help' => 'Анықтыма бетін қарау',
20432045 'tooltip-ca-nstab-category' => 'Санат бетін қарау',
20442046 'tooltip-minoredit' => 'Осыны шағын түзету деп белгілеу',
2045 -'tooltip-save' => 'Түзетуіңізді сақтау',
2046 -'tooltip-preview' => 'Сақтаудың алдынан түзетуіңізді қарап шығыңыз!',
 2047+'tooltip-save' => 'Жасаған өзгерістеріңізді сақтау',
 2048+'tooltip-preview' => 'Сақтаудың алдынан жасаған өзгерістеріңізді қарап шығыңыз!',
20472049 'tooltip-diff' => 'Мәтінге қандай өзгерістерді жасағаныңызды қарау.',
20482050 'tooltip-compareselectedversions' => 'Беттің екі нұсқасының айырмасын қарау.',
20492051 'tooltip-watch' => 'Бұл бетті бақылау тізіміңізге үстеу',
@@ -2134,7 +2136,7 @@
21352137 'spamprotectiontitle' => '«Спам»-нан қорғайтын сүзгі',
21362138 'spamprotectiontext' => 'Бұл беттің сақтауын «спам» сүзгісі бұғаттады. Бұның себебі сыртқы торап сілтемесінен болуы мүмкін.',
21372139 'spamprotectionmatch' => 'Келесі «спам» мәтіні сүзгіленген: $1',
2138 -'subcategorycount' => 'Бұл санатта {{PLURAL:$1|бір|$1}} төменгі санат бар.',
 2140+'subcategorycount' => 'Бұл санатта {{PLURAL:$1|бір|$1}} санатша бар.',
21392141 'categoryarticlecount' => 'Бұл санатта {{PLURAL:$1|бір|$1}} бет бар.',
21402142 'category-media-count' => 'Бұл санатта {{PLURAL:$1|бір|$1}} файл бар.',
21412143 'listingcontinuesabbrev' => ' (жалғ.)',
@@ -2167,7 +2169,7 @@
21682170 'rcpatroldisabledtext' => 'Жуықтағы өзгерістер Күзеті қасиеті ағымда өшірілген.',
21692171 'markedaspatrollederror' => 'Күзетте деп белгіленбейді',
21702172 'markedaspatrollederrortext' => 'Күзетте деп белгілеу үшін нұсқасын енгізіңіз.',
2171 -'markedaspatrollederror-noautopatrol' => 'Өзіңіздің өзгерістеріңізді күзетке қоя алмайсыз.',
 2173+'markedaspatrollederror-noautopatrol' => 'Өзіңіз жасаған өзгерістеріңізді күзетке қоя алмайсыз.',
21722174
21732175 # Patrol log
21742176 'patrol-log-page' => 'Күзет журналы',
@@ -2184,7 +2186,7 @@
21852187
21862188 # Media information
21872189 'mediawarning' => "'''Назар салыңыз''': Бұл файл түрінде қаскүнемді әмірдің бар болуы ықтимал; файлды жегіп жүйеңізге зиян келтіруіңіз мүмкін.<hr />",
2188 -'imagemaxsize' => 'Сурет түйіндеме бетіндегі суреттің мөлшерін шектеуі:',
 2190+'imagemaxsize' => 'Сипаттамасы бетіндегі суреттің мөлшерін шектеуі:',
21892191 'thumbsize' => 'Нобай мөлшері:',
21902192 'widthheight' => '$1 × $2',
21912193 'file-info' => 'Файл мөлшері: $1, MIME түрі: $2',
@@ -2490,7 +2492,7 @@
24912493 # Pseudotags used for GPSSpeedRef and GPSDestDistanceRef
24922494 'exif-gpsspeed-k' => 'km/h',
24932495 'exif-gpsspeed-m' => 'mil/h',
2494 -'exif-gpsspeed-n' => 'Ж. түйін',
 2496+'exif-gpsspeed-n' => 'knot',
24952497
24962498 # Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef
24972499 'exif-gpsdirection-t' => 'Шын бағыт',
Index: branches/liquidthreads/languages/messages/MessagesKa.php
@@ -37,859 +37,533 @@
3838 );
3939
4040 $messages = array(
41 -
4241 # User preference toggles
43 -'tog-underline' => 'ბმულებზე გაზის გასმა:',
44 -'tog-highlightbroken' => 'აჩვენე არარსებული ბმულები <a href="" class="new">როგორც ეს</a> (ალტერნატივა: როგორც ეს<a href="" class="internal">?</a>).',
45 -#TODO: 'tog-justify' => 'Justify paragraphs',
46 -'tog-hideminor' => 'უკანასკნელ ცვლილებებში მცირე რედაქტირებების დამალვა',
47 -'tog-extendwatchlist' => 'განავრცეთ კონტროლის სია ისე, რომ აჩვენოთ ყველა შესაძლებელი ცვლილება',
48 -'tog-usenewrc' => 'გაზარდეთ ბოლო ცვლილებების სია (ჯავასკრიპტი)',
49 -'tog-numberheadings' => 'სათაურების ავტომატურად გადანომვრა',
50 -'tog-showtoolbar' => 'სარედაქტორო ინსტრუმენტების პანელის (ჯავასკრიპტი) ჩვენება',
51 -'tog-editondblclick' => 'გვერდების რედაქტირება მოახდინეთ ორმაგი დაჭერით (ჯავასკრიპტი)',
52 -'tog-editsection' => 'ნება დართეთ სექციის რედაქტირებაზე \'[რედაქტირება]\' ბმულების გავლით',
53 -'tog-editsectiononrightclick' => 'ნება დართეთ სექციის რედაქტირებაზე მარჯვენა ღილაკზე დაჭერით<br />სექციის სათაურებზე (ჯავასკრიპტი)',
54 -'tog-showtoc' => 'აჩვენეთ სარჩევი ცხრილი (იმ გვერდებისათვის, რომელსაც სამზე მეტი სათაური აქვთ)',
55 -'tog-rememberpassword' => 'სესიებს შორის პაროლის დამახსოვრება',
56 -'tog-editwidth' => 'სარედაქტირო ყუთს აქვს სრული სიგანე',
57 -'tog-watchcreations' => 'გვერდები, რომელიც მე გავხსენი, დაამატეთ ჩემს საკონტროლო სიას',
58 -'tog-watchdefault' => 'დამალეთ რობოტის რედაქტირებები საკონტროლო სიიდან',
59 -#TODO: 'tog-watchmoves' => 'Add pages I move to my watchlist',
60 -#TODO: 'tog-watchdeletion' => 'Add pages I delete to my watchlist',
61 -#TODO: 'tog-minordefault' => 'Mark all edits minor by default',
62 -'tog-previewontop' => 'რედაქტირების ფანჯრამდე წინასწარი ხედვის ჩვენება',
63 -'tog-previewonfirst' => 'პირველი რედაქტიებისას წინასწარი გადახედვის ჩვენება',
64 -#TODO: 'tog-nocache' => 'Disable page caching',
65 -'tog-enotifwatchlistpages' => 'მომწერეთ როდესაც გვერდი, რომელსაც მე ვაკონტროლებ შეიცვლება',
66 -'tog-enotifusertalkpages' => 'მომწერეთ, როდესაც ჩემი მომხმარებლის განხილვის გვერდი შეიცვლება',
67 -'tog-enotifminoredits' => 'მომწერეთ ასევე მცირე რედაქტირებების შესახებ გვერდებზე',
68 -'tog-enotifrevealaddr' => 'ჩემი ელ. ფოსტის მისამართი შეხსენებების წერილებში აჩვენეთ',
69 -'tog-shownumberswatching' => 'კონტროლის ქვეშ მყოფი მომხმარებელთა რაოდენობის ჩვენება',
70 -'tog-fancysig' => 'გამოუყენებელი ხელმოწერები (ავტომატური ბმულის გარეშე)',
71 -#TODO: 'tog-externaleditor' => 'Use external editor by default',
72 -#TODO: 'tog-externaldiff' => 'Use external diff by default',
73 -'tog-showjumplinks' => 'დამხმარე ბმულების "გადასვლა -კენ" ჩართვა',
74 -'tog-uselivepreview' => 'გამოიყენეთ ახალი წინასწარი გადახედვა (ჯავასკრიპტი) (ექსპერიმენტული)',
75 -#TODO: 'tog-forceeditsummary' => 'Prompt me when entering a blank edit summary',
76 -'tog-watchlisthideown' => 'დამალეთ საკონტროლო სიიდან ჩემი რედაქტირებები',
77 -'tog-watchlisthidebots' => 'დამალეთ საკონტროლო სიიდან ჩემი რედაქტირებები',
78 -'tog-watchlisthideminor' => 'დამალეთ საკონტროლო სიიდან მცირე რედაქტირებები',
79 -#TODO: 'tog-nolangconversion' => 'Disable variants conversion',
80 -#TODO: 'tog-ccmeonemails' => 'Send me copies of emails I send to other users',
81 -#TODO: 'tog-diffonly' => "Don't show page content below diffs",
 42+'tog-underline' => 'ბმულებზე გაზის გასმა:',
 43+'tog-highlightbroken' => 'აჩვენე არარსებული ბმულები <a href="" class="new">როგორც ეს</a> (ალტერნატივა: როგორც ეს<a href="" class="internal">?</a>).',
 44+'tog-hideminor' => 'უკანასკნელ ცვლილებებში მცირე რედაქტირებების დამალვა',
 45+'tog-extendwatchlist' => 'განავრცეთ კონტროლის სია ისე, რომ აჩვენოთ ყველა შესაძლებელი ცვლილება',
 46+'tog-usenewrc' => 'გაზარდეთ ბოლო ცვლილებების სია (ჯავასკრიპტი)',
 47+'tog-numberheadings' => 'სათაურების ავტომატურად გადანომვრა',
 48+'tog-showtoolbar' => 'სარედაქტორო ინსტრუმენტების პანელის (ჯავასკრიპტი) ჩვენება',
 49+'tog-editondblclick' => 'გვერდების რედაქტირება მოახდინეთ ორმაგი დაჭერით (ჯავასკრიპტი)',
 50+'tog-editsection' => "ნება დართეთ სექციის რედაქტირებაზე '[რედაქტირება]' ბმულების გავლით",
 51+'tog-editsectiononrightclick' => 'ნება დართეთ სექციის რედაქტირებაზე მარჯვენა ღილაკზე დაჭერით<br />სექციის სათაურებზე (ჯავასკრიპტი)',
 52+'tog-showtoc' => 'აჩვენეთ სარჩევი ცხრილი (იმ გვერდებისათვის, რომელსაც სამზე მეტი სათაური აქვთ)',
 53+'tog-rememberpassword' => 'სესიებს შორის პაროლის დამახსოვრება',
 54+'tog-editwidth' => 'სარედაქტირო ყუთს აქვს სრული სიგანე',
 55+'tog-watchcreations' => 'გვერდები, რომელიც მე გავხსენი, დაამატეთ ჩემს საკონტროლო სიას',
 56+'tog-watchdefault' => 'დამალეთ ბოტის რედაქტირებები საკონტროლო სიიდან',
 57+'tog-previewontop' => 'რედაქტირების ფანჯრამდე წინასწარი ხედვის ჩვენება',
 58+'tog-previewonfirst' => 'პირველი რედაქტიებისას წინასწარი გადახედვის ჩვენება',
 59+'tog-enotifwatchlistpages' => 'მომწერეთ როდესაც გვერდი, რომელსაც მე ვაკონტროლებ შეიცვლება',
 60+'tog-enotifusertalkpages' => 'მომწერეთ, როდესაც ჩემი მომხმარებლის განხილვის გვერდი შეიცვლება',
 61+'tog-enotifminoredits' => 'მომწერეთ ასევე მცირე რედაქტირებების შესახებ გვერდებზე',
 62+'tog-enotifrevealaddr' => 'ჩემი ელ. ფოსტის მისამართი შეხსენებების წერილებში აჩვენეთ',
 63+'tog-shownumberswatching' => 'კონტროლის ქვეშ მყოფი მომხმარებელთა რაოდენობის ჩვენება',
 64+'tog-fancysig' => 'გამოუყენებელი ხელმოწერები (ავტომატური ბმულის გარეშე)',
 65+'tog-showjumplinks' => 'დამხმარე ბმულების "გადასვლა -კენ" ჩართვა',
 66+'tog-uselivepreview' => 'გამოიყენეთ ახალი წინასწარი გადახედვა (ჯავასკრიპტი) (ექსპერიმენტული)',
 67+'tog-watchlisthideown' => 'დამალეთ საკონტროლო სიიდან ჩემი რედაქტირებები',
 68+'tog-watchlisthidebots' => 'დამალეთ საკონტროლო სიიდან ჩემი რედაქტირებები',
 69+'tog-watchlisthideminor' => 'დამალეთ საკონტროლო სიიდან მცირე რედაქტირებები',
8270
8371 'underline-always' => 'ყოველთვის',
84 -'underline-never' => 'არასოდეს',
85 -#TODO: 'underline-default' => 'Browser default',
 72+'underline-never' => 'არასოდეს',
8673
8774 'skinpreview' => '(წინასწარი გადახედვა)',
8875
8976 # Dates
90 -'sunday' => 'კვირა',
91 -'monday' => 'ორშაბათი',
92 -'tuesday' => 'სამშაბათი',
93 -'wednesday' => 'ოთხშაბათი',
94 -'thursday' => 'ხუთშაბათი',
95 -'friday' => 'პარასკევი',
96 -'saturday' => 'შაბათი',
97 -'sun' => 'კვი',
98 -'mon' => 'ორშ',
99 -'tue' => 'სამ',
100 -'wed' => 'ოთხ',
101 -'thu' => 'ხუთ',
102 -'fri' => 'პარ',
103 -'sat' => 'შაბ',
104 -'january' => 'იანვარი',
105 -'february' => 'თებერვალი',
106 -'march' => 'მარტი',
107 -'april' => 'აპრილი',
108 -'may_long' => 'მაისი',
109 -'june' => 'ივნისი',
110 -'july' => 'ივლისი',
111 -'august' => 'აგვისტო',
112 -'september' => 'სექტემბერი',
113 -'october' => 'ოქტომბერი',
114 -'november' => 'ნოემბერი',
115 -'december' => 'დეკემბერი',
116 -'january-gen' => 'იანვრის',
117 -'february-gen' => 'თებერვლის',
118 -'march-gen' => 'მარტის',
119 -'april-gen' => 'აპრილის',
120 -'may-gen' => 'მაისის',
121 -'june-gen' => 'ივნისის',
122 -'july-gen' => 'ივლისის',
123 -'august-gen' => 'აგვისტოს',
124 -'september-gen' => 'სექტემბრის',
125 -'october-gen' => 'ოქტომბრის',
126 -'november-gen' => 'ნოემბრის',
127 -'december-gen' => 'დეკემბრის',
128 -'jan' => 'იან',
129 -'feb' => 'თებ',
130 -'mar' => 'მარ',
131 -'apr' => 'აპრ',
132 -'may' => 'მაი',
133 -'jun' => 'ივნ',
134 -'jul' => 'ივლ',
135 -'aug' => 'აგვ',
136 -'sep' => 'სექ',
137 -'oct' => 'ოქტ',
138 -'nov' => 'ნოე',
139 -'dec' => 'დეკ',
 77+'sunday' => 'კვირა',
 78+'monday' => 'ორშაბათი',
 79+'tuesday' => 'სამშაბათი',
 80+'wednesday' => 'ოთხშაბათი',
 81+'thursday' => 'ხუთშაბათი',
 82+'friday' => 'პარასკევი',
 83+'saturday' => 'შაბათი',
 84+'sun' => 'კვი',
 85+'mon' => 'ორშ',
 86+'tue' => 'სამ',
 87+'wed' => 'ოთხ',
 88+'thu' => 'ხუთ',
 89+'fri' => 'პარ',
 90+'sat' => 'შაბ',
 91+'january' => 'იანვარი',
 92+'february' => 'თებერვალი',
 93+'march' => 'მარტი',
 94+'april' => 'აპრილი',
 95+'may_long' => 'მაისი',
 96+'june' => 'ივნისი',
 97+'july' => 'ივლისი',
 98+'august' => 'აგვისტო',
 99+'september' => 'სექტემბერი',
 100+'october' => 'ოქტომბერი',
 101+'november' => 'ნოემბერი',
 102+'december' => 'დეკემბერი',
 103+'january-gen' => 'იანვრის',
 104+'february-gen' => 'თებერვლის',
 105+'march-gen' => 'მარტის',
 106+'april-gen' => 'აპრილის',
 107+'may-gen' => 'მაისის',
 108+'june-gen' => 'ივნისის',
 109+'july-gen' => 'ივლისის',
 110+'august-gen' => 'აგვისტოს',
 111+'september-gen' => 'სექტემბრის',
 112+'october-gen' => 'ოქტომბრის',
 113+'november-gen' => 'ნოემბრის',
 114+'december-gen' => 'დეკემბრის',
 115+'jan' => 'იან',
 116+'feb' => 'თებ',
 117+'mar' => 'მარ',
 118+'apr' => 'აპრ',
 119+'may' => 'მაი',
 120+'jun' => 'ივნ',
 121+'jul' => 'ივლ',
 122+'aug' => 'აგვ',
 123+'sep' => 'სექ',
 124+'oct' => 'ოქტ',
 125+'nov' => 'ნოე',
 126+'dec' => 'დეკ',
140127
141 -# Bits of text used by many pages:
142 -'categories' => 'კატეგორიები',
143 -'pagecategories' => '{{PLURAL:$1|კატეგორია|კატეგორიები}}',
 128+# Bits of text used by many pages
 129+'categories' => 'კატეგორიები',
 130+'pagecategories' => '{{PLURAL:$1|კატეგორია|კატეგორიები}}',
144131 'category_header' => 'სტატიები კატეგორიაში "$1"',
145 -'subcategories' => 'ქვეკატეგორიები',
 132+'subcategories' => 'ქვეკატეგორიები',
146133
147 -
148134 'linkprefix' => '/^(.*?)(„|«)$/sD',
149 -'mainpage' => 'მთავარი გვერდი',
150 -#TODO: 'mainpagetext' => "<big>'''MediaWiki has been successfully installed.'''</big>",
151 -/*TODO: 'mainpagedocfooter' => "Consult the [http://meta.wikimedia.org/wiki/Help:Contents User's Guide] for information on using the wiki software.
152135
153 -== Getting started ==
 136+'about' => 'შესახებ',
 137+'article' => 'სტატია',
 138+'newwindow' => '(ახალ ფანჯარაში)',
 139+'cancel' => 'გაუქმება',
 140+'qbfind' => 'ძებნა',
 141+'qbbrowse' => 'მიმოხილვა',
 142+'qbedit' => 'რედაქტირება',
 143+'qbpageoptions' => 'ეს გვერდი',
 144+'qbpageinfo' => 'კონტექსტი',
 145+'qbmyoptions' => 'ჩემი გვერდები',
 146+'qbspecialpages' => 'სპეციალური გვერდები',
 147+'moredotdotdot' => 'მეტი...',
 148+'mypage' => 'ჩემი გვერდი',
 149+'mytalk' => 'ჩემი განხილვა',
 150+'anontalk' => 'ამ IP-ს განხილვა',
 151+'navigation' => 'ნავიგაცია',
154152
155 -* [http://www.mediawiki.org/wiki/Help:Configuration_settings Configuration settings list]
156 -* [http://www.mediawiki.org/wiki/Help:FAQ MediaWiki FAQ]
157 -* [http://mail.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list]",*/
 153+'errorpagetitle' => 'შეცდომა',
 154+'returnto' => '$1-ზე დაბრუნება.',
 155+'tagline' => '{{SITENAME}}დან',
 156+'help' => 'დახმარება',
 157+'search' => 'ძიება',
 158+'searchbutton' => 'ძიება',
 159+'go' => 'გვერდი',
 160+'searcharticle' => 'გვერდი',
 161+'history' => 'გვერდის ისტორია',
 162+'history_short' => 'ისტორია',
 163+'updatedmarker' => 'ჩემი უკანასკნელი შემოსვლიდან ცვლილებები',
 164+'info_short' => 'ინფორმაცია',
 165+'printableversion' => 'დასაბეჭდი ვერსია',
 166+'permalink' => 'მუდმივი ბმული',
 167+'print' => 'ბეჭდვა',
 168+'edit' => 'რედაქტირება',
 169+'editthispage' => 'ამ გვერდის რედაქტირება',
 170+'delete' => 'წაშლა',
 171+'deletethispage' => 'ამ გვერდის წაშლა',
 172+'undelete_short' => '$1 ცვლილების აღდგენა',
 173+'protect' => 'დაცვა',
 174+'unprotect' => 'დაცვის მოხსნა',
 175+'unprotectthispage' => 'გვერდის დაცვის მოხსნა',
 176+'newpage' => 'ახალი გვერდი',
 177+'talkpage' => 'განიხილეთ ეს გვერდი',
 178+'talkpagelinktext' => 'განხილვა',
 179+'specialpage' => 'სპეციალური გვერდი',
 180+'postcomment' => 'დაურთეთ კომენტარი',
 181+'articlepage' => 'სტატიის ნახვა',
 182+'talk' => 'განხილვა',
 183+'toolbox' => 'ხელსაწყოები',
 184+'userpage' => 'მომხმარებლის გვერდის ხილვა',
 185+'projectpage' => 'პროექტის გვერდის ხილვა',
 186+'imagepage' => 'სურათის გვერდის ნახვა',
 187+'categorypage' => 'კატეგორიის გვერდის ხილვა',
 188+'otherlanguages' => 'სხვა ენებზე',
 189+'redirectedfrom' => '(გადამისამართდა გვერდიდან $1)',
 190+'redirectpagesub' => 'გადამისამართების გვერდი',
 191+'lastmodifiedat' => 'ეს გვერდი ბოლოს განახლდა $2, $1.', # $1 date, $2 time
 192+'jumptonavigation' => 'ნავიგაცია',
 193+'jumptosearch' => 'ძიება',
158194
159 -'portal' => 'საზოგადოების პორტალი',
160 -'portal-url' => '{{ns:project}}:საზოგადოების პორტალი',
161 -'about' => 'შესახებ',
162 -'aboutsite' => '{{SITENAME}}-ის შესახებ',
163 -'aboutpage' => 'პროექტი:შესახებ',
164 -'article' => 'სტატია',
165 -'help' => 'დახმარება',
166 -'helppage' => '{{ns:project}}:დახმარება',
167 -'bugreports' => 'ანგარიში შეცდომის შესახებ',
168 -'bugreportspage' => '{{ns:project}}:ანგარიში შეცდომის შესახებ',
169 -'sitesupport' => 'შეწირულობები',
170 -'sitesupport-url' => '{{ns:project}}:შეწირულობები',
171 -'faq' => 'ხშირი შეკითხვები',
172 -'faqpage' => '{{ns:project}}:ხშირი შეკითხვები',
173 -'edithelp' => 'რედაქტირების დახმარება',
174 -'newwindow' => '(ახალ ფანჯარაში)',
175 -'edithelppage' => '{{ns:project}}:რედაქტირების დახმარება',
176 -'cancel' => 'გაუქმება',
177 -'qbfind' => 'ძებნა',
178 -'qbbrowse' => 'მიმოხილვა',
179 -'qbedit' => 'რედაქტირება',
180 -'qbpageoptions' => 'ეს გვერდი',
181 -'qbpageinfo' => 'კონტექსტი',
182 -'qbmyoptions' => 'ჩემი გვერდები',
183 -'qbspecialpages' => 'სპეციალური გვერდები',
184 -'moredotdotdot' => 'მეტი...',
185 -'mypage' => 'ჩემი გვერდი',
186 -'mytalk' => 'ჩემი განხილვა',
187 -'anontalk' => 'ამ IP-ს განხილვა',
188 -'navigation' => 'ნავიგაცია',
189 -
190 -# Metadata in edit box
191 -#TODO: 'metadata_help' => 'Metadata (see [[{{ns:project}}:Metadata]] for an explanation):',
192 -
193 -'currentevents' => 'ახალი ამბები',
 195+# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage) and the disambiguation template definition (see disambiguations).
 196+'aboutsite' => '{{SITENAME}}-ის შესახებ',
 197+'aboutpage' => 'პროექტი:შესახებ',
 198+'bugreports' => 'ანგარიში შეცდომის შესახებ',
 199+'bugreportspage' => '{{ns:project}}:ანგარიში შეცდომის შესახებ',
 200+'currentevents' => 'ახალი ამბები',
194201 'currentevents-url' => 'ახალი ამბები',
 202+'disclaimers' => 'პასუხისმგებლობის უარყოფა',
 203+'disclaimerpage' => '{{ns:project}}:პასუხისმგებლობის უარყოფა',
 204+'edithelp' => 'რედაქტირების დახმარება',
 205+'edithelppage' => '{{ns:project}}:რედაქტირების დახმარება',
 206+'faq' => 'ხშირი შეკითხვები',
 207+'faqpage' => '{{ns:project}}:ხშირი შეკითხვები',
 208+'helppage' => '{{ns:project}}:დახმარება',
 209+'mainpage' => 'მთავარი გვერდი',
 210+'portal' => 'საზოგადოების პორტალი',
 211+'portal-url' => '{{ns:project}}:საზოგადოების პორტალი',
 212+'privacy' => 'კონფიდენციალურობის პოლიტიკა',
 213+'privacypage' => '{{ns:project}}:კონფიდენციალურობის პოლიტიკა',
 214+'sitesupport' => 'შეწირულობები',
 215+'sitesupport-url' => '{{ns:project}}:შეწირულობები',
195216
196 -'disclaimers' => 'პასუხისმგებლობის უარყოფა',
197 -'disclaimerpage' => '{{ns:project}}:პასუხისმგებლობის უარყოფა',
198 -'privacy' => 'კონფიდენციალურობის პოლიტიკა',
199 -'privacypage' => '{{ns:project}}:კონფიდენციალურობის პოლიტიკა',
200 -'errorpagetitle' => 'შეცდომა',
201 -'returnto' => '$1-ზე დაბრუნება.',
202 -'tagline' => '{{SITENAME}}დან',
203 -'help' => 'დახმარება',
204 -'search' => 'ძიება',
205 -'searchbutton' => 'ძიება',
206 -'go' => 'გვერდი',
207 -'searcharticle' => 'გვერდი',
208 -'history' => 'გვერდის ისტორია',
209 -'history_short' => 'ისტორია',
210 -'updatedmarker' => 'ჩემი უკანასკნელი შემოსვლიდან ცვლილებები',
211 -'info_short' => 'ინფორმაცია',
212 -'printableversion' => 'დასაბეჭდი ვერსია',
213 -'permalink' => 'მუდმივი ბმული',
214 -'print' => 'ბეჭდვა',
215 -'edit' => 'რედაქტირება',
216 -'editthispage' => 'ამ გვერდის რედაქტირება',
217 -'delete' => 'წაშლა',
218 -'deletethispage' => 'ამ გვერდის წაშლა',
219 -'undelete_short' => '$1 ცვლილების აღდგენა',
220 -'protect' => 'დაცვა',
221 -#TODO: 'protectthispage' => 'Protect this page',
222 -'unprotect' => 'დაცვის მოხსნა',
223 -'unprotectthispage' => 'გვერდის დაცვის მოხსნა',
224 -'newpage' => 'ახალი გვერდი',
225 -'talkpage' => 'განიხილეთ ეს გვერდი',
226 -'specialpage' => 'სპეციალური გვერდი',
227 -#TODO: 'personaltools' => 'Personal tools',
228 -'postcomment' => 'დაურთეთ კომენტარი',
 217+'badaccess' => 'აკრძალული მოქმედება',
229218
230 -'articlepage' => 'სტატიის ნახვა',
231 -'talk' => 'განხილვა',
232 -#TODO: 'views' => 'Views',
233 -'toolbox' => 'ხელსაწყოები',
234 -'userpage' => 'მომხმარებლის გვერდის ხილვა',
235 -'projectpage' => 'პროექტის გვერდის ხილვა',
236 -'imagepage' => 'სურათის გვერდის ნახვა',
237 -#TODO: 'mediawikipage' => 'View message page',
238 -#TODO: #'templatepage' => 'View template page',
239 -#TODO: 'viewhelppage' => 'View help page',
240 -'categorypage' => 'კატეგორიის გვერდის ხილვა',
241 -#TODO: 'viewtalkpage' => 'View discussion',
242 -'otherlanguages' => 'სხვა ენებზე',
243 -#TODO: 'redirectedfrom' => '(Redirected from $1)',
244 -'autoredircomment' => 'გადამისამართება [[$1]]-ზე',
245 -'redirectpagesub' => 'გადამისამართების გვერდი',
246 -'lastmodifiedat' => 'ეს გვერდი ბოლოს განახლდა $2, $1.', //$1 date, $2 time
247 -#TODO: 'viewcount' => 'This page has been accessed {{plural:$1|one time|$1 times}}.',
248 -#TODO: 'copyright' => 'Content is available under $1.',
249 -#TODO: 'protectedpage' => 'Protected page',
250 -#TODO: 'jumpto' => 'Jump to:',
251 -'jumptonavigation' => 'ნავიგაცია',
252 -'jumptosearch' => 'ძიება',
253 -
254 -'badaccess' => 'აკრძალული მოქმედება',
255 -#'badaccess-group0' => 'You are not allowed to execute the action you have requested.',
256 -#'badaccess-group1' => 'The action you have requested is limited to users in the group $1.',
257 -#'badaccess-group2' => 'The action you have requested is limited to users in one of the groups $1.',
258 -#'badaccess-groups' => 'The action you have requested is limited to users in one of the groups $1.',
259 -
260 -#'versionrequired' => 'Version $1 of MediaWiki required',
261 -#'versionrequiredtext' => 'Version $1 of MediaWiki is required to use this page. See [[Special:Version]]',
262 -
263 -#DONT: 'widthheight' => '$1×$2',
264 -#'ok' => 'OK',
265 -#'sitetitle' => '{{SITENAME}}',
266 -#'pagetitle' => '$1 - {{SITENAME}}',
267 -#'sitesubtitle' => '',
268 -#'retrievedfrom' => 'Retrieved from "$1"',
269 -'youhavenewmessages' => 'თქვენ გაქვთ $1 ($2).',
270 -'newmessageslink' => 'ახალი შეტყობინებები',
 219+'youhavenewmessages' => 'თქვენ გაქვთ $1 ($2).',
 220+'newmessageslink' => 'ახალი შეტყობინებები',
271221 'newmessagesdifflink' => 'განსხვავება უკანასკნელ მდგომარეობას შორის',
272 -'editsection'=>'რედაქტირება',
273 -'editold'=>'რედაქტირება',
274 -'editsectionhint' => 'სექციის რედაქტირება: $1',
275 -'toc' => 'სარჩევი',
276 -'showtoc' => 'ჩვენება',
277 -'hidetoc' => 'დამალვა',
278 -'thisisdeleted' => 'გსურთ განიხილოთ ან აღადგინოთ $1?',
279 -'viewdeleted' => 'იხილე $1?',
280 -'restorelink' => '{{PLURAL:$1|ერთი წაშლილი რედაქტირება|$1 წაშლილი რედაქტირება}}',
281 -#'feedlinks' => 'Feed:',
282 -#'feed-invalid' => 'Invalid subscription feed type.',
283 -#'sitenotice' => '-', # the equivalent to wgSiteNotice
284 -#'anonnotice' => '-',
 222+'editsection' => 'რედაქტირება',
 223+'editold' => 'რედაქტირება',
 224+'editsectionhint' => 'სექციის რედაქტირება: $1',
 225+'toc' => 'სარჩევი',
 226+'showtoc' => 'ჩვენება',
 227+'hidetoc' => 'დამალვა',
 228+'thisisdeleted' => 'გსურთ განიხილოთ ან აღადგინოთ $1?',
 229+'viewdeleted' => 'იხილე $1?',
 230+'restorelink' => '{{PLURAL:$1|ერთი წაშლილი რედაქტირება|$1 წაშლილი რედაქტირება}}',
285231
286232 # Short words for each namespace, by default used in the 'article' tab in monobook
287 -'nstab-main' => 'სტატია',
288 -'nstab-user' => 'მომხმარებლის გვერდი',
289 -'nstab-media' => 'მედია',
290 -'nstab-special' => 'სპეციალური',
291 -'nstab-project' => 'პროექტის გვერდი',
292 -'nstab-image' => 'ფაილი',
 233+'nstab-main' => 'სტატია',
 234+'nstab-user' => 'მომხმარებლის გვერდი',
 235+'nstab-media' => 'მედია',
 236+'nstab-special' => 'სპეციალური',
 237+'nstab-project' => 'პროექტის გვერდი',
 238+'nstab-image' => 'ფაილი',
293239 'nstab-mediawiki' => 'შეტყობინება',
294 -'nstab-template' => 'თარგი',
295 -'nstab-help' => 'დახმარება',
296 -'nstab-category' => 'კატეგორია',
 240+'nstab-template' => 'თარგი',
 241+'nstab-help' => 'დახმარება',
 242+'nstab-category' => 'კატეგორია',
297243
298 -# Main script and global functions
299 -
300 -# General errors
301 -
302244 # Login and logout pages
 245+'login' => 'შესვლა',
 246+'userlogin' => 'შესვლა / ანგარიშის გახსნა',
 247+'logout' => 'გასვლა',
 248+'userlogout' => 'გასვლა',
303249
304 -# Edit page toolbar
305 -
306 -# Edit pages
307 -
308 -# History pages
309 -
310 -# Revision deletion
311 -
312250 # Diffs
313 -'difference' => '(სხვაობა ვერსიებს შორის)',
314 -#TODO: 'loadingrev' => 'loading revision for diff',
315 -'lineno' => "ხაზი $1:",
316 -'editcurrent' => 'ამ გვერდის ამჟამინდელი ვერსიის რედაქტირება',
317 -#TODO: 'selectnewerversionfordiff' => 'Select a newer version for comparison',
318 -#TODO: 'selectolderversionfordiff' => 'Select an older version for comparison',
 251+'difference' => '(სხვაობა ვერსიებს შორის)',
 252+'lineno' => 'ხაზი $1:',
 253+'editcurrent' => 'ამ გვერდის ამჟამინდელი ვერსიის რედაქტირება',
319254 'compareselectedversions' => 'არჩეული ვერსიების შედარება',
320255
321256 # Search results
322 -#
323 -#TODO: 'searchresults' => 'Search results',
324 -#TODO: 'searchresulttext' => "For more information about searching {{SITENAME}}, see [[{{MediaWiki:helppage}}|{{int:help}}]].",
325 -#TODO: 'searchsubtitle' => "You searched for '''[[:$1]]'''",
326 -#TODO: 'searchsubtitleinvalid' => "You searched for '''$1'''",
327 -#TODO: 'badquery' => 'Badly formed search query',
328 -/*#TODO: 'badquerytext' => 'We could not process your query.
329 -This is probably because you have attempted to search for a
330 -word fewer than three letters long, which is not yet supported.
331 -It could also be that you have mistyped the expression, for
332 -example "fish and and scales".
333 -Please try another query.',*/
334 -/*#TODO: 'matchtotals' => "The query \"$1\" matched $2 page titles
335 -and the text of $3 pages.",*/
336 -#TODO: 'noexactmatch' => "'''There is no page titled \"$1\".''' You can [[:$1|create this page]].",
337 -#TODO: 'titlematches' => 'Article title matches',
338 -#TODO: 'notitlematches' => 'No page title matches',
339 -#TODO: 'textmatches' => 'Page text matches',
340 -#TODO: 'notextmatches' => 'No page text matches',
341 -#TODO: 'prevn' => "previous $1",
342 -#TODO: 'nextn' => "next $1",
343 -#TODO: 'viewprevnext' => "View ($1) ($2) ($3).",
344 -#TODO: 'showingresults' => "Showing below up to <b>$1</b> results starting with #<b>$2</b>.",
345 -#TODO: 'showingresultsnum' => "Showing below <b>$3</b> results starting with #<b>$2</b>.",
346 -/*#TODO: 'nonefound' => "'''Note''': Unsuccessful searches are
347 -often caused by searching for common words like \"have\" and \"from\",
348 -which are not indexed, or by specifying more than one search term (only pages
349 -containing all of the search terms will appear in the result).",*/
350 -#TODO: 'powersearch' => 'Search',
351 -#TODO: 'powersearchtext' => "Search in namespaces:<br />$1<br />$2 List redirects<br />Search for $3 $9",
352 -#TODO: 'searchdisabled' => '{{SITENAME}} search is disabled. You can search via Google in the meantime. Note that their indexes of {{SITENAME}} content may be out of date.',
353 -
354 -/*#TODO: 'googlesearch' => '
355 -<form method="get" action="http://www.google.com/search" id="googlesearch">
356 - <input type="hidden" name="domains" value="{{SERVER}}" />
357 - <input type="hidden" name="num" value="50" />
358 - <input type="hidden" name="ie" value="$2" />
359 - <input type="hidden" name="oe" value="$2" />
360 -
361 - <input type="text" name="q" size="31" maxlength="255" value="$1" />
362 - <input type="submit" name="btnG" value="$3" />
363 - <div>
364 - <input type="radio" name="sitesearch" id="gwiki" value="{{SERVER}}" checked="checked" /><label for="gwiki">{{SITENAME}}</label>
365 - <input type="radio" name="sitesearch" id="gWWW" value="" /><label for="gWWW">WWW</label>
366 - </div>
367 -</form>',*/
368257 'blanknamespace' => '(მთავარი)',
369258
370259 # Preferences page
371 -'preferences' => 'კონფიგურაცია',
372 -'mypreferences' => 'ჩემი კონფიგურაცია',
373 -#TODO: 'prefsnologin' => 'Not logged in',
374 -#TODO: 'prefsnologintext' => "You must be [[Special:Userlogin|logged in]] to set user preferences.",
375 -#TODO: 'prefsreset' => 'Preferences have been reset from storage.',
376 -'qbsettings' => 'სწრაფი ზოლი',
377 -'changepassword' => 'პაროლის შეცვლა',
378 -#TODO: 'skin' => 'Skin',
379 -#TODO: 'math' => 'Math',
380 -'dateformat' => 'თარიღის ფორმატი',
381 -#TODO: 'datedefault' => 'No preference',
382 -'datetime' => 'თარიღი და დრო',
383 -#TODO: 'math_failure' => 'Failed to parse',
384 -#TODO: 'math_unknown_error' => 'unknown error',
385 -#TODO: 'math_unknown_function' => 'unknown function',
386 -#TODO: 'math_lexing_error' => 'lexing error',
387 -#TODO: 'math_syntax_error' => 'syntax error',
388 -#TODO: 'math_image_error' => 'PNG conversion failed; check for correct installation of latex, dvips, gs, and convert',
389 -#TODO: 'math_bad_tmpdir' => 'Can\'t write to or create math temp directory',
390 -#TODO: 'math_bad_output' => 'Can\'t write to or create math output directory',
391 -#TODO: 'math_notexvc' => 'Missing texvc executable; please see math/README to configure.',
392 -'prefs-personal' => 'მომხმარებლის მონაცემები',
393 -'prefs-rc' => 'ბოლო ცვლილებები',
394 -'prefs-watchlist' => 'კონტროლის სია',
395 -#TODO: 'prefs-watchlist-days' => 'Number of days to show in watchlist:',
396 -#TODO: 'prefs-watchlist-edits' => 'Number of edits to show in expanded watchlist:',
397 -#TODO: 'prefs-misc' => 'Misc',
398 -'saveprefs' => 'შენახვა',
399 -'resetprefs' => 'გადატვირთვა',
400 -'oldpassword' => 'ძველი პაროლი:',
401 -'newpassword' => 'ახალი პაროლი:',
402 -#TODO: 'retypenew' => 'Retype new password:',
403 -'textboxsize' => 'რედაქტირება',
404 -'rows' => 'რიგები:',
405 -'columns' => 'სვეტები:',
 260+'preferences' => 'კონფიგურაცია',
 261+'mypreferences' => 'ჩემი კონფიგურაცია',
 262+'qbsettings' => 'სწრაფი ზოლი',
 263+'changepassword' => 'პაროლის შეცვლა',
 264+'dateformat' => 'თარიღის ფორმატი',
 265+'datetime' => 'თარიღი და დრო',
 266+'prefs-personal' => 'მომხმარებლის მონაცემები',
 267+'prefs-rc' => 'ბოლო ცვლილებები',
 268+'prefs-watchlist' => 'კონტროლის სია',
 269+'saveprefs' => 'შენახვა',
 270+'resetprefs' => 'გადატვირთვა',
 271+'oldpassword' => 'ძველი პაროლი:',
 272+'newpassword' => 'ახალი პაროლი:',
 273+'textboxsize' => 'რედაქტირება',
 274+'rows' => 'რიგები:',
 275+'columns' => 'სვეტები:',
406276 'searchresultshead' => 'ძიება',
407 -#TODO: 'resultsperpage' => 'Hits per page:',
408 -'contextlines' => 'სტრიქონები შედეგის მიხედვით:',
409 -'contextchars' => 'კონტექსტი სტრიქონების მიხედვით:',
410 -#TODO: 'stubthreshold' => 'Threshold for stub display:',
411 -#TODO: 'recentchangescount' => 'Titles in recent changes:',
412 -'savedprefs' => 'თქვენს მიერ შერჩეული პარამეტრები დამახსოვრებულია.',
413 -#TODO: 'timezonelegend' => 'Time zone',
414 -#TODO: 'timezonetext' => 'The number of hours your local time differs from server time (UTC).',
415 -'localtime' => 'ლოკალური დრო',
416 -#TODO: 'timezoneoffset' => 'Offset¹',
417 -#TODO: 'servertime' => 'Server time',
418 -'guesstimezone' => 'ბრაუზერიდან შევსება',
419 -'allowemail' => 'შესაძლებელია ელ. წერილების მიღება სხვა მომხმარებლებისაგან',
420 -'defaultns' => 'სტანდარტული ძიება ამ სახელთა სივრცეებში:',
421 -'default' => 'სტანდარტული',
422 -'files' => 'ფაილები',
 277+'contextlines' => 'სტრიქონები შედეგის მიხედვით:',
 278+'contextchars' => 'კონტექსტი სტრიქონების მიხედვით:',
 279+'savedprefs' => 'თქვენს მიერ შერჩეული პარამეტრები დამახსოვრებულია.',
 280+'localtime' => 'ლოკალური დრო',
 281+'guesstimezone' => 'ბრაუზერიდან შევსება',
 282+'allowemail' => 'შესაძლებელია ელ. წერილების მიღება სხვა მომხმარებლებისაგან',
 283+'defaultns' => 'სტანდარტული ძიება ამ სახელთა სივრცეებში:',
 284+'default' => 'სტანდარტული',
 285+'files' => 'ფაილები',
423286
424 -# User rights
425 -
426287 # Groups
427 -'group' => 'ჯგუფი:',
428 -'group-bot' => 'რობოტები',
429 -'group-sysop' => 'ადმინისტრატორები',
430 -'group-bureaucrat' => 'ბიუროკრატები',
431 -'group-all' => '(ყველა)',
 288+'group' => 'ჯგუფი:',
 289+'group-bot' => 'ბოტები',
 290+'group-sysop' => 'ადმინისტრატორები',
 291+'group-bureaucrat' => 'ბიუროკრატები',
 292+'group-all' => '(ყველა)',
432293
433 -'group-bot-member' => 'რობოტი',
 294+'group-bot-member' => 'ბოტი',
434295 'group-sysop-member' => 'ადმინისტრატორი',
435296 'group-bureaucrat-member' => 'ბიუროკრატი',
436297
437 -'grouppage-bot' => '{{ns:project}}:რობოტები',
438 -'grouppage-sysop' => '{{ns:project}}:ადმინისტრატორები',
 298+'grouppage-bot' => '{{ns:project}}:ბოტები',
 299+'grouppage-sysop' => '{{ns:project}}:ადმინისტრატორები',
439300 'grouppage-bureaucrat' => '{{ns:project}}:ბიუროკრატები',
440301
441302 # Recent changes
442 -'changes' => 'ცვლილებები',
443 -'recentchanges' => 'ბოლო ცვლილებები',
444 -#DONT: 'recentchanges-url' => 'Special:Recentchanges',
445 -#TODO: 'recentchangestext' => 'Track the most recent changes to the wiki on this page.',
446 -#TODO: 'rcnote' => "Below are the last <strong>$1</strong> changes in the last <strong>$2</strong> days, as of $3.",
447 -#TODO: 'rcnotefrom' => "Below are the changes since <b>$2</b> (up to <b>$1</b> shown).",
448 -'rclistfrom' => "ახალი ცვლილებების ჩვენება დაწყებული $1-დან",
 303+'nchanges' => '$1 ცვლილები',
 304+'recentchanges' => 'ბოლო ცვლილებები',
 305+'rclistfrom' => 'ახალი ცვლილებების ჩვენება დაწყებული $1-დან',
449306 'rcshowhideminor' => 'მცირე რედაქტირების $1',
450 -'rcshowhidebots' => 'რობოტების $1',
451 -'rcshowhideliu' => 'რეგისტრირებული მომხმარებლების $1',
 307+'rcshowhidebots' => 'ბოტების $1',
 308+'rcshowhideliu' => 'რეგისტრირებული მომხმარებლების $1',
452309 'rcshowhideanons' => 'ანონიმური მომხმარებლების $1',
453 -#TODO: 'rcshowhidepatr' => '$1 patrolled edits',
454 -'rcshowhidemine' => 'ჩემი რედაქტირების $1',
455 -'rclinks' => "ბოლო $1 ცვლილების ჩვენება უკანასკნელი $2 დღის მანძილზე<br />$3",
456 -'diff' => 'განსხ.',
457 -'hist' => 'ისტ.',
458 -'hide' => 'დამალვა',
459 -'show' => 'ჩვენება',
 310+'rcshowhidemine' => 'ჩემი რედაქტირების $1',
 311+'rclinks' => 'ბოლო $1 ცვლილების ჩვენება უკანასკნელი $2 დღის მანძილზე<br />$3',
 312+'diff' => 'განსხ.',
 313+'hist' => 'ისტ.',
 314+'hide' => 'დამალვა',
 315+'show' => 'ჩვენება',
460316 'minoreditletter' => 'მ',
461 -'newpageletter' => 'ა',
462 -'boteditletter' => 'რ',
463 -'sectionlink' => '→',
464 -#TODO: 'number_of_watching_users_RCview' => '[$1]',
465 -#TODO: 'number_of_watching_users_pageview' => '[$1 watching user/s]',
466 -#TODO: 'rc_categories' => 'Limit to categories (separate with "|")',
467 -#TODO: 'rc_categories_any' => 'Any',
 317+'newpageletter' => 'ა',
 318+'boteditletter' => 'ბ',
 319+'sectionlink' => '→',
468320
 321+# Recent changes linked
 322+'recentchangeslinked' => 'დაკავშირებული ცვლილებები',
 323+
469324 # Upload
 325+'upload' => 'ფაილის დამატება',
 326+'uploadbtn' => 'ფაილის დამატება',
470327
471328 # Image list
472 -'imagelist' => 'ფაილების სია',
473 -'imagelisttext' => "ქვემოთ მოცემულია '''$1''' ფაილის სია დახარისხებული მომხმარებლის $2 მიერ.",
474 -'imagelistforuser' => "აქ მხოლოდ ნაჩვენებია მომხმარებლის $1 მიერ ჩატვირთული სურათები.",
475 -'getimagelist' => 'ფაილთა სიის ჩამოტვირთვა',
476 -'ilsubmit' => 'ძიება',
477 -#TODO: 'showlast' => 'Show last $1 files sorted $2.',
478 -'byname' => 'სახელით',
479 -'bydate' => 'თარიღით',
480 -'bysize' => 'ზომით',
481 -'imgdelete' => 'წაშ.',
482 -'imgdesc' => 'აღწ.',
483 -'imgfile' => 'ფაილი',
484 -#TODO: 'imglegend' => 'Legend: (desc) = show/edit file description.',
485 -'imghistory' => 'ფაილის ისტორია',
486 -#TODO: 'revertimg' => 'rev',
487 -'deleteimg' => 'წაშ.',
488 -#TODO: 'deleteimgcompletely' => 'Delete all revisions of this file',
489 -/*TODO: 'imghistlegend' => 'Legend: (cur) = this is the current file, (del) = delete
490 -this old version, (rev) = revert to this old version.
491 -<br /><i>Click on date to see the file uploaded on that date</i>.',*/
492 -'imagelinks' => 'ბმულები',
493 -'linkstoimage' => 'ამ ფაილზე ბმული მოცემულია შემდეგ გვერდებზე:',
494 -'nolinkstoimage' => 'არ არსებობს ამ ფაილთან დაკავშირებული გვერდები.',
495 -#TODO: 'sharedupload' => 'This file is a shared upload and may be used by other projects.',
496 -'shareduploadwiki' => 'გთხოვთ, იხილოთ $1 შემდგომი ინფორმაციის მისაღებად.',
497 -#TODO: 'shareduploadwiki-linktext' => 'file description page',
498 -#DONT: 'shareddescriptionfollows' => '-',
499 -'noimage' => 'ამ სახელის მქონე ფაილი არ არსებობს, თქვენ შეგიძლიათ $1.',
500 -'noimage-linktext' => 'ფაილის ატვირთვა',
 329+'imagelist' => 'ფაილების სია',
 330+'imagelisttext' => "ქვემოთ მოცემულია '''$1''' ფაილის სია დახარისხებული მომხმარებლის $2 მიერ.",
 331+'imagelistforuser' => 'აქ მხოლოდ ნაჩვენებია მომხმარებლის $1 მიერ ჩატვირთული სურათები.',
 332+'getimagelist' => 'ფაილთა სიის ჩამოტვირთვა',
 333+'ilsubmit' => 'ძიება',
 334+'byname' => 'სახელით',
 335+'bydate' => 'თარიღით',
 336+'bysize' => 'ზომით',
 337+'imgdelete' => 'წაშ.',
 338+'imgdesc' => 'აღწ.',
 339+'imgfile' => 'ფაილი',
 340+'imghistory' => 'ფაილის ისტორია',
 341+'deleteimg' => 'წაშ.',
 342+'imagelinks' => 'ბმულები',
 343+'linkstoimage' => 'ამ ფაილზე ბმული მოცემულია შემდეგ გვერდებზე:',
 344+'nolinkstoimage' => 'არ არსებობს ამ ფაილთან დაკავშირებული გვერდები.',
 345+'shareduploadwiki' => 'გთხოვთ, იხილოთ $1 შემდგომი ინფორმაციის მისაღებად.',
 346+'noimage' => 'ამ სახელის მქონე ფაილი არ არსებობს, თქვენ შეგიძლიათ $1.',
 347+'noimage-linktext' => 'ფაილის ატვირთვა',
501348 'uploadnewversion-linktext' => 'ამ ფაილის ახალი ვერსიის ატვირთვა',
502 -'imagelist_date' => 'თარიღი',
503 -'imagelist_name' => 'სახელი',
504 -'imagelist_user' => 'მომხმარებელი',
505 -'imagelist_size' => 'ზომა (ბაიტები)',
506 -'imagelist_description' => 'აღწერილობა',
507 -'imagelist_search_for' => 'ძიება სურათის სახელის მიხედვით:',
 349+'imagelist_date' => 'თარიღი',
 350+'imagelist_name' => 'სახელი',
 351+'imagelist_user' => 'მომხმარებელი',
 352+'imagelist_size' => 'ზომა (ბაიტები)',
 353+'imagelist_description' => 'აღწერილობა',
 354+'imagelist_search_for' => 'ძიება სურათის სახელის მიხედვით:',
508355
509 -# Mime search
510 -
511 -# Unwatchedpages
512 -
513 -# List redirects
514 -
515 -# Unused templates
516 -
517356 # Random redirect
518357 'randomredirect' => 'ნებისმიერი გადამისამართება',
519358
520359 # Statistics
 360+'statistics' => 'სტატისტიკა',
521361
522362 # Miscellaneous special pages
523 -#
524 -'nbytes' => '$1 ბაიტი',
525 -'ncategories' => '$1 კატეგორია',
526 -'nlinks' => '$1 ბმული',
527 -'nmembers' => '$1 წევრი',
528 -#TODO: 'nrevisions' => '$1 {{PLURAL:$1|revision|revisions}}',
529 -#TODO: 'nviews' => '$1 {{PLURAL:$1|view|views}}',
530 -#TODO: 'specialpage-empty' => 'This page is empty.',
531 -#TODO: 'lonelypages' => 'Orphaned pages',
532 -#'lonelypages-summary' => '',
533 -#TODO: 'lonelypagestext' => 'The following pages are not linked from other pages in this wiki.',
534 -'uncategorizedpages' => 'გვერდები კატეგორიის გარეშე',
535 -#'uncategorizedpages-summary' => '',
536 -'uncategorizedcategories' => 'კატეგორიები კატეგორიის გარეშე',
537 -#'uncategorizedcategories-summary' => '',
538 -'uncategorizedimages' => 'სურათები კატეგორიის გარეშე',
539 -#'uncategorizedimages-summary' => '',
540 -'unusedcategories' => 'გამოუყენებელი კატეგორიები',
541 -'unusedimages' => 'გამოუყენებელი სურათები',
542 -'popularpages' => 'პოპულარული გვერდები',
543 -#'popularpages-summary' => '',
544 -'wantedcategories' => 'მოთხოვნილი კატეგორიები',
545 -#'wantedcategories-summary' => '',
546 -'wantedpages' => 'მოთხოვნილი გვერდები',
547 -#'wantedpages-summary' => '',
548 -#TODO: 'mostlinked' => 'Most linked to pages',
549 -#'mostlinked-summary' => '',
550 -#TODO: 'mostlinkedcategories' => 'Most linked to categories',
551 -#'mostlinkedcategories-summary' => '',
552 -'mostcategories' => 'ყველაზე მეტი კატეგორიის მქონე სტატიები',
553 -#'mostcategories-summary' => '',
554 -#TODO: 'mostimages' => 'Most linked to images',
555 -#'mostimages-summary' => '',
556 -'mostrevisions' => 'ყველაზე მეტად რედაქტირებული სტატიები',
557 -#'mostrevisions-summary' => '',
558 -'allpages' => 'ყველა გვერდი',
559 -#'allpages-summary' => '',
560 -#TODO: 'prefixindex' => 'Prefix index',
561 -#'prefixindex-summary' => '',
562 -'randompage' => 'ნებისმიერი გვერდი',
563 -#DONT: 'randompage-url'=> 'სპეციალური:Random',
564 -'shortpages' => 'მოკლე გვერდები',
565 -#'shortpages-summary' => '',
566 -'longpages' => 'გრძელი გვერდები',
567 -#'longpages-summary' => '',
568 -'deadendpages' => 'ჩიხის გვერდები',
569 -#'deadendpages-summary' => '',
570 -#TODO: 'deadendpagestext' => 'The following pages do not link to other pages in this wiki.',
571 -#TODO: 'protectedpages' => 'Protected pages',
572 -#'protectedpages-summary' => '',
573 -#TODO: 'protectedpagestext' => 'The following pages are protected from moving or editing',
574 -#TODO: 'protectedpagesempty' => 'No pages are currently protected',
575 -'listusers' => 'მომხმარებლების სია',
576 -#'listusers-summary' => '',
577 -'specialpages' => 'სპეციალური გვერდები',
578 -#'specialpages-summary' => '',
579 -'spheading' => 'სპეციალური გვერდები ყველა მომხმარებლისათვის',
580 -'restrictedpheading' => 'შეზღუდული სპეციალური გვერდები',
581 -'recentchangeslinked' => 'დაკავშირებული ცვლილებები',
582 -#TODO: 'rclsub' => "(to pages linked from \"$1\")",
583 -'newpages' => 'ახალი გვერდები',
584 -#'newpages-summary' => '',
585 -'newpages-username' => 'მომხმარებლის სახელი:',
586 -'ancientpages' => 'ხანდაზმული გვერდები',
587 -#'ancientpages-summary' => '',
588 -'intl' => 'ენათშორისი ბმულები',
589 -'move' => 'გადატანა',
590 -'movethispage' => 'ამ გვერდის გადატანა',
591 -'unusedimagestext' => '<p>გთხოვთ გაითვალისწინოთ, რომ შეიძლება სხვა ვიკი ზოგიერთ ამ გამოსახულებას იყენებს.</p>',
592 -#TODO: 'unusedcategoriestext' => 'The following category pages exist although no other article or category make use of them.',
 363+'nbytes' => '$1 ბაიტი',
 364+'ncategories' => '$1 კატეგორია',
 365+'nlinks' => '$1 ბმული',
 366+'nmembers' => '$1 წევრი',
 367+'uncategorizedpages' => 'გვერდები კატეგორიის გარეშე',
 368+'uncategorizedcategories' => 'კატეგორიები კატეგორიის გარეშე',
 369+'uncategorizedimages' => 'სურათები კატეგორიის გარეშე',
 370+'unusedcategories' => 'გამოუყენებელი კატეგორიები',
 371+'unusedimages' => 'გამოუყენებელი სურათები',
 372+'popularpages' => 'პოპულარული გვერდები',
 373+'wantedcategories' => 'მოთხოვნილი კატეგორიები',
 374+'wantedpages' => 'მოთხოვნილი გვერდები',
 375+'mostcategories' => 'ყველაზე მეტი კატეგორიის მქონე სტატიები',
 376+'mostrevisions' => 'ყველაზე მეტად რედაქტირებული სტატიები',
 377+'allpages' => 'ყველა გვერდი',
 378+'randompage' => 'ნებისმიერი გვერდი',
 379+'shortpages' => 'მოკლე გვერდები',
 380+'longpages' => 'გრძელი გვერდები',
 381+'deadendpages' => 'ჩიხის გვერდები',
 382+'listusers' => 'მომხმარებლების სია',
 383+'specialpages' => 'სპეციალური გვერდები',
 384+'spheading' => 'სპეციალური გვერდები ყველა მომხმარებლისათვის',
 385+'restrictedpheading' => 'შეზღუდული სპეციალური გვერდები',
 386+'newpages' => 'ახალი გვერდები',
 387+'newpages-username' => 'მომხმარებლის სახელი:',
 388+'ancientpages' => 'ხანდაზმული გვერდები',
 389+'intl' => 'ენათშორისი ბმულები',
 390+'move' => 'გადატანა',
 391+'movethispage' => 'ამ გვერდის გადატანა',
 392+'unusedimagestext' => '<p>გთხოვთ გაითვალისწინოთ, რომ შეიძლება სხვა ვიკი ზოგიერთ ამ გამოსახულებას იყენებს.</p>',
593393
 394+# Special:Log
 395+'specialloguserlabel' => 'მომხმარებელი:',
 396+'speciallogtitlelabel' => 'სათაური:',
 397+
594398 # Special:Allpages
595399 'nextpage' => 'შემდეგი გვერდი ($1)',
596400 'prevpage' => 'წინა გვერდი ($1)',
597 -'allpagesfrom' => 'გვერდების ჩვენება დაწყებული:',
598 -'allarticles' => 'ყველა სტატია',
599 -'allinnamespace' => 'ყველა გვერდი ($1 სახელთა სივრცეში)',
600 -'allnotinnamespace' => 'ყველა გვერდი ($1 სახელთა სივრცის გარეშე)',
601 -'allpagesprev' => 'წინა',
602 -'allpagesnext' => 'შემდეგი',
603 -'allpagessubmit' => 'ჩვენება',
604 -'allpagesprefix' => 'ასახე გვერდები პრეფიქსით:',
605 -'allpagesbadtitle' => 'მოცემული გვერდის სათაური არასწორია ან აქვს ინტერვიკი ან ნათშორისი პრეფიქსი.
 401+'allpagesfrom' => 'გვერდების ჩვენება დაწყებული:',
 402+'allarticles' => 'ყველა სტატია',
 403+'allinnamespace' => 'ყველა გვერდი ($1 სახელთა სივრცეში)',
 404+'allnotinnamespace' => 'ყველა გვერდი ($1 სახელთა სივრცის გარეშე)',
 405+'allpagesprev' => 'წინა',
 406+'allpagesnext' => 'შემდეგი',
 407+'allpagessubmit' => 'ჩვენება',
 408+'allpagesprefix' => 'ასახე გვერდები პრეფიქსით:',
 409+'allpagesbadtitle' => 'მოცემული გვერდის სათაური არასწორია ან აქვს ინტერვიკი ან ნათშორისი პრეფიქსი.
606410 იგი შესაძლოა შეიცავდეს ერთ ან მეტ სიმბოლოს, რომელიც არ შეიძლება გამოყენებულ იქნას სათაურში.',
607411
608 -# Special:Listusers
609 -
610 -# Email this user
611 -
612412 # Watchlist
 413+'watchlist' => 'ჩემი კონტროლის სია',
 414+'mywatchlist' => 'ჩემი კონტროლის სია',
 415+'watch' => 'კონტროლი',
613416
614 -# Delete/protect/revert
615 -
616 -# restrictions (nouns)
 417+# Restrictions (nouns)
617418 'restriction-edit' => 'რედაქტირება',
618 -#TODO: 'restriction-move' => 'Move',
619419
620 -# restriction levels
621 -
622 -
623420 # Undelete
624 -'undelete' => 'აჩვენე წაშლილი გვერდები',
625 -'undeletepage' => 'იხილეთ და აღადგინეთ წაშლილი გვერდები',
626 -'viewdeletedpage' => 'იხილეთ წაშლილი გვერდები',
627 -'undeletepagetext' => 'მომდევნო გვრდები წაშლილია, მაგრამ ჯერ კიდევ არქივშია და
 421+'undelete' => 'აჩვენე წაშლილი გვერდები',
 422+'undeletepage' => 'იხილეთ და აღადგინეთ წაშლილი გვერდები',
 423+'viewdeletedpage' => 'იხილეთ წაშლილი გვერდები',
 424+'undeletepagetext' => 'მომდევნო გვრდები წაშლილია, მაგრამ ჯერ კიდევ არქივშია და
628425 შესაძლებელია აღდგენა. არქივი შესაძლებელია პერიოდულად გასუფთავდეს.',
629 -'undeleteextrahelp' => "ამ მთლიანი გვერდის აღსადგენად, დატოვეთ ყველა მოსანიშნი უჯრა მოუნიშნავად და
 426+'undeleteextrahelp' => "ამ მთლიანი გვერდის აღსადგენად, დატოვეთ ყველა მოსანიშნი უჯრა მოუნიშნავად და
630427 დააწკაპუნეთ '''''აღდგენა'''''. იმისათვის, რომ მოახდინოთ შერჩევითი აღდგენა მონიშნეთ უჯრები ჩასატარებელი
631428 ვერსიების შესაბამისად და დააწკაპუნეთ '''''აღდგენა'''''. '''''გადატვირთვაზე''''' დაწკაპუნებით გაუქმდება ყველა
632429 კომენტარის ველი და ყველა მოსანიშნი უჯრა.",
633 -'undeletearticle' => 'აღადგინე წაშლილი გვერდი',
634 -'undeleterevisions' => "$1 ვერსიები დაარქივებულია",
635 -'undeletehistory' => 'თუ თქვენ აღადგენთ გვერდს, ყველა ვერსია აღდგება ისტორიაში.
 430+'undeleterevisions' => '$1 ვერსიები დაარქივებულია',
 431+'undeletehistory' => 'თუ თქვენ აღადგენთ გვერდს, ყველა ვერსია აღდგება ისტორიაში.
636432 თუ ახალი გვერდი იგივე სახელით შეიქმნა მისი წაშლის შემდეგ, აღდგენილი
637433 ვერსიები გამოჩნდება წინა ისტორიაში და მიმდინარე ვერსია
638434 ავტომატურად არ ჩანაცვლდება.',
639 -'undeletehistorynoadmin' => 'ეს სტატია წაშლილია. წაშლის მიზეზი ნაჩვენებია მოკლე ანოტაციაში ქვემოთ, იმ
 435+'undeletehistorynoadmin' => 'ეს სტატია წაშლილია. წაშლის მიზეზი ნაჩვენებია მოკლე ანოტაციაში ქვემოთ, იმ
640436 მომხმარებელთა დეტალებთან ერთად ვინც რედაქტირება გაუკეთა ამ გვერდს წაშლის წინ.
641437 იმ წაშლილი ტექსტების აქტუალური ვერსიები მიღწევადია მხოლოდ ადმინისტრატორებისათვის.',
642 -#TODO: 'undelete-revision' => 'Deleted revision of $1 from $2:',
643 -/*#TODO: 'undeleterevision-missing' => "Invalid or missing revision. You may have a bad link, or the
644 -revision may have been restored or removed from the archive.",*/
645 -'undeletebtn' => 'აღდგენა',
646 -'undeletereset' => 'გადატვირთეთ',
647 -'undeletecomment' => 'კომენტარი:',
648 -'undeletedarticle' => 'აღდგენილია "[[$1]]"',
649 -'undeletedrevisions' => "$1 ვერსია აღდგენილია",
650 -'undeletedrevisions-files' => "$1 ვერსია და $2 ფაილი აღდგენილია",
651 -'undeletedfiles' => "$1 ფაილი აღდგენილია",
652 -'cannotundelete' => 'აღდგენა ვერ შედგა; შესაძლოა უკვე ვიღაცამ აღადგინა ეს გვერდი.',
653 -'undeletedpage' => "<big>'''$1 აღდგენილია'''</big>
 438+'undeletebtn' => 'აღდგენა',
 439+'undeletereset' => 'გადატვირთეთ',
 440+'undeletecomment' => 'კომენტარი:',
 441+'undeletedarticle' => 'აღდგენილია "[[$1]]"',
 442+'undeletedrevisions' => '$1 ვერსია აღდგენილია',
 443+'undeletedrevisions-files' => '$1 ვერსია და $2 ფაილი აღდგენილია',
 444+'undeletedfiles' => '$1 ფაილი აღდგენილია',
 445+'cannotundelete' => 'აღდგენა ვერ შედგა; შესაძლოა უკვე ვიღაცამ აღადგინა ეს გვერდი.',
 446+'undeletedpage' => "<big>'''$1 აღდგენილია'''</big>
654447
655448 უკანასკნელი წაშლილთა და აღდგენის სია შეგიძლიათ ნახოთ [[Special:Log/delete|წაშლილთა სიაში]].",
656 -#TODO: 'undelete-header' => 'See [[Special:Log/delete|the deletion log]] for recently deleted pages.',
657 -#TODO: 'undelete-search-box' => 'Search deleted pages',
658 -#TODO: 'undelete-search-prefix' => 'Show pages starting with:',
659 -'undelete-search-submit' => 'ძიება',
660 -#TODO: 'undelete-no-results' => 'No matching pages found in the deletion archive.',
 449+'undelete-search-submit' => 'ძიება',
661450
662451 # Namespace form on various pages
663452 'namespace' => 'სახელთა სივრცე:',
664 -'invert' => 'ყველა, მონიშნულის გარდა',
 453+'invert' => 'ყველა, მონიშნულის გარდა',
665454
666455 # Contributions
 456+'contributions' => 'მომხმარებლის წვლილი',
 457+'mycontris' => 'ჩემი წვლილი',
667458
668459 # What links here
669 -#
670 -'whatlinkshere' => 'სადაა მითითებული ეს გვერდი',
671 -#'whatlinkshere-summary' => '',
672 -#'whatlinkshere-barrow' => '&lt;',
 460+'whatlinkshere' => 'სადაა მითითებული ეს გვერდი',
673461 'notargettitle' => 'სამიზნე არაა',
674 -'notargettext' => 'თქვენ არ მიუთითეთ სამიზნე გვერდი ან მომხმარებელი
 462+'notargettext' => 'თქვენ არ მიუთითეთ სამიზნე გვერდი ან მომხმარებელი
675463 ამ ფუნქციის შესასრულებლად.',
676 -#TODO: 'linklistsub' => '(List of links)',
677 -#TODO: 'linkshere' => "The following pages link to '''[[:$1]]''':",
678 -'nolinkshere' => "'''[[:$1]]'''-ზე ბმული არ არის.",
679 -#TODO: 'isredirect' => 'redirect page',
680 -#TODO: 'istemplate' => 'inclusion',
 464+'nolinkshere' => "'''[[:$1]]'''-ზე ბმული არ არის.",
681465
682 -# Block/unblock IP
683 -
684 -# Developer tools
685 -
686 -# Make sysop
687 -
688466 # Move page
689 -#
690 -'movepage' => 'გვერდის გადატანა',
691 -/*TODO: 'movepagetext' => 'Using the form below will rename a page, moving all
692 -of its history to the new name.
693 -The old title will become a redirect page to the new title.
694 -Links to the old page title will not be changed; be sure to
695 -check for double or broken redirects.
696 -You are responsible for making sure that links continue to
697 -point where they are supposed to go.
698 -
699 -Note that the page will \'\'\'not\'\'\' be moved if there is already
700 -a page at the new title, unless it is empty or a redirect and has no
701 -past edit history. This means that you can rename a page back to where
702 -it was just renamed from if you make a mistake, and you cannot overwrite
703 -an existing page.
704 -
705 -<b>WARNING!</b>
706 -This can be a drastic and unexpected change for a popular page;
707 -please be sure you understand the consequences of this before
708 -proceeding.',*/
709 -/*TODO: 'movepagetalktext' => 'The associated talk page will be automatically moved along with it \'\'\'unless:\'\'\'
710 -*A non-empty talk page already exists under the new name, or
711 -*You uncheck the box below.
712 -
713 -In those cases, you will have to move or merge the page manually if desired.',*/
714 -'movearticle' => 'გვერდის გადატანა',
715 -'movenologin' => 'რეგისტრაცია ვერ გაიარა',
716 -/*TODO: 'movenologintext' => "You must be a registered user and [[Special:Userlogin|logged in]]
717 -to move a page.",*/
718 -'newtitle' => 'ახალი სათაური',
719 -#TODO: 'move-watch' => 'Watch this page',
720 -'movepagebtn' => 'გვერდის გადატანა',
721 -#TODO: 'pagemovedsub' => 'Move succeeded',
722 -'pagemovedtext' => "გვერდი \"[[$1]]\" გადავიდა \"[[$2]]\".",
723 -'articleexists' => 'ამ დასახელების გვერდი უკვე არსებობს,
 467+'movepage' => 'გვერდის გადატანა',
 468+'movearticle' => 'გვერდის გადატანა',
 469+'movenologin' => 'რეგისტრაცია ვერ გაიარა',
 470+'newtitle' => 'ახალი სათაური',
 471+'move-watch' => 'ამ გვერდის კონტროლი',
 472+'movepagebtn' => 'გვერდის გადატანა',
 473+'pagemovedtext' => 'გვერდი "[[$1]]" გადავიდა "[[$2]]".',
 474+'articleexists' => 'ამ დასახელების გვერდი უკვე არსებობს,
724475 ან თქვენს მიერ მითითებული დასახელება არასწორია.
725476 თუ შეიძლება, მიუთითეთ სხვა სახელი.',
726 -#TODO: 'talkexists' => "'''The page itself was moved successfully, but the talk page could not be moved because one already exists at the new title. Please merge them manually.'''",
727 -'movedto' => 'გადატანილია',
728 -'movetalk' => 'დაკავშირებული განხილვის გადატანა',
729 -#TODO: 'talkpagemoved' => 'The corresponding talk page was also moved.',
730 -#TODO: 'talkpagenotmoved' => 'The corresponding talk page was <strong>not</strong> moved.',
731 -'1movedto2' => '[[$1]] გადატანილია [[$2]]-ზე',
732 -'1movedto2_redir' => '[[$1]] გადატანილია [[$2]]-ზე გადამისამართებულ გვერდში',
733 -'movelogpage' => 'გადატანის ჟურნალი',
734 -#TODO: 'movelogpagetext' => 'Below is a list of page moved.',
735 -'movereason' => 'მიზეზი',
736 -#TODO: 'revertmove' => 'revert',
737 -'delete_and_move' => 'წაშლა და გადატანა',
738 -'delete_and_move_text' =>
739 -'==საჭიროა წაშლა==
 477+'movedto' => 'გადატანილია',
 478+'movetalk' => 'დაკავშირებული განხილვის გადატანა',
 479+'1movedto2' => '[[$1]] გადატანილია [[$2]]-ზე',
 480+'1movedto2_redir' => '[[$1]] გადატანილია [[$2]]-ზე გადამისამართებულ გვერდში',
 481+'movelogpage' => 'გადატანის ჟურნალი',
 482+'movereason' => 'მიზეზი',
 483+'delete_and_move' => 'წაშლა და გადატანა',
 484+'delete_and_move_text' => '==საჭიროა წაშლა==
740485
741486 სტატია დასახელებით "[[$1]]" უკვე არსებობს. გსურთ მისი წაშლა გადატანისთვის ადგილის დასათმობად?',
742487 'delete_and_move_confirm' => 'დიახ, წაშალეთ ეს გვერდი',
743 -'delete_and_move_reason' => 'წაშლილია გადატანისთვის ადგილის დასათმობად',
744 -#TODO: 'selfmove' => "Source and destination titles are the same; can't move a page over itself.",
745 -#TODO: 'immobile_namespace' => "Source or destination title is of a special type; cannot move pages from and into that namespace.",*/
 488+'delete_and_move_reason' => 'წაშლილია გადატანისთვის ადგილის დასათმობად',
746489
747 -# Export
748 -
749490 # Namespace 8 related
750 -
751 -'allmessages' => 'სისტემური შეტყობინება',
752 -'allmessagesname' => 'დასახელება',
753 -'allmessagesdefault' => 'სტანდარტული ტექსტი',
754 -'allmessagescurrent' => 'მიმდინარე ტექსტი',
755 -'allmessagestext' => 'ეს არის სახელთა სივრცე მედიავიკიში არსებული სისტემური შეტყობინებების ჩამონათვალი.',
 491+'allmessages' => 'სისტემური შეტყობინება',
 492+'allmessagesname' => 'დასახელება',
 493+'allmessagesdefault' => 'სტანდარტული ტექსტი',
 494+'allmessagescurrent' => 'მიმდინარე ტექსტი',
 495+'allmessagestext' => 'ეს არის სახელთა სივრცე მედიავიკიში არსებული სისტემური შეტყობინებების ჩამონათვალი.',
756496 'allmessagesnotsupportedUI' => 'თქვენს ამჟამინდელ ინტერფეისის ენას <b>$1</b> არ აქვს სპეციალური:AllMessages-ის უზრუნველყოფა ამ საიტზე.',
757497 'allmessagesnotsupportedDB' => 'სპეციალური:AllMessages-ის უზრუნველყოფა არ ხდება, ვინაიდან wgUseDatabaseMessages გამორთულია.',
758 -'allmessagesfilter' => 'ფილტრი შეტყობინების სახელის მიხედვით:',
759 -'allmessagesmodified' => 'აჩვენე მხოლოდ შეცვლილი',
 498+'allmessagesfilter' => 'ფილტრი შეტყობინების სახელის მიხედვით:',
 499+'allmessagesmodified' => 'აჩვენე მხოლოდ შეცვლილი',
760500
761501 # Thumbnails
762 -'thumbnail-more' => 'გაზარდეთ',
763 -#TODO: 'missingimage' => '<b>Missing image</b><br /><i>$1</i>',
764 -'filemissing' => 'ფაილი ვერ მოიძებნა',
765 -'thumbnail_error' => 'ესკიზის შექმნის შეცდომა: $1',
 502+'thumbnail-more' => 'გაზარდეთ',
 503+'filemissing' => 'ფაილი ვერ მოიძებნა',
 504+'thumbnail_error' => 'ესკიზის შექმნის შეცდომა: $1',
766505
767 -# Special:Import
768 -
769 -# import log
770 -
771 -# tooltip help for some actions, most are in Monobook.js
772 -
773 -# stylesheets
774 -
775 -# Metadata
776 -
777506 # Attribution
778507 'anonymous' => '{{SITENAME}}-ის ანონიმური მომხმარებლები',
779 -'siteuser' => '{{SITENAME}} მომხმარებელი $1',
780 -#TODO: 'lastmodifiedatby' => 'This page was last modified $2, $1 by $3.', // $1 date, $2 time. $3 user
781 -'and' => 'და',
782 -#TODO: 'othercontribs' => 'Based on work by $1.',
783 -'others' => 'სხვები',
 508+'siteuser' => '{{SITENAME}} მომხმარებელი $1',
 509+'and' => 'და',
 510+'others' => 'სხვები',
784511 'siteusers' => '{{SITENAME}} მომხმარებლები $1',
785 -#TODO: 'creditspage' => 'წვლილი',
786 -#TODO: 'nocredits' => 'There is no credits info available for this page.',
787512
788 -# Spam protection
789 -
790 -# Info page
791 -
792 -# Math options
793 -
794 -# Patrolling
795 -
796 -# Monobook.js: tooltips and access keys for monobook
797 -
798 -# image deletion
799 -
800 -# browsing diffs
801 -
802 -# labels for User: and Title: on Special:Log pages
803 -'specialloguserlabel' => 'მომხმარებელი:',
804 -'speciallogtitlelabel' => 'სათაური:',
805 -
806513 'passwordtooshort' => 'თქვენი პაროლი ძალიან მოკლეა. მასში უნდა შედიოდეს არანაკლებ $1 ასო-ნიშანი.',
807514
808 -# Media Warning
809 -#TODO: 'mediawarning' => '\'\'\'Warning\'\'\': This file may contain malicious code, by executing it your system may be compromised.<hr />',
810 -
811 -'fileinfo' => '$1KB, MIME ტიპი: <code>$2</code>',
812 -
813 -# Metadata
814 -
815 -# Exif tags
816 -
817 -# Make & model, can be wikified in order to link to the camera and model name
818 -
819 -# Exif attributes
820 -
821 -# external editor support
822 -
823515 # 'all' in various places, this might be different for inflected languages
824516 'recentchangesall' => 'ყველა',
825 -'imagelistall' => 'ყველა',
826 -'watchlistall1' => 'ყველა',
827 -'watchlistall2' => 'ყველა',
828 -'namespacesall' => 'ყველა',
 517+'imagelistall' => 'ყველა',
 518+'watchlistall1' => 'ყველა',
 519+'watchlistall2' => 'ყველა',
 520+'namespacesall' => 'ყველა',
829521
830 -# E-mail address confirmation
831 -
832522 # Inputbox extension, may be useful in other contexts as well
833 -'tryexact' => 'სცადეთ ზუსტი ძიება',
 523+'tryexact' => 'სცადეთ ზუსტი ძიება',
834524 'searchfulltext' => 'სრული ტექსტის ძიება',
835 -'createarticle' => 'სტატიის შექმნა',
 525+'createarticle' => 'სტატიის შექმნა',
836526
837 -# Scary transclusion
 527+# Delete conflict
 528+'deletedwhileediting' => "[[მომხმარებელი:$1|$1]] მომხმარებელმა ([[მომხმარებელი განხილვა:$1|განხილვა]]) წაშალა თქვენი რედაქტირების შემდეგ. მიზეზი:
 529+: ''$2''
 530+გთხოვთ დაადასტუროთ რომ ნამდვილად გსურთ ამ გვერდის თავიდან შექმნა.",
838531
839 -# Trackbacks
840 -
841 -# delete conflict
842 -
843 -'deletedwhileediting' => '[[მომხმარებელი:$1|$1]] მომხმარებელმა ([[მომხმარებელი განხილვა:$1|განხილვა]]) წაშალა თქვენი რედაქტირების შემდეგ. მიზეზი:
844 -: \'\'$2\'\'
845 -გთხოვთ დაადასტუროთ რომ ნამდვილად გსურთ ამ გვერდის თავიდან შექმნა.',
846 -#TODO: 'recreate' => 'Recreate',
847 -
848 -#TODO: 'unit-pixel' => 'px',
849 -
850532 # HTML dump
 533+'redirectingto' => 'გადამისამართდება [[$1]]-ზე...',
851534
852535 # action=purge
853 -#TODO: 'confirm_purge' => "Clear the cache of this page?\n\n$1",
854 -#TODO: 'confirm_purge_button' => 'OK',
 536+'confirm_purge' => 'გსურთ ამ გვერდის ქეშის წაშლა? $1',
855537
856 -'youhavenewmessagesmulti' => "თქვენ გაქვთ ახალი შეტყობინება $1-ზე",
 538+'youhavenewmessagesmulti' => 'თქვენ გაქვთ ახალი შეტყობინება $1-ზე',
857539
858 -#TODO: 'searchcontaining' => "Search for articles containing ''$1''.",
859 -#TODO: 'searchnamed' => "Search for articles named ''$1''.",
860540 'articletitles' => "სტატიები დაწყებული ''$1''-ით",
861 -'hideresults' => 'შედეგების დამალვა',
 541+'hideresults' => 'შედეგების დამალვა',
862542
863543 # DISPLAYTITLE
864544 'displaytitle' => '(ამ გვერდის ბმული როგორც [[$1]])',
865545
866546 'loginlanguagelabel' => 'ენა: $1',
867547
868 -# Multipage image navigation
869 -
870548 # Table pager
871 -#TODO: 'ascending_abbrev' => 'asc',
872 -#TODO: 'descending_abbrev' => 'desc',
873 -'table_pager_next' => 'შემდეგი გვერდი',
874 -'table_pager_prev' => 'წინა გვერდი',
875 -'table_pager_first' => 'პირველი გვერდი',
876 -'table_pager_last' => 'ბოლო გვერდი',
877 -#TODO: 'table_pager_limit' => 'Show $1 items per page',
 549+'table_pager_next' => 'შემდეგი გვერდი',
 550+'table_pager_prev' => 'წინა გვერდი',
 551+'table_pager_first' => 'პირველი გვერდი',
 552+'table_pager_last' => 'ბოლო გვერდი',
878553 'table_pager_limit_submit' => 'აჩვენე',
879 -'table_pager_empty' => 'შედეგები არაა',
 554+'table_pager_empty' => 'შედეგები არაა',
880555
881556 # Auto-summaries
882 -'autosumm-blank' => 'გვერდი დაცარიელდა',
883 -'autosumm-replace' => 'შინაარსი შეიცვალა \'$1\'-ით',
884 -'autoredircomment' => 'გადამისამართება [[$1]]-ზე', # This should be changed to the new naming convention, but existed beforehand.
885 -'autosumm-new' => 'ახალი გვერდი: $1',
 557+'autosumm-blank' => 'გვერდი დაცარიელდა',
 558+'autosumm-replace' => "შინაარსი შეიცვალა '$1'-ით",
 559+'autoredircomment' => 'გადამისამართება [[$1]]-ზე', # This should be changed to the new naming convention, but existed beforehand
 560+'autosumm-new' => 'ახალი გვერდი: $1',
886561
887562 # Size units
888 -'size-bytes' => '$1 ბ',
 563+'size-bytes' => '$1 ბ',
889564 'size-kilobytes' => '$1 კბ',
890565 'size-megabytes' => '$1 მბ',
891566 'size-gigabytes' => '$1 გბ',
892567
893 -
894568 );
895569
896570 ?>
Index: branches/liquidthreads/languages/messages/MessagesZh_classical.php
@@ -29,7 +29,10 @@
3030 * The special key "default" is an alias for either dmy or mdy depending on
3131 * $wgAmericanDates
3232 */
33 -$datePreferences = false;
 33+$datePreferences = array(
 34+ 'default',
 35+ 'ISO 8601',
 36+);
3437
3538 $defaultDateFormat = 'zh';
3639
@@ -43,12 +46,27 @@
4447 */
4548 $dateFormats = array(
4649 'zh time' => 'H時i分',
47 - 'zh date' => 'Y年Fj日',
48 - 'zh both' => 'Y年Fj日H時i分',
 50+ 'zh date' => 'Y年n月j日 (l)',
 51+ 'zh both' => 'Y年n月j日 (D) H時i分',
4952 );
5053
5154 $linkTrail = '/^([a-z]+)(.*)$/sD';
5255
 56+$digitTransformTable = array(
 57+ '0' => '〇',
 58+ '1' => '一',
 59+ '2' => '二',
 60+ '3' => '三',
 61+ '4' => '四',
 62+ '5' => '五',
 63+ '6' => '六',
 64+ '7' => '七',
 65+ '8' => '八',
 66+ '9' => '九',
 67+ '.' => '點',
 68+ ',' => '',
 69+);
 70+
5371 #-------------------------------------------------------------------
5472 # Default messages
5573 #-------------------------------------------------------------------
@@ -151,7 +169,8 @@
152170 'dec' => '十二月',
153171
154172 # Bits of text used by many pages
155 -'categories' => '$1類',
 173+'categories' => '類',
 174+'pagecategories' => '$1類',
156175 'category_header' => '"$1"文',
157176 'subcategories' => '次類',
158177
@@ -214,7 +233,6 @@
215234 'specialpage' => '奇頁',
216235 'personaltools' => '家私',
217236 'postcomment' => '贊',
218 -'addsection' => '新議',
219237 'articlepage' => '閱內文',
220238 'talk' => '議',
221239 'views' => '覽',
@@ -381,10 +399,9 @@
382400 'yourlanguage' => '語',
383401 'yournick' => '暱名',
384402 'email' => '電郵',
385 -'prefs-help-email-enotif' => '信遣此',
386 -'prefs-help-realname' => '*本名,可略: if you choose to provide it this will be used for giving you attribution for your work.',
 403+'prefs-help-realname' => '本名可略也。if you choose to provide it this will be used for giving you attribution for your work.',
387404 'loginerror' => '登簿有誤',
388 -'prefs-help-email' => '*電郵,可略:以此通他人,或於共議處匿論',
 405+'prefs-help-email' => '電郵可略也。以此通他人,或於共議處匿論。',
389406 'nocookiesnew' => '簿已增而未登。登簿{{SITENAME}}須cookies,請釋之後登。',
390407 'nocookieslogin' => '登簿{{SITENAME}}須cookies,請釋之後登。',
391408 'noname' => '簿名缺',
@@ -447,8 +464,13 @@
448465 'missingcommenttext' => '請贊之',
449466 'blockedtitle' => '子見禁',
450467 'blockedtext' => "<big>'''子名、IP見禁。'''</big>
451 -禁者$1也, 因''$2''故,存惑可詢$1,或[[{{ns:project}}:Administrators|有秩]],[[Special:Preferences|簿註]]無驛則信不遣。
452 -另,子IP為$3,詢時切附之。",
 468+
 469+禁者$1也,因''$2''故。
 470+
 471+終止之時為:$6
 472+
 473+存惑可詢$1,或[[{{ns:project}}:Administrators|有秩]],[[Special:Preferences|簿註]]無驛則信不遣。
 474+另,子IP為$3,其簿名為#$5。詢時切附之。",
453475 'blockedoriginalsource' => "'''$1'''本源如下:",
454476 'blockededitsource' => "子'''$1纂文'''如下:",
455477 'whitelistedittitle' => '登簿以纂',
@@ -557,7 +579,7 @@
558580 'revisiondelete' => '刪、還審',
559581 'revdelete-nooldid-title' => '無此審。',
560582 'revdelete-nooldid-text' => '審未擇,不可為之。',
561 -'revdelete-selected' => '審[[:$1]]已擇。',
 583+'revdelete-selected' => '審[[:$1]]已擇$2。',
562584 'revdelete-text' => '刪審雖見誌,其文摒公眾,惟有秩可得之。無規則有秩可復還焉。',
563585 'revdelete-legend' => '審,規之以:',
564586 'revdelete-hide-text' => '藏審文',
@@ -784,6 +806,7 @@
785807 'wantedpages' => '缺頁',
786808 'mostrevisions' => '首審之文',
787809 'allpages' => '全頁',
 810+'randompage' => '清風翻書',
788811 'shortpages' => '短頁',
789812 'longpages' => '長頁',
790813 'listusers' => '盡列有簿',
@@ -794,8 +817,8 @@
795818 'newpages-username' => '名:',
796819 'ancientpages' => '舊頁',
797820 'intl' => '通他語',
798 -'move' => '搬',
799 -'movethispage' => '搬此頁',
 821+'move' => '遷',
 822+'movethispage' => '遷此頁',
800823
801824 'categoriespagetext' => '大典有門:',
802825 'version' => '版',
@@ -928,13 +951,13 @@
929952 'protectsub' => '(正錮"$1")',
930953 'confirmprotecttext' => '篤欲錮之?',
931954 'confirmprotect' => '准錮',
932 -'protectmoveonly' => '惟限搬之',
 955+'protectmoveonly' => '惟限遷之',
933956 'protectcomment' => '錮之有由:',
934957 'unprotectsub' => '(正赦"$1")',
935958 'confirmunprotecttext' => '篤欲赦之?',
936959 'confirmunprotect' => '准赦',
937960 'unprotectcomment' => '赦之有由:',
938 -'protect-unchain' => '准搬之',
 961+'protect-unchain' => '准遷之',
939962 'protect-text' => '錮級可見<strong>$1</strong>',
940963 'protect-default' => '(予定)',
941964 'protect-level-autoconfirmed' => '驅無簿',
@@ -942,7 +965,7 @@
943966
944967 # Restrictions (nouns)
945968 'restriction-edit' => '纂',
946 -'restriction-move' => '搬',
 969+'restriction-move' => '遷',
947970
948971 # Undelete
949972 'undelete' => '覽已刪',
@@ -968,7 +991,7 @@
969992 # Contributions
970993 'contributions' => '功績',
971994 'mycontris' => '吾績',
972 -'contribsub2' => '就$1',
 995+'contribsub2' => '$1之功績 ($2)',
973996 'nocontribs' => '尺斯無易',
974997 'ucnote' => '近<b>$2</b>有<b>$1</b>新易。',
975998 'uclinks' => ' 近$1易,近$2日',
@@ -996,12 +1019,12 @@
9971020 'ipaddress' => 'IP址',
9981021 'ipadressorusername' => 'IP或簿名',
9991022 'ipbexpiry' => '限期',
1000 -'ipbreason' => '綠',
 1023+'ipbreason' => '緣',
10011024 'ipbanononly' => '禁名匿',
10021025 'ipbcreateaccount' => '禁增簿',
10031026 'ipbsubmit' => '禁此簿',
10041027 'ipbother' => '它時',
1005 -'ipboptions' => '2 hours:二時,1 day:一日,3 days:三日,1 week:一週,2 weeks:二週,1 month:一月,3 months:三月,6 months:六月,1 year:一年,infinite:永',
 1028+'ipboptions' => '二時:2 hours,一日:1 day,三日:3 days,一週:1 week,二週:2 weeks,一月:1 month,三月:3 months,六月:6 months,一年:1 year,永:infinite',
10061029 'ipbotheroption' => '他',
10071030 'badipaddress' => 'IP不格',
10081031 'blockipsuccesssub' => '見禁',
@@ -1016,13 +1039,13 @@
10171040 'expiringblock' => '過$1',
10181041 'anononlyblock' => '惟名匿',
10191042 'createaccountblock' => '禁增簿',
1020 -'ipblocklistempty' => '誌空也。',
 1043+'ipblocklist-empty' => '誌空也。',
10211044 'blocklink' => '禁',
10221045 'unblocklink' => '赦',
10231046 'contribslink' => '功績',
10241047 'autoblocker' => '近日"[[User:$1|$1]]"用子IP"\'\'\'$2\'\'\'",故禁',
10251048 'blocklogpage' => '誌禁',
1026 -'blocklogentry' => '禁"[[$1]]"至$2',
 1049+'blocklogentry' => '禁"[[$1]]"至$2 $3',
10271050 'unblocklogentry' => '赦$1',
10281051 'ipb_expiry_invalid' => '限期不格。',
10291052 'ipb_already_blocked' => '"$1"早禁矣',
@@ -1159,8 +1182,10 @@
11601183 'confirmemail_noemail' => '[[Special:Preferences|簿註]]有驛。',
11611184 'confirmemail_send' => '遣核符',
11621185 'confirmemail_sent' => '核符遣矣',
1163 -'confirmemail_sendfailed' => '信未遣焉,請核郵驛。',
 1186+'confirmemail_sendfailed' => '信未遣焉,請核郵驛。
11641187
 1188+郵者覆之:$1',
 1189+
11651190 # Inputbox extension, may be useful in other contexts as well
11661191 'tryexact' => '查全合',
11671192 'searchfulltext' => '尋全文',
@@ -1202,7 +1227,8 @@
12031228 'imgmultipageprev' => '←前頁',
12041229 'imgmultipagenext' => '次頁→',
12051230 'imgmultigo' => '往',
1206 -'imgmultigotopre' => '往',
 1231+'imgmultigotopre' => '往第',
 1232+'imgmultigotopost' => '頁',
12071233
12081234 # Table pager
12091235 'ascending_abbrev' => '升冪',
Index: branches/liquidthreads/languages/messages/MessagesHe.php
@@ -1504,17 +1504,18 @@
15051505 'sp-newimages-showfrom' => 'הצג תמונות חדשות החל מ־$1',
15061506
15071507 # What links here
1508 -'whatlinkshere' => 'דפים המקושרים לכאן',
1509 -'notargettitle' => 'אין דף מטרה',
1510 -'notargettext' => 'לא ציינתם דף מטרה או משתמש לגביו תבוצע פעולה זו.',
1511 -'linklistsub' => '(רשימת קישורים)',
1512 -'linkshere' => "הדפים שלהלן מקושרים לדף '''[[:$1]]''':",
1513 -'nolinkshere' => "אין דפים המקושרים לדף '''[[:$1]]'''.",
1514 -'nolinkshere-ns' => "אין דפים המקושרים לדף '''[[:$1]]''' במרחב השם שנבחר.",
1515 -'isredirect' => 'דף הפניה',
1516 -'istemplate' => 'הכללה',
1517 -'whatlinkshere-prev' => '{{plural:$1|הקודם|$1 הקודמים}}',
1518 -'whatlinkshere-next' => '{{plural:$1|הבא|$1 הבאים}}',
 1508+'whatlinkshere' => 'דפים המקושרים לכאן',
 1509+'notargettitle' => 'אין דף מטרה',
 1510+'notargettext' => 'לא ציינתם דף מטרה או משתמש לגביו תבוצע פעולה זו.',
 1511+'linklistsub' => '(רשימת קישורים)',
 1512+'linkshere' => "הדפים שלהלן מקושרים לדף '''[[:$1]]''':",
 1513+'nolinkshere' => "אין דפים המקושרים לדף '''[[:$1]]'''.",
 1514+'nolinkshere-ns' => "אין דפים המקושרים לדף '''[[:$1]]''' במרחב השם שנבחר.",
 1515+'isredirect' => 'דף הפניה',
 1516+'istemplate' => 'הכללה',
 1517+'whatlinkshere-prev' => '{{plural:$1|הקודם|$1 הקודמים}}',
 1518+'whatlinkshere-next' => '{{plural:$1|הבא|$1 הבאים}}',
 1519+'whatlinkshere-links' => '→ קישורים',
15191520
15201521 # Block/unblock
15211522 'blockip' => 'חסימת משתמש',
Index: branches/liquidthreads/languages/messages/MessagesZh_cn.php
@@ -1401,6 +1401,7 @@
14021402 'nolinkshere-ns' => '在所选的名字空间内没有页面链接到[[:$1]]。',
14031403 'isredirect' => '重定向页',
14041404 'istemplate' => '包含',
 1405+'whatlinkshere-links' => '(←链入页面)',
14051406 'whatlinkshere-prev' => '前$1个',
14061407 'whatlinkshere-next' => '后$1个',
14071408
Index: branches/liquidthreads/languages/messages/MessagesId.php
@@ -1529,7 +1529,7 @@
15301530 'istemplate' => 'dengan templat',
15311531 'whatlinkshere-prev' => '$1 sebelumnya',
15321532 'whatlinkshere-next' => '$1 selanjutnya',
1533 -'whatlinkshere-links' => '(← pranala)',
 1533+'whatlinkshere-links' => '← pranala',
15341534
15351535 # Block/unblock
15361536 'blockip' => 'Blokir pengguna',
Index: branches/liquidthreads/languages/messages/MessagesMy.php
@@ -0,0 +1,205 @@
 2+<?php
 3+/** Burmese (Myanmasa)
 4+ *
 5+ * @addtogroup Language
 6+ */
 7+
 8+$messages = array(
 9+# Dates
 10+'sunday' => 'တ​နင်္ဂ​နွေ​',
 11+'monday' => 'တ​နင်္လာ​',
 12+'tuesday' => 'အင်္ဂါ​',
 13+'wednesday' => 'ဗုဒ္ဒ​ဟူး​',
 14+'thursday' => 'ကြာ​သာ​ပ​တေး​',
 15+'friday' => 'သော​ကြာ​',
 16+'saturday' => 'စ​နေ​',
 17+'january' => 'ဇန်​န​ဝါ​ရီ​',
 18+'february' => 'ဖေ​ဖော်​ဝါ​ရီ​',
 19+'march' => 'မတ်​',
 20+'april' => 'ဧ​ပြီ​',
 21+'may_long' => 'မေ​',
 22+'june' => 'ဇွန်​',
 23+'july' => 'ဇူ​လိုင်​',
 24+'august' => 'ဩ​ဂုတ်​',
 25+'september' => 'စက်​တင်​ဘာ​',
 26+'october' => 'အောက်​တို​ဘာ​',
 27+'november' => 'နို​ဝင်​ဘာ​',
 28+'december' => 'ဒီ​ဇင်​ဘာ​',
 29+'january-gen' => 'ဇန်​န​ဝါ​ရီ​',
 30+'february-gen' => 'ဖေ​ဖော်​ဝါ​ရီ​',
 31+'march-gen' => 'မတ်​',
 32+'april-gen' => 'ဧ​ပြီ​',
 33+'may-gen' => 'မေ​',
 34+'june-gen' => 'ဇွန်​',
 35+'july-gen' => 'ဇူ​လိုင်​',
 36+'august-gen' => 'ဩ​ဂုတ်​',
 37+'september-gen' => 'စက်​တင်​ဘာ​',
 38+'october-gen' => 'အောက်​တို​ဘာ​',
 39+'november-gen' => 'နို​ဝင်​ဘာ​',
 40+'december-gen' => 'ဒီ​ဇင်​ဘာ​',
 41+'may' => 'မေ​',
 42+
 43+'cancel' => 'မ​လုပ်​တော့​ပါ​',
 44+'qbedit' => 'ပြင်​ဆင်​ရန်​',
 45+'qbspecialpages' => 'အ​ထူး​စာ​မျက်​နှာ​',
 46+'mytalk' => 'ကျွန်​တော့​ပြော​ရေး​ဆို​ရာ​',
 47+'navigation' => 'အ​ညွှန်း​',
 48+
 49+'help' => 'အ​ကူ​အ​ညီ​',
 50+'search' => 'ရှာ​ဖွေ​ရန်​',
 51+'searchbutton' => 'ရှာ​ဖွေ​ရန်​',
 52+'go' => 'သွား​ပါ​',
 53+'history_short' => 'မှတ်​တမ်း​',
 54+'printableversion' => 'ပ​ရင်​တာ​ထုတ်​ရန်​',
 55+'permalink' => 'ပုံ​သေ​လိပ်​စာ​',
 56+'edit' => 'ပြင်​ဆင်​ရန်​',
 57+'delete' => 'ဖျက်​ပါ​',
 58+'protect' => 'ထိမ်း​သိမ်း​ပါ​',
 59+'talk' => 'ပြော​ရေး​ဆို​ရာ​',
 60+'toolbox' => 'တန်​ဆာ​ပ​လာ​',
 61+'otherlanguages' => 'အ​ခြား​ဘာ​သာ​ဖြင့်​',
 62+'jumptonavigation' => 'အ​ညွှန်း​',
 63+'jumptosearch' => 'ရှာ​ဖွေ​ရန်​',
 64+
 65+# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage) and the disambiguation template definition (see disambiguations).
 66+'currentevents' => 'လက်​ရှိ​လုပ်​ငန်း​များ​',
 67+'currentevents-url' => 'လက်​ရှိ​လုပ်​ငန်း​များ​',
 68+'disclaimers' => 'သ​တိ​ပေး​ချက်​များ​',
 69+'edithelp' => 'ပြင်​ဆင်​ခြင်း​အ​ကူ​အ​ညီ​',
 70+'mainpage' => 'ဗ​ဟို​စာ​မျက်​နှာ​',
 71+'portal' => 'ပြော​ရေး​ဆို​ရာ​',
 72+'sitesupport' => 'လှု​ဒါန်း​မှု​',
 73+
 74+'newmessageslink' => 'သ​တင်း​အ​သစ်​',
 75+'editsection' => 'ပြင်​ဆင်​ရန်​',
 76+'editold' => 'ပြင်​ဆင်​ရန်​',
 77+
 78+# Short words for each namespace, by default used in the 'article' tab in monobook
 79+'nstab-main' => 'စာ​မျက်​နှာ​',
 80+'nstab-user' => 'မှတ်​ပုံ​တင်​အ​သုံး​ပြု​သူ​၏​စာ​မျက်​နှာ​',
 81+
 82+# General errors
 83+'viewsource' => 'ဆို့​ကို​ပြ​ပါ​',
 84+
 85+# Login and logout pages
 86+'welcomecreation' => 'မင်္ဂ​လာ​ပါ​ $1။ သင့်​အား​မှတ်​ပုံ​တင်​ပြီး​ပါ​ပြီ။​ ဝီ​ကီ​အ​တွက်​သင့်​စိတ်​ကြိုက်​များ​ကို​ရွေး​ချယ်​နိုင်​ပါ​သည်။​',
 87+'yourname' => 'မှတ်​ပုံ​တင်​အ​မည်:',
 88+'yourpassword' => 'လှို့​ဝှက်​စ​ကား​လုံး:',
 89+'yourpasswordagain' => 'ပြန်​ရိုက်​ပါ:',
 90+'remembermypassword' => 'ဤ​ကွန်​ပျူ​တာ​တွင်​ကျွန်​တော့​ကို​မှတ်​ထား​ပါ​',
 91+'login' => 'မှတ်​ပုံ​တင်​ဖြင့်​ဝင်​ပါ​',
 92+'logout' => 'ထွက်​ပါ​',
 93+'userlogout' => 'ထွက်​ပါ​',
 94+'notloggedin' => 'မှတ်​ပုံ​တင်​ဖြင့်​မ​ဝင်​ရ​သေး​ပါ​',
 95+'createaccount' => 'မှတ်​ပုံ​တင်​ပြု​လုပ်​ပါ​',
 96+'gotaccountlink' => 'မှတ်​ပုံ​တင်​ဖြင့်​ဝင်​ပါ​',
 97+'youremail' => 'အီ​မေး:',
 98+'username' => 'မှတ်​ပုံ​တင်​အ​မည်:',
 99+'email' => 'အီ​မေး​',
 100+
 101+# Edit pages
 102+'summary' => 'အ​ကျဉ်း​ချုပ်​',
 103+'minoredit' => 'သာ​မန်​ပြင်​ဆင်​မှု​ဖြစ်​ပါ​သည်​',
 104+'watchthis' => 'ဤ​စာ​မျက်​နှာ​အား​စောင့်​ကြည့်​ပါ​',
 105+'savearticle' => 'သိမ်း​ပါ​',
 106+'showpreview' => 'န​မူ​နာ​ပြ​ပါ​',
 107+'showdiff' => 'ပြင်​ဆင်​ထား​သည်​များ​ကို​ပြ​ပါ​',
 108+'whitelistedittitle' => 'ပြင်​ဆင်​ခြင်း​သည်​မှတ်​ပုံ​တင်​ရန်​လို​သည်​',
 109+'loginreqlink' => 'မှတ်​ပုံ​တင်​ဖြင့်​ဝင်​ပါ​',
 110+
 111+# Search results
 112+'powersearch' => 'ရှာ​ဖွေ​ရန်​',
 113+
 114+# Preferences page
 115+'mypreferences' => 'ကျွန်​တော့​ရွေး​ချယ်​စ​ရာ​များ​',
 116+'prefsnologin' => 'မှတ်​ပုံ​တင်​ဖြင့်​မ​ဝင်​ရ​သေး​ပါ​',
 117+'searchresultshead' => 'ရှာ​ဖွေ​ရန်​',
 118+
 119+# Recent changes
 120+'recentchanges' => 'လတ်​တ​လော​အ​ပြောင်း​အ​လဲ​များ​',
 121+
 122+# Recent changes linked
 123+'recentchangeslinked' => 'ဆက်​ဆပ်​သော​အ​ပြောင်း​အ​လဲ​များ​',
 124+
 125+# Upload
 126+'upload' => 'ဖိုင်​တင်​ရန်​',
 127+'uploadbtn' => 'ဖိုင်​တင်​ရန်​',
 128+'uploadnologin' => 'မှတ်​ပုံ​တင်​ဖြင့်​မ​ဝင်​ရ​သေး​ပါ​',
 129+'filedesc' => 'အ​ကျဉ်း​ချုပ်​',
 130+'fileuploadsummary' => 'အ​ကျဉ်း​ချုပ်:',
 131+'watchthisupload' => 'ဤ​စာ​မျက်​နှာ​အား​စောင့်​ကြည့်​ပါ​',
 132+
 133+# Image list
 134+'ilsubmit' => 'ရှာ​ဖွေ​ရန်​',
 135+
 136+'brokenredirects-edit' => '(ပြင်​ဆင်​ရန်)',
 137+'brokenredirects-delete' => '(ဖျက်​ပါ)',
 138+
 139+# Miscellaneous special pages
 140+'randompage' => 'ကျ​ပန်း​စာ​မျက်​နှာ​',
 141+'specialpages' => 'အ​ထူး​စာ​မျက်​နှာ​',
 142+'newpages-username' => 'မှတ်​ပုံ​တင်​အ​မည်:',
 143+'move' => 'ရွေ့​ပြောင်း​ပါ​',
 144+
 145+# Book sources
 146+'booksources-go' => 'သွား​ပါ​',
 147+
 148+# Special:Log
 149+'log-search-submit' => 'သွား​ပါ​',
 150+
 151+# Special:Allpages
 152+'allpagessubmit' => 'သွား​ပါ​',
 153+
 154+# Watchlist
 155+'watchlist' => 'စောင့်​ကြည့်​စာ​ရင်း​',
 156+'mywatchlist' => 'စောင့်​ကြည့်​စာ​ရင်း​',
 157+'watch' => 'စောင့်​ကြည့်​ပါ​',
 158+'watchthispage' => 'ဤ​စာ​မျက်​နှာ​အား​စောင့်​ကြည့်​ပါ​',
 159+
 160+# Restrictions (nouns)
 161+'restriction-edit' => 'ပြင်​ဆင်​ရန်​',
 162+'restriction-move' => 'ရွေ့​ပြောင်း​ပါ​',
 163+
 164+# Undelete
 165+'undelete-search-submit' => 'ရှာ​ဖွေ​ရန်​',
 166+
 167+# Contributions
 168+'contributions' => 'မှတ်​ပုံ​တင်​အ​သုံး​ပြု​သူ​:ပံ​ပိုး​မှု​များ​',
 169+'mycontris' => 'ကျွန်​တော်​ပေး​သော​ပံ​ပိုး​မှု​များ​',
 170+
 171+'sp-contributions-submit' => 'ရှာ​ဖွေ​ရန်​',
 172+
 173+# What links here
 174+'whatlinkshere' => 'မည်​သည့်​စာ​မျက်​နှာ​များ​မှ​ညွန်း​ထား​သည်​',
 175+
 176+# Block/unblock
 177+'ipbreason' => 'အ​ကြောင်း​ပြ​ချက်:',
 178+'ipblocklist-submit' => 'ရှာ​ဖွေ​ရန်​',
 179+
 180+# Move page
 181+'movepage' => 'စာ​မျက်​နှာ​အား​ရွေ့​ပြောင်း​ပါ​',
 182+'movearticle' => 'စာ​မျက်​နှာ​အား​ရွေ့​ပြောင်း​ပါ​',
 183+'movenologin' => 'မှတ်​ပုံ​တင်​ဖြင့်​မ​ဝင်​ရ​သေး​ပါ​',
 184+'movepagebtn' => 'စာ​မျက်​နှာ​အား​ရွေ့​ပြောင်း​ပါ​',
 185+'movedto' => 'ရွေ့​ပြောင်း​ရန်​နေ​ရာ​',
 186+'1movedto2' => '[[$1]] မှ​ [[$2]] သို့​',
 187+'movereason' => 'အ​ကြောင်း​ပြ​ချက်​',
 188+
 189+# Namespace 8 related
 190+'allmessages' => 'စ​နစ်​၏​သ​တင်း​များ​',
 191+
 192+# Tooltip help for the actions
 193+'tooltip-pt-logout' => 'ထွက်​ပါ​',
 194+'tooltip-ca-move' => 'ဤ​စာ​မျက်​နှာ​အား​ရွေ့​ပြောင်း​ပါ​',
 195+
 196+'youhavenewmessagesmulti' => 'သင့်​အ​တွက်​သီ​တင်း​အ​သစ်​ $1 တွင်​ရှိ​သည်​',
 197+
 198+# Multipage image navigation
 199+'imgmultigo' => 'သွား​ပါ!',
 200+
 201+# Table pager
 202+'table_pager_limit_submit' => 'သွား​ပါ​',
 203+
 204+);
 205+
 206+?>
Property changes on: branches/liquidthreads/languages/messages/MessagesMy.php
___________________________________________________________________
Added: svn:eol-style
1207 + native
Index: branches/liquidthreads/languages/messages/MessagesZh_tw.php
@@ -1378,6 +1378,7 @@
13791379 'nolinkshere-ns' => '在所選的名字空間內沒有頁面鏈接到[[:$1]]。',
13801380 'isredirect' => '重定向頁',
13811381 'istemplate' => '包含',
 1382+'whatlinkshere-links' => '(← 鏈入頁面)',
13821383 'whatlinkshere-prev' => '前$1個',
13831384 'whatlinkshere-next' => '後$1個',
13841385
Index: branches/liquidthreads/languages/messages/MessagesKk_tr.php
@@ -167,7 +167,7 @@
168168 'localmonth' => array( 1, 'JERGİLİKTİAÝ', 'LOCALMONTH' ),
169169 'localmonthname' => array( 1, 'JERGİLİKTİAÝATAWI', 'LOCALMONTHNAME' ),
170170 'localmonthnamegen' => array( 1, 'JERGİLİKTİAÝİLİKATAWI', 'LOCALMONTHNAMEGEN' ),
171 - 'localmonthabbrev' => array( 1, 'JERGİLİKTİAÝJÏIR', 'JERGİLİKTİAÝQISQA', 'LOCALMONTHABBREV' ),
 171+ 'localmonthabbrev' => array( 1, 'JERGİLİKTİAÝJÏIR', 'JERGİLİKTİAÝQISQAŞA', 'JERGİLİKTİAÝQISQA', 'LOCALMONTHABBREV' ),
172172 'localday' => array( 1, 'JERGİLİKTİKÜN', 'LOCALDAY' ),
173173 'localday2' => array( 1, 'JERGİLİKTİKÜN2', 'LOCALDAY2' ),
174174 'localdayname' => array( 1, 'JERGİLİKTİKÜNATAWI', 'LOCALDAYNAME' ),
@@ -189,8 +189,8 @@
190190 'subjectspacee' => array( 1, 'TAQIRIPBETİ2', 'MAQALABETİ2', 'SUBJECTSPACEE', 'ARTICLESPACEE' ),
191191 'fullpagename' => array( 1, 'TOLIQBETATAWI', 'FULLPAGENAME' ),
192192 'fullpagenamee' => array( 1, 'TOLIQBETATAWI2', 'FULLPAGENAMEE' ),
193 - 'subpagename' => array( 1, 'ASTIÑĞIBETATAWI', 'SUBPAGENAME' ),
194 - 'subpagenamee' => array( 1, 'ASTIÑĞIBETATAWI2', 'SUBPAGENAMEE' ),
 193+ 'subpagename' => array( 1, 'BETŞEATAWI', 'ASTIÑĞIBETATAWI', 'SUBPAGENAME' ),
 194+ 'subpagenamee' => array( 1, 'BETŞEATAWI2', 'ASTIÑĞIBETATAWI2', 'SUBPAGENAMEE' ),
195195 'basepagename' => array( 1, 'NEGİZGİBETATAWI', 'BASEPAGENAME' ),
196196 'basepagenamee' => array( 1, 'NEGİZGİBETATAWI2', 'BASEPAGENAMEE' ),
197197 'talkpagename' => array( 1, 'TALQILAWBETATAWI', 'TALKPAGENAME' ),
@@ -208,6 +208,7 @@
209209 'img_width' => array( 1, '$1 px', '$1px' ),
210210 'img_center' => array( 1, 'ortağa', 'orta', 'center', 'centre' ),
211211 'img_framed' => array( 1, 'sürmeli', 'framed', 'enframed', 'frame' ),
 212+ 'img_frameless' => array( 1, 'sürmesiz', 'frameless' ),
212213 'img_page' => array( 1, 'bet=$1', 'bet $1', 'page=$1', 'page $1' ),
213214 'img_upright' => array( 1, 'tikti', 'tiktik=$1', 'tiktik $1' ),
214215 'img_border' => array( 1, 'şekti' ),
@@ -221,18 +222,18 @@
222223 'img_text-bottom' => array( 1, 'mätin-astında', 'text-bottom' ),
223224 'int' => array( 0, 'İŞKİ:', 'INT:' ),
224225 'sitename' => array( 1, 'TORAPATAWI', 'SITENAME' ),
225 - 'ns' => array( 0, 'EA:', 'NS:' ),
 226+ 'ns' => array( 0, 'EA:', 'ESİMAYA:', 'NS:' ),
226227 'localurl' => array( 0, 'JERGİLİKTİJAÝ:', 'LOCALURL:' ),
227228 'localurle' => array( 0, 'JERGİLİKTİJAÝ2:', 'LOCALURLE:' ),
228229 'server' => array( 0, 'SERVER', 'SERVER' ),
229230 'servername' => array( 0, 'SERVERATAWI', 'SERVERNAME' ),
230231 'scriptpath' => array( 0, 'ÄMİRJOLI', 'SCRIPTPATH' ),
231 - 'grammar' => array( 0, 'SEPTİK:', 'GRAMMAR:' ),
 232+ 'grammar' => array( 0, 'SEPTİGİ:', 'SEPTİK:', 'GRAMMAR:' ),
232233 'notitleconvert' => array( 0, '__ATAWALMASTIRĞIZBAW__', '__AABAW__', '__NOTITLECONVERT__', '__NOTC__' ),
233234 'nocontentconvert' => array( 0, '__MAĞLUMATALMASTIRĞIZBAW__', '__MABAW__', '__NOCONTENTCONVERT__', '__NOCC__' ),
234 - 'currentweek' => array( 1, 'AĞIMDAĞIAPTA', 'CURRENTWEEK' ),
 235+ 'currentweek' => array( 1, 'AĞIMDAĞIAPTASI', 'AĞIMDAĞIAPTA', 'CURRENTWEEK' ),
235236 'currentdow' => array( 1, 'AĞIMDAĞIAPTAKÜNİ', 'CURRENTDOW' ),
236 - 'localweek' => array( 1, 'JERGİLİKTİAPTA', 'LOCALWEEK' ),
 237+ 'localweek' => array( 1, 'JERGİLİKTİAPTASI', 'JERGİLİKTİAPTA', 'LOCALWEEK' ),
237238 'localdow' => array( 1, 'JERGİLİKTİAPTAKÜNİ', 'LOCALDOW' ),
238239 'revisionid' => array( 1, 'NUSQANÖMİRİ', 'REVISIONID' ),
239240 'revisionday' => array( 1, 'NUSQAKÜNİ' , 'REVISIONDAY' ),
@@ -240,13 +241,13 @@
241242 'revisionmonth' => array( 1, 'NUSQAAÝI', 'REVISIONMONTH' ),
242243 'revisionyear' => array( 1, 'NUSQAJILI', 'REVISIONYEAR' ),
243244 'revisiontimestamp' => array( 1, 'NUSQAWAQITTÜÝİNDEMESİ', 'REVISIONTIMESTAMP' ),
244 - 'plural' => array( 0, 'KÖPŞE:', 'PLURAL:' ),
245 - 'fullurl' => array( 0, 'TOLIQJAÝ:', 'FULLURL:' ),
246 - 'fullurle' => array( 0, 'TOLIQJAÝ2:', 'FULLURLE:' ),
247 - 'lcfirst' => array( 0, 'KÄ1:', 'LCFIRST:' ),
248 - 'ucfirst' => array( 0, 'BÄ1:', 'UCFIRST:' ),
249 - 'lc' => array( 0, 'KÄ:', 'LC:' ),
250 - 'uc' => array( 0, 'BÄ:', 'UC:' ),
 245+ 'plural' => array( 0, 'KÖPŞETÜRİ:','KÖPŞE:', 'PLURAL:' ),
 246+ 'fullurl' => array( 0, 'TOLIQJAÝI:', 'TOLIQJAÝ:', 'FULLURL:' ),
 247+ 'fullurle' => array( 0, 'TOLIQJAÝI2:', 'TOLIQJAÝ2:', 'FULLURLE:' ),
 248+ 'lcfirst' => array( 0, 'KÄ1:', 'KİŞİÄRİPPEN1:', 'LCFIRST:' ),
 249+ 'ucfirst' => array( 0, 'BÄ1:', 'BASÄRİPPEN1:', 'UCFIRST:' ),
 250+ 'lc' => array( 0, 'KÄ:', 'KİŞİÄRİPPEN:', 'LC:' ),
 251+ 'uc' => array( 0, 'BÄ:', 'BASÄRİPPEN:', 'UC:' ),
251252 'raw' => array( 0, 'QAM:', 'RAW:' ),
252253 'displaytitle' => array( 1, 'KÖRSETİLETİNATAW', 'DISPLAYTITLE' ),
253254 'rawsuffix' => array( 1, 'Q', 'R' ),
@@ -262,8 +263,8 @@
263264 'pagesinnamespace' => array( 1, 'ESİMAYABETSANI:', 'EABETSANI:', 'AYABETSANI:', 'PAGESINNAMESPACE:', 'PAGESINNS:' ),
264265 'numberofadmins' => array( 1, 'ÄKİMŞİSANI', 'NUMBEROFADMINS' ),
265266 'formatnum' => array( 0, 'SANPİŞİMİ', 'FORMATNUM' ),
266 - 'padleft' => array( 0, 'SOLIĞIS', 'PADLEFT' ),
267 - 'padright' => array( 0, 'OÑIĞIS', 'PADRIGHT' ),
 267+ 'padleft' => array( 0, 'SOLĞAIĞIS', 'SOLIĞIS', 'PADLEFT' ),
 268+ 'padright' => array( 0, 'OÑĞAIĞIS', 'OÑIĞIS', 'PADRIGHT' ),
268269 'special' => array( 0, 'arnaýı', 'special', ),
269270 'defaultsort' => array( 1, 'ÄDEPKİSURIPTAW:', 'ÄDEPKİSURIP:', 'DEFAULTSORT:' ),
270271 );
@@ -348,7 +349,7 @@
349350 $messages = array(
350351 # User preference toggles
351352 'tog-underline' => 'Siltemeni astınan sız:',
352 -'tog-highlightbroken' => 'Joqtalğan siltemelerdi <a href="" class="new">bılaý</a> pişimde (basqaşa: bılaý <a href="" class="internal">?</a> sïyaqtı).',
 353+'tog-highlightbroken' => 'Jaramsız siltemelerdi <a href="" class="new">bılaý</a> pişimde (balaması: bılaý <a href="" class="internal">?</a> sïyaqtı).',
353354 'tog-justify' => 'Ejelerdi eni boýınşa twralaw',
354355 'tog-hideminor' => 'Jwıqtağı özgeristerde şağın tüzetwdi jasır',
355356 'tog-extendwatchlist' => 'Baqılaw tizimdi ulğaýt (barlıq jaramdı özgeristerdi körset)',
@@ -449,7 +450,7 @@
450451 'categories' => 'Barlıq sanat tizimi',
451452 'pagecategories' => '{{PLURAL:$1|Sanat|Sanattar}}',
452453 'category_header' => '«$1» sanatındağı better',
453 -'subcategories' => 'Tömengi sanattar',
 454+'subcategories' => 'Sanatşalar',
454455 'category-media-header' => '«$1» sanatındağı taspalar',
455456
456457 'linkprefix' => '/^(.*?)([a-zäçéğıïñöşüýа-яёәіңғүұқөһA-ZÄÇÉĞİÏÑÖŞÜÝА-ЯЁӘІҢҒҮҰҚӨҺʺʹ«„]+)$/sDu',
@@ -888,7 +889,7 @@
889890 'nonunicodebrowser' => '<strong>AÑĞARTPA: Şolğışıñız Unicode belgilewine üýlesimdi emes, sondıqtan latın emes äripteri bar betterdi öñdew zil bolw mümkin. Jumıs istewge ıqtïmaldıq berw üşin, tömengi öñdew awmağında ASCII emes äripter onaltılıq sanımen körsetiledi</strong>.',
890891 'editingold' => '<strong>AÑĞARTPA: Osı bettiñ erterek nusqasın
891892 öñdep jatırsız.
892 -Bunı saqtasañız, osı nwsqadan soñğı barlıq tüzetwler joýıladı.</strong>',
 893+Bunı saqtasañız, osı nwsqadan soñğı barlıq özgerister joýıladı.</strong>',
893894 'yourdiff' => 'Aýırmalar',
894895 'copyrightwarning' => '{{SITENAME}} jobasına qosılğan bükil üles $2 (köbirek aqparat üşin: $1) qujatına saý jiberilgen bolıp sanaladı. Eger jazwıñızdıñ erkin köşirilip tüzetilwin qalamasañız, mında usınbawıñız jön.<br />
895896 Tağı, qosqan ülesiñiz - öziñizdiñ jazğanığız, ne aşıq aqparat közderinen alınğan mağlumat bolğanın wäde etesiz.<br />
@@ -1069,7 +1070,7 @@
10701071 'qbsettings-fixedright' => 'Oñğa bekitilgen',
10711072 'qbsettings-floatingleft' => 'Solğa qalqığan',
10721073 'qbsettings-floatingright' => 'Oñğa qalqığan',
1073 -'changepassword' => 'Qupïya söz özgertw',
 1074+'changepassword' => 'Qupïya sözdi awıstırw',
10741075 'skin' => 'Bezendirw',
10751076 'math' => 'Matematïka',
10761077 'dateformat' => 'Kün-aý pişimi',
@@ -1151,7 +1152,7 @@
11521153 'rightsnone' => '(eşqandaý)',
11531154
11541155 # Recent changes
1155 -'nchanges' => '{{PLURAL:$1|bir tüzetw|$1 tüzetw}}',
 1156+'nchanges' => '{{PLURAL:$1|bir özgeris|$1 özgeris}}',
11561157 'recentchanges' => 'Jwıqtağı özgerister',
11571158 'recentchangestext' => 'Bul bette osı wïkïdegi bolğan jwıqtağı özgerister baýqaladı.',
11581159 'recentchanges-feed-description' => 'Bul arnamenen wïkïdegi eñ soñğı özgerister qadağalanadı.',
@@ -1178,9 +1179,9 @@
11791180 'rc_categories_any' => 'Qaýsıbir',
11801181
11811182 # Recent changes linked
1182 -'recentchangeslinked' => 'Qatıstı tüzetwler',
 1183+'recentchangeslinked' => 'Qatıstı özgerister',
11831184 'recentchangeslinked-noresult' => 'Siltegen betterde aýtılmış merzimde eşqandaý özgeris bolmağan.',
1184 -'recentchangeslinked-summary' => "Bul arnaýı bette siltegen betterdegi jwıqtağı özgerister tizimi beriledi. Baqılaw tizimiñizdegi better '''jwan''' ärpimen belgilenedi.",
 1185+'recentchangeslinked-summary' => "Bul arnaýı bette siltegen betterdegi jwıqtağı özgerister tizimi beriledi. Baqılaw tizimiñizdegi better '''jwan''' ärbimen belgilenedi.",
11851186
11861187 # Upload
11871188 'upload' => 'Faýl qotarw',
@@ -1218,8 +1219,8 @@
12191220 'filetype-missing' => 'Bul faýldıñ («.jpg» sïyaqtı) keñeýtimi joq.',
12201221 'large-file' => 'Faýldı $1 mölşerden aspawına tırısıñız; bul faýl mölşeri — $2.',
12211222 'largefileserver' => 'Osı faýldıñ mölşeri serverdiñ qalawınan asıp ketken.',
1222 -'emptyfile' => 'Qotarılğan faýlıñız bos sïyaqtı. Bul faýl atawı jansaq engizilgeninen bolwı mümkin. Qotarğıñız kelgen faýl şınında da osı faýl bolğanın tekserip alıñız.',
1223 -'fileexists' => 'Osındaý atawlı faýl bar tüge. Qaýta jazwdıñ aldınan $1 tekserip şığıñız.',
 1223+'emptyfile' => 'Qotarılğan faýlıñız bos sïyaqtı. Bul faýl atawında qate bolwı mümkin. Osı faýldı şınaýı qotarğıñız keletin tekserip şığıñız.',
 1224+'fileexists' => 'Osındaý atawlı faýl bar tüge, eger bunı özgertwge senimiñiz joq bolsa <strong><tt>$1</tt></strong> degendi tekserip şığıñız.',
12241225 'fileexists-extension' => 'Uqsastı faýl atawı bar tüge:<br />
12251226 Qotarılatın faýl atawı: <strong><tt>$1</tt></strong><br />
12261227 Bar bolğan faýl atawı: <strong><tt>$2</tt></strong><br />
@@ -1306,7 +1307,7 @@
13071308
13081309 # MIME search
13091310 'mimesearch' => 'Faýldı MIME türimen izdew',
1310 -'mimesearch-summary' => 'Bul bet faýldardı MIME türimen süzgilew mümkindigin beredi. Kirisi: «mağlumat türi»/«taraw türi», mısalı <tt>image/jpeg</tt>.',
 1311+'mimesearch-summary' => 'Bul bet faýldardı MIME türimen süzgilew mümkindigin beredi. Kirisi: «mağlumat türi»/«tür tarawı», mısalı <tt>image/jpeg</tt>.',
13111312 'mimetype' => 'MIME türi:',
13121313 'download' => 'jüktew',
13131314
@@ -1520,7 +1521,7 @@
15211522 * [[{{ns:special}}:Watchlist/edit|Bükil tizimdi qaraw jäne özgertw]].
15221523 * [[{{ns:special}}:Watchlist/clear|Tizimdegi barlıq dana alastatw]].",
15231524 'wlheader-enotif' => '* Eskertw xat jiberwi endirilgen.',
1524 -'wlheader-showupdated' => "* Soñğı kirgenimnen beri tüzetilgen betterdi '''jwan''' mätinmen körset",
 1525+'wlheader-showupdated' => "* Soñğı kirgenimnen beri özgertilgen betterdi '''jwan''' ärbimen körset",
15251526 'watchmethod-recent' => 'baqılawlı betterdiñ jwıqtağı özgeristerin tekserw',
15261527 'watchmethod-list' => 'jwıqtağı özgeristerde baqılawlı betterdi tekserw',
15271528 'removechecked' => 'Belgilengendi baqılaw tiziminen alastatw',
@@ -1621,7 +1622,7 @@
16221623 «Artqa» tüýmesin basıñız, jäne betti keri jükteñiz, sosın qaýtalap köriñiz.',
16231624 'protectlogpage' => 'Qorğaw_jwrnalı',
16241625 'protectlogtext' => 'Tömende betterdiñ qorğaw/qorğamaw tizimi berilgen. Ağımdağı qorğaw ärektter bar better üşin [[{{ns:special}}:Protectedpages|qorğalğan bet tizimin]] qarañız.',
1625 -'protectedarticle' => '«$1» qorğaldı',
 1626+'protectedarticle' => '«[[$1]]» qorğaldı',
16261627 'unprotectedarticle' => '«[[$1]]» qorğalmadı',
16271628 'protectsub' => '(«$1» qorğawda)',
16281629 'confirmprotecttext' => 'Osı betti rasında da qorğaw qajet pe?',
@@ -1651,7 +1652,7 @@
16521653 'protect-expiring' => 'bitwi: $1 (UTC)',
16531654 'protect-cascade' => 'Bawlı qorğaw — bul betke kiristirilgen ärqaýsı betterdi qorğaw.',
16541655 'restriction-type' => 'Ruqsatı:',
1655 -'restriction-level' => 'Ruqsat deñgeýi:',
 1656+'restriction-level' => 'Ruqsat şektew deñgeýi:',
16561657 'minimum-size' => 'Eñ az mölşeri',
16571658 'maximum-size' => 'Eñ köp mölşeri',
16581659 'pagesize' => '(baýt)',
@@ -1695,7 +1696,7 @@
16961697 'undeletedarticle' => '«[[$1]]» qaýtardı',
16971698 'undeletedrevisions' => '{{PLURAL:$1|Nusqanı|$1 nusqanı}} qaýtardı',
16981699 'undeletedrevisions-files' => '{{PLURAL:$1|Nusqanı|$1 nusqanı}} jäne {{PLURAL:$2|faýldı|$2 faýldı}} qaýtardı',
1699 -'undeletedfiles' => '{{PLURAL:$1|1 faýl|$1 faýl}} qaýtardı',
 1700+'undeletedfiles' => '{{PLURAL:$1|1 faýldı|$1 faýldı}} qaýtardı',
17001701 'cannotundelete' => 'Qaýtarw sätsiz bitti; tağı birew sizden burın sol betti qaýtarğan bolar.',
17011702 'undeletedpage' => "<big>'''$1 qaýtarıldı'''</big>
17021703
@@ -1746,6 +1747,7 @@
17471748 'istemplate' => 'kiriktirw',
17481749 'whatlinkshere-prev' => '{{PLURAL:$1|aldıñğı|aldıñğı $1}}',
17491750 'whatlinkshere-next' => '{{PLURAL:$1|kelesi|kelesi $1}}',
 1751+'whatlinkshere-links' => '← siltemeler',
17501752
17511753 # Block/unblock
17521754 'blockip' => 'Paýdalanwşını buğattaw',
@@ -1866,7 +1868,7 @@
18671869 barlıq tarïxın jaña atawğa jıljıtadı.
18681870 Burınğı bet atawı jaña atawğa aýdatatın bet boladı.
18691871 Eski atawına silteýtin siltemeler özgertilmeýdi; jıljıtwdan soñ
1870 -şınjırlı aýdatwlar bar-joğın tekseriñiz.
 1872+şınjırlı ne jaramsız aýdatwlar bar-joğın tekserip şığıñız.
18711873 Siltemeler burınğı joldawımen bılaýğı ötwin tekserwine
18721874 siz mindetti bolasız.
18731875
@@ -1877,7 +1879,7 @@
18781880 biraq bar bettiñ üstine jazwğa bolmaýdı.
18791881
18801882 <b>NAZAR SALIÑIZ!</b>
1881 -Bul däripti betke qatañ jäne kenet özgeris jasawğa mümkin;
 1883+Bul äýgili betke qatañ jäne kenet özgeris jasawğa mümkin;
18821884 ärekettiñ aldınan osınıñ zardaptarın tüsingeniñizge batıl
18831885 bolıñız.",
18841886 'movepagetalktext' => "Kelesi sebepter '''bolğanşa''' deýin, talqılaw beti özdiktik birge jıljıtıladı:
@@ -2042,8 +2044,8 @@
20432045 'tooltip-ca-nstab-help' => 'Anıqtıma betin qaraw',
20442046 'tooltip-ca-nstab-category' => 'Sanat betin qaraw',
20452047 'tooltip-minoredit' => 'Osını şağın tüzetw dep belgilew',
2046 -'tooltip-save' => 'Tüzetwiñizdi saqtaw',
2047 -'tooltip-preview' => 'Saqtawdıñ aldınan tüzetwiñizdi qarap şığıñız!',
 2048+'tooltip-save' => 'Jasağan özgeristeriñizdi saqtaw',
 2049+'tooltip-preview' => 'Saqtawdıñ aldınan jasağan özgeristeriñizdi qarap şığıñız!',
20482050 'tooltip-diff' => 'Mätinge qandaý özgeristerdi jasağanıñızdı qaraw.',
20492051 'tooltip-compareselectedversions' => 'Bettiñ eki nusqasınıñ aýırmasın qaraw.',
20502052 'tooltip-watch' => 'Bul betti baqılaw tizimiñizge üstew',
@@ -2135,7 +2137,7 @@
21362138 'spamprotectiontitle' => '«Spam»-nan qorğaýtın süzgi',
21372139 'spamprotectiontext' => 'Bul bettiñ saqtawın «spam» süzgisi buğattadı. Bunıñ sebebi sırtqı torap siltemesinen bolwı mümkin.',
21382140 'spamprotectionmatch' => 'Kelesi «spam» mätini süzgilengen: $1',
2139 -'subcategorycount' => 'Bul sanatta {{PLURAL:$1|bir|$1}} tömengi sanat bar.',
 2141+'subcategorycount' => 'Bul sanatta {{PLURAL:$1|bir|$1}} sanatşa bar.',
21402142 'categoryarticlecount' => 'Bul sanatta {{PLURAL:$1|bir|$1}} bet bar.',
21412143 'category-media-count' => 'Bul sanatta {{PLURAL:$1|bir|$1}} faýl bar.',
21422144 'listingcontinuesabbrev' => ' (jalğ.)',
@@ -2168,7 +2170,7 @@
21692171 'rcpatroldisabledtext' => 'Jwıqtağı özgerister Küzeti qasïeti ağımda öşirilgen.',
21702172 'markedaspatrollederror' => 'Küzette dep belgilenbeýdi',
21712173 'markedaspatrollederrortext' => 'Küzette dep belgilew üşin nusqasın engiziñiz.',
2172 -'markedaspatrollederror-noautopatrol' => 'Öziñizdiñ özgeristeriñizdi küzetke qoya almaýsız.',
 2174+'markedaspatrollederror-noautopatrol' => 'Öziñiz jasağan özgeristeriñizdi küzetke qoya almaýsız.',
21732175
21742176 # Patrol log
21752177 'patrol-log-page' => 'Küzet jwrnalı',
@@ -2185,7 +2187,7 @@
21862188
21872189 # Media information
21882190 'mediawarning' => "'''Nazar salıñız''': Bul faýl türinde qaskünemdi ämirdiñ bar bolwı ıqtïmal; faýldı jegip jüýeñizge zïyan keltirwiñiz mümkin.<hr />",
2189 -'imagemaxsize' => 'Swret tüýindeme betindegi swrettiñ mölşerin şektewi:',
 2191+'imagemaxsize' => 'Sïpattaması betindegi swrettiñ mölşerin şektewi:',
21902192 'thumbsize' => 'Nobaý mölşeri:',
21912193 'widthheight' => '$1 × $2',
21922194 'file-info' => 'Faýl mölşeri: $1, MIME türi: $2',
@@ -2491,7 +2493,7 @@
24922494 # Pseudotags used for GPSSpeedRef and GPSDestDistanceRef
24932495 'exif-gpsspeed-k' => 'km/h',
24942496 'exif-gpsspeed-m' => 'mil/h',
2495 -'exif-gpsspeed-n' => 'J. tüýin',
 2497+'exif-gpsspeed-n' => 'knot',
24962498
24972499 # Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef
24982500 'exif-gpsdirection-t' => 'Şın bağıt',
Index: branches/liquidthreads/languages/messages/MessagesDa.php
@@ -1494,6 +1494,7 @@
14951495 'istemplate' => 'Skabelonmedtagning',
14961496 'whatlinkshere-prev' => '{{PLURAL:$1|forrige|forrige $1}}',
14971497 'whatlinkshere-next' => '{{PLURAL:$1|næste|næste $1}}',
 1498+'whatlinkshere-links' => '← henvisninger',
14981499
14991500 # Block/unblock
15001501 'blockip' => 'Bloker bruger',
Index: branches/liquidthreads/languages/messages/MessagesDe.php
@@ -1532,7 +1532,7 @@
15331533 'istemplate' => 'Vorlageneinbindung',
15341534 'whatlinkshere-prev' => '{{PLURAL:$1|vorheriger|vorherige $1}}',
15351535 'whatlinkshere-next' => '{{PLURAL:$1|nächster|nächste $1}}',
1536 -'whatlinkshere-links' => '(← Links)',
 1536+'whatlinkshere-links' => '← Links',
15371537
15381538 # Block/unblock
15391539 'blockip' => 'IP-Adresse/Benutzer sperren',
Index: branches/liquidthreads/languages/messages/MessagesZh_yue.php
@@ -340,9 +340,9 @@
341341 'categorypage' => '去睇分類頁',
342342 'viewtalkpage' => '睇討論',
343343 'otherlanguages' => '第啲語言',
344 -'redirectedfrom' => '(由 $1 重新定向)', //REDIRECT
345 -'redirectpagesub' => '重新定向頁',
346 -'lastmodifiedat' => '呢一頁嘅最後修改係響$1 $2。',
 344+'redirectedfrom' => '(由$1跳轉過來)', //REDIRECT
 345+'redirectpagesub' => '跳轉頁',
 346+'lastmodifiedat' => '呢一頁嘅最後修改係響$1 $2。',
347347 'viewcount' => '呢一頁已經有$1人次睇過。',
348348 'copyright' => '響版度嘅內容係根據$1嘅條款發佈。',
349349 'protectedpage' => '受保護頁',
@@ -1116,7 +1116,7 @@
11171117 'userstats' => '用戶統計',
11181118 'sitestatstext' => "資料庫中而家有'''$1'''頁。
11191119 其中包括咗「討論」頁、關於{{SITENAME}}嘅頁、好短嘅「楔位」
1120 -文章、重新定向, 以及其他唔計入內容嘅頁。
 1120+文章、跳轉,以及其他唔計入內容嘅頁。
11211121 唔計非內容頁在內,則總共有'''$2'''頁可能會計入正規嘅內容。
11221122
11231123 '''$8''' 個檔案已經上載。
@@ -1248,7 +1248,7 @@
12491249 先可以傳送電郵畀其他用戶。",
12501250 'emailuser' => '發電郵畀呢位用戶',
12511251 'emailpage' => '發電郵畀用戶',
1252 -'emailpagetext' => '如果呢位用戶已經喺佢嘅用戶使用偏好入邊填咗個合法嘅電郵地址,以下表格會發送單單一條訊息。
 1252+'emailpagetext' => '如果呢位用戶已經喺佢嘅用戶使用偏好入邊填咗個合法嘅電郵地址,以下表格會發送單單一條信息。
12531253 你喺你嘅用戶喜好設定入面填寫嘅電郵地址會出現喺呢封電郵「由」嘅地址度,以便收件人可以回覆到。',
12541254 'usermailererror' => '目標郵件地址返回錯誤:',
12551255 'defemailsubject' => "{{SITENAME}} 電郵",
@@ -1263,7 +1263,7 @@
12641264 'emailccme' => '傳送一個我嘅信息電郵畀我。',
12651265 'emailccsubject' => '複製你嘅信息到 $1: $2',
12661266 'emailsent' => '電郵已傳送',
1267 -'emailsenttext' => '你嘅電郵訊息已傳送。',
 1267+'emailsenttext' => '你嘅電郵信息已傳送。',
12681268
12691269 # Watchlist
12701270 'watchlist' => '監視清單',
@@ -1515,6 +1515,7 @@
15161516 'istemplate' => '包含',
15171517 'whatlinkshere-prev' => '前$1版',
15181518 'whatlinkshere-next' => '後$1版',
 1519+'whatlinkshere-links' => '(← 連結)',
15191520
15201521 # Block/unblock IP
15211522 #
@@ -1699,10 +1700,10 @@
17001701 'allmessagesname' => '名稱',
17011702 'allmessagesdefault' => '預設文字',
17021703 'allmessagescurrent' => '現時文字',
1703 -'allmessagestext' => '以下係 MediaWiki 空間名入邊現有系統訊息嘅清單。',
 1704+'allmessagestext' => '以下係 MediaWiki 空間名入邊現有系統信息嘅清單。',
17041705 'allmessagesnotsupportedUI' => '呢個網站嘅{{ns:special}}:AllMessages唔支持你現時嘅介面語言<b>$1</b>。',
17051706 'allmessagesnotsupportedDB' => '唔可以用\'\'\'{{ns:special}}:AllMessages\'\'\',因為\'\'\'$wgUseDatabaseMessages\'\'\'已經閂咗。',
1706 -'allmessagesfilter' => '訊息名過濾(器):',
 1707+'allmessagesfilter' => '信息名過濾(器):',
17071708 'allmessagesmodified' => '只顯示修改過嘅',
17081709
17091710
@@ -2294,7 +2295,7 @@
22952296 'unit-pixel' => 'px',
22962297
22972298 # HTML dump
2298 -'redirectingto' => '重新定向到[[$1]]...',
 2299+'redirectingto' => '跳轉去[[$1]]...',
22992300
23002301 # action=purge
23012302 'confirm_purge' => "肯定要洗咗呢版個快取版本?\n\n$1",
@@ -2334,7 +2335,7 @@
23352336 # Auto-summaries
23362337 'autosumm-blank' => '移除緊響嗰一版嘅全部內容',
23372338 'autosumm-replace' => '用 \'$1\' 取代緊嗰一版',
2338 -'autoredircomment' => '重新定向緊到[[$1]]', # This should be changed to the new naming convention, but existed beforehand.
 2339+'autoredircomment' => '跳緊轉到[[$1]]', # This should be changed to the new naming convention, but existed beforehand.
23392340 'autosumm-new' => '新頁: $1',
23402341
23412342 # Size units
Index: branches/liquidthreads/RELEASE-NOTES
@@ -43,7 +43,13 @@
4444 * Introducing 'frameless' keyword to [[Image:]] syntax which respects the
4545 user preferences for image width like 'thumb' but without a frame.
4646 * (bug 7960) Link to "what links here" for each "what links here" entry
 47+* Added support for configuration of an arbitrary number of commons-style
 48+ file repositories.
 49+* Added a Content-Disposition header to thumb.php output
 50+* Improved thumb.php error handling
 51+* Display file history on local image description pages of shared images
4752
 53+
4854 == Bugfixes since 1.10 ==
4955
5056 * (bug 9712) Use Arabic comma in date/time formats for Arabic and Farsi
@@ -93,7 +99,9 @@
94100 * (bug 7899) Added \hline and \vline to the list of allowed TeX commands
95101 * (bug 7993) support mathematical symbol classes
96102 * (bug 10007) Allow Block IP to work with Postgrs again.
 103+* Add Google Wireless Transcoder to the Unicode editing blacklist
97104
 105+
98106 == MediaWiki API changes since 1.10 ==
99107
100108 (For ongoing development discussion, see http://www.mediawiki.org/wiki/API)
@@ -136,16 +144,19 @@
137145 * Spanish (es)
138146 * Farsi (fa)
139147 * Finnish (fi)
 148+* Võro (fiu-vro)
140149 * French (fr)
141150 * Hakka (hak)
142151 * Hebrew (he)
143152 * Indonesian (id)
144153 * Italian (it)
145154 * Japanese (ja)
 155+* Georgian (ka)
146156 * Kabyle (kab)
147157 * Kazakh (kk)
148158 * Kurdish (ku)
149159 * Lithuanian (lt)
 160+* Burmese (my)
150161 * Norwegian (no)
151162 * Polish (pl)
152163 * Russian (ru)
@@ -153,6 +164,7 @@
154165 * Somali (so)
155166 * Sundanese (su)
156167 * Swedish (sv)
 168+* Old Chinese / Late Middle Chinese (zh-classical)
157169 * Chinese (PRC) (zh-cn)
158170 * Chinese (Taiwan) (zh-tw)
159171 * Cantonese (zh-yue)
Property changes on: branches/liquidthreads
___________________________________________________________________
Modified: svnmerge-integrated
160172 - /trunk/phase3:1-22554
161173 + /trunk/phase3:1-22586

Follow-up revisions

RevisionCommit summaryAuthorDate
r22794* (bug 7859) Update Kazakh translations...raymond10:01, 6 June 2007
r22811Merged revisions 22791-22810 via svnmerge from...david07:26, 7 June 2007
r22901* (bug 7859) Update Kazakh translations...raymond08:53, 11 June 2007
r22919Merged revisions 22878-22918 via svnmerge from...david00:00, 12 June 2007
r22973* (bug 7859) Update Kazakh translations...raymond22:27, 13 June 2007
r23039Merged revisions 22967-23037 via svnmerge from...david20:15, 16 June 2007
r23058* (bug 7859) Update Kazakh translations...raymond07:49, 18 June 2007
r23087Merged revisions 23050-23086 via svnmerge from...david03:14, 19 June 2007
r23201* (bug 7859) Update Kazakh translations...raymond08:40, 22 June 2007
r23203Merged revisions 23120-23202 via svnmerge from...david09:07, 22 June 2007
r23286(bug 7859) Kazakh message updates [patch from AlefZet]robchurch09:27, 23 June 2007
r23407Merged revisions 23203-23405 via svnmerge from...david23:00, 25 June 2007

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r17525(bug 7859) Updates for Kazakh i18nrotem13:30, 10 November 2006
r17537(bug 7859) Updates for Kazakh i18nrotem16:57, 11 November 2006
r17723(bug 7859) Updates for Kazakh i18nrotem13:46, 16 November 2006
r17781(bug 7859) Updates for Kazakh i18nrotem13:03, 18 November 2006
r17908(bug 7859) Updates for Kazakh i18nrotem15:06, 24 November 2006
r17924(bug 7859) Updates for Kazakh i18nrotem14:12, 25 November 2006
r17925(bug 7859) Updates for Kazakh i18nrotem14:35, 25 November 2006
r19547* (bug 7859) yet another kazakh localisation patch.leon09:55, 21 January 2007
r20180* (bug 7859) Update Kazakh messages...raymond18:32, 6 March 2007
r20737* (bug 7859) Update Kazahk translations...raymond12:10, 27 March 2007
r20948* (bug 7859) Update Kazahk translations...raymond18:13, 2 April 2007
r21604* (bug 7859) Update Kazakh translations...raymond04:54, 26 April 2007
r21680* (bug 7859) Update Kazakh translations...raymond05:05, 28 April 2007
r21706* (bug 7859) Update Kazakh translations...raymond20:57, 29 April 2007
r21746* (bug 7859) Update Kazakh translations...raymond09:40, 1 May 2007
r21747* (bug 7859) Update Kazakh translations...raymond09:45, 1 May 2007
r22009* (bug 7859) Update Kazakh translations...raymond20:57, 8 May 2007
r22056* (bug 7859) Update Kazakh translations...raymond21:13, 9 May 2007
r22062* (bug 7859) Update Kazakh translations...raymond10:32, 10 May 2007
r22081* (bug 7859) Update Kazakh translations...raymond19:53, 10 May 2007
r22082* (bug 7859) Update Kazakh translations...raymond20:01, 10 May 2007
r22201* (bug 7859) Update Kazakh translations...raymond10:53, 16 May 2007
r22269* (bug 7859) Update Kazakh translations...raymond20:28, 19 May 2007
r22510* (bug 7859) Update Kazakh translations...raymond16:37, 28 May 2007
r22518Merged revisions 22484-22517 via svnmerge from...david22:22, 28 May 2007
r22536* Adding 1 message to optional per hint (bug 7859, comment# 71)...raymond14:43, 29 May 2007
r22549Merged revisions 22518-22547 via svnmerge from...david01:48, 30 May 2007
r22557* (bug 7859) Update Kazakh translations...raymond09:47, 30 May 2007
r22562* Start of Burmese (my) translation by Hakkanikerabbit13:41, 30 May 2007
r22563New danish translation for whatlinkshere-linkswegge13:50, 30 May 2007
r22564Update.rotem15:03, 30 May 2007
r22565* Add Google Wireless Transcoder to the Unicode editing blacklistbrion15:24, 30 May 2007
r22569* Võro (fiu-vro) updates from Võroknikerabbit15:56, 30 May 2007
r22571* (bug 10071) Update Georgian translations...raymond16:24, 30 May 2007
r22572* (bug 10070) Update Old Chinese / Late Middle Chinese (zh-classical) transla...raymond16:35, 30 May 2007
r22573* (bug 10068) Update Chinese (zh-cn and zh-tw) translations...raymond16:42, 30 May 2007
r22574* (bug 10069) Update Cantonese (zh-yue) translations...raymond16:46, 30 May 2007
r22576* (bug 10070) Update Old Chinese / Late Middle Chinese (zh-classical) transla...raymond17:47, 30 May 2007
r22577Honestly this whatlinkshere further link looks weird to me still, but it look...brion18:57, 30 May 2007
r22578Fix for http://bugzilla.wikimedia.org/show_bug.cgi?id=10036 -- add "maxlength...amidaniel19:48, 30 May 2007
r22580Merged filerepo-work branch:...tstarling21:02, 30 May 2007
r22581*clean up commentaaron23:06, 30 May 2007
r22584*Add newFromArchiveName()aaron00:35, 31 May 2007
r22585Fixed a few filerepo bugs, added some documentationtstarling01:43, 31 May 2007