Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
SimpleFFS.php
Go to the documentation of this file.
1<?php
18use UtfNormal\Validator;
19
20class SimpleFFS implements FFS {
21 public function supportsFuzzy() {
22 return 'no';
23 }
24
25 public function getFileExtensions() {
26 return [];
27 }
28
30 protected $group;
31 protected $writePath;
36 protected $extra;
37
38 private const RECORD_SEPARATOR = "\0";
39 private const PART_SEPARATOR = "\0\0\0\0";
40
41 public function __construct( FileBasedMessageGroup $group ) {
42 $this->setGroup( $group );
43 $conf = $group->getConfiguration();
44 $this->extra = $conf['FILES'];
45 }
46
48 public function setGroup( FileBasedMessageGroup $group ) {
49 $this->group = $group;
50 }
51
53 public function getGroup() {
54 return $this->group;
55 }
56
58 public function setWritePath( $writePath ) {
59 $this->writePath = $writePath;
60 }
61
63 public function getWritePath() {
64 return $this->writePath;
65 }
66
77 public function exists( $code = false ) {
78 if ( $code === false ) {
79 $code = $this->group->getSourceLanguage();
80 }
81
82 $filename = $this->group->getSourceFilePath( $code );
83 if ( $filename === null ) {
84 return false;
85 }
86
87 return file_exists( $filename );
88 }
89
98 public function read( $code ) {
99 if ( !$this->isGroupFfsReadable() ) {
100 return [];
101 }
102
103 if ( !$this->exists( $code ) ) {
104 return false;
105 }
106
107 $filename = $this->group->getSourceFilePath( $code );
108 $input = file_get_contents( $filename );
109 if ( $input === false ) {
110 throw new MWException( "Unable to read file $filename." );
111 }
112
113 if ( !StringUtils::isUtf8( $input ) ) {
114 throw new MWException( "Contents of $filename are not valid utf-8." );
115 }
116
117 $input = Validator::cleanUp( $input );
118
119 // Strip BOM mark
120 $input = ltrim( $input, "\u{FEFF}" );
121
122 try {
123 return $this->readFromVariable( $input );
124 } catch ( Exception $e ) {
125 throw new MWException( "Parsing $filename failed: " . $e->getMessage() );
126 }
127 }
128
137 public function readFromVariable( $data ) {
138 $parts = explode( self::PART_SEPARATOR, $data );
139
140 if ( count( $parts ) !== 2 ) {
141 throw new MWException( 'Wrong number of parts.' );
142 }
143
144 list( $authorsPart, $messagesPart ) = $parts;
145 $authors = explode( self::RECORD_SEPARATOR, $authorsPart );
146 $messages = [];
147
148 foreach ( explode( self::RECORD_SEPARATOR, $messagesPart ) as $line ) {
149 if ( $line === '' ) {
150 continue;
151 }
152
153 $lineParts = explode( '=', $line, 2 );
154
155 if ( count( $lineParts ) !== 2 ) {
156 throw new MWException( "Wrong number of parts in line $line." );
157 }
158
159 list( $key, $message ) = $lineParts;
160 $key = trim( $key );
161 $messages[$key] = $message;
162 }
163
164 $messages = $this->group->getMangler()->mangleArray( $messages );
165
166 return [
167 'AUTHORS' => $authors,
168 'MESSAGES' => $messages,
169 ];
170 }
171
178 public function write( MessageCollection $collection ) {
179 $writePath = $this->writePath;
180
181 if ( $writePath === null ) {
182 throw new MWException( 'Write path is not set.' );
183 }
184
185 if ( !file_exists( $writePath ) ) {
186 throw new MWException( "Write path '$writePath' does not exist." );
187 }
188
189 if ( !is_writable( $writePath ) ) {
190 throw new MWException( "Write path '$writePath' is not writable." );
191 }
192
193 $targetFile = $writePath . '/' . $this->group->getTargetFilename( $collection->code );
194
195 $targetFileExists = file_exists( $targetFile );
196
197 if ( $targetFileExists ) {
198 $this->tryReadSource( $targetFile, $collection );
199 } else {
200 $sourceFile = $this->group->getSourceFilePath( $collection->code );
201 $this->tryReadSource( $sourceFile, $collection );
202 }
203
204 $output = $this->writeReal( $collection );
205 if ( !$output ) {
206 return;
207 }
208
209 // Some file formats might have changing parts, such as timestamp.
210 // This allows the file handler to skip updating files, where only
211 // the timestamp would change.
212 if ( $targetFileExists ) {
213 $oldContent = $this->tryReadFile( $targetFile );
214 if ( !$this->shouldOverwrite( $oldContent, $output ) ) {
215 return;
216 }
217 }
218
219 wfMkdirParents( dirname( $targetFile ), null, __METHOD__ );
220 file_put_contents( $targetFile, $output );
221 }
222
229 public function writeIntoVariable( MessageCollection $collection ) {
230 $sourceFile = $this->group->getSourceFilePath( $collection->code );
231 $this->tryReadSource( $sourceFile, $collection );
232
233 return $this->writeReal( $collection );
234 }
235
240 protected function writeReal( MessageCollection $collection ) {
241 $output = '';
242
243 $authors = $collection->getAuthors();
244 $authors = $this->filterAuthors( $authors, $collection->code );
245
246 $output .= implode( self::RECORD_SEPARATOR, $authors );
247 $output .= self::PART_SEPARATOR;
248
249 $mangler = $this->group->getMangler();
250
252 foreach ( $collection as $key => $m ) {
253 $key = $mangler->unmangle( $key );
254 $trans = $m->translation();
255 $output .= "$key=$trans" . self::RECORD_SEPARATOR;
256 }
257
258 return $output;
259 }
260
270 protected function tryReadSource( $filename, MessageCollection $collection ) {
271 if ( !$this->isGroupFfsReadable() ) {
272 return;
273 }
274
275 $sourceText = $this->tryReadFile( $filename );
276
277 // No need to do anything in SimpleFFS if it's false,
278 // it only reads author data from it.
279 if ( $sourceText !== false ) {
280 $sourceData = $this->readFromVariable( $sourceText );
281
282 if ( isset( $sourceData['AUTHORS'] ) ) {
283 $collection->addCollectionAuthors( $sourceData['AUTHORS'] );
284 }
285 }
286 }
287
298 protected function tryReadFile( $filename ) {
299 if ( !$filename ) {
300 return false;
301 }
302
303 if ( !file_exists( $filename ) ) {
304 return false;
305 }
306
307 if ( !is_readable( $filename ) ) {
308 throw new MWException( "File $filename is not readable." );
309 }
310
311 $data = file_get_contents( $filename );
312 if ( $data === false ) {
313 throw new MWException( "Unable to read file $filename." );
314 }
315
316 return $data;
317 }
318
326 public function filterAuthors( array $authors, $code ) {
327 $groupId = $this->group->getId();
328 $configHelper = Services::getInstance()->getConfigHelper();
329
330 foreach ( $authors as $i => $v ) {
331 if ( $configHelper->isAuthorExcluded( $groupId, $code, $v ) ) {
332 unset( $authors[$i] );
333 }
334 }
335
336 return array_values( $authors );
337 }
338
339 public function isContentEqual( $a, $b ) {
340 return $a === $b;
341 }
342
343 public function shouldOverwrite( $a, $b ) {
344 return true;
345 }
346
353 public function isGroupFfsReadable(): bool {
354 try {
355 $ffs = $this->group->getFFS();
356 } catch ( RunTimeException $e ) {
357 if ( $e->getCode() === FileBasedMessageGroup::NO_FFS_CLASS ) {
358 return false;
359 }
360
361 throw $e;
362 }
363
364 return get_class( $ffs ) === get_class( $this );
365 }
366}
This class implements default behavior for file based message groups.
This file contains the class for core message collections implementation.
getAuthors()
Lists all translators that have contributed to the latest revisions of each translation.
addCollectionAuthors(array $authors, string $mode='append')
Add external authors (usually from the file).
Minimal service container.
Definition Services.php:40
filterAuthors(array $authors, $code)
Remove excluded authors.
exists( $code=false)
Returns true if the file for this message group in a given language exists.
Definition SimpleFFS.php:77
isGroupFfsReadable()
Check if the file format of the current group is readable by the file format system.
tryReadSource( $filename, MessageCollection $collection)
This tries to pick up external authors in the source files so that they are not lost if those authors...
shouldOverwrite( $a, $b)
Allows to skip writing the export output into a file.
isContentEqual( $a, $b)
Checks whether two strings are equal.
supportsFuzzy()
Query the capabilities of this FFS.
Definition SimpleFFS.php:21
writeReal(MessageCollection $collection)
read( $code)
Reads messages from the file in a given language and returns an array of AUTHORS, MESSAGES and possib...
Definition SimpleFFS.php:98
$extra
Stores the FILES section of the YAML configuration, which can be accessed for extra FFS class specifi...
Definition SimpleFFS.php:36
writeIntoVariable(MessageCollection $collection)
Read a collection and return it as a SimpleFFS formatted string.
getFileExtensions()
Return the commonly used file extensions for these formats.
Definition SimpleFFS.php:25
setGroup(FileBasedMessageGroup $group)
Definition SimpleFFS.php:48
write(MessageCollection $collection)
Write the collection to file.
setWritePath( $writePath)
Definition SimpleFFS.php:58
readFromVariable( $data)
Parse the message data given as a string in the SimpleFFS format and return it as an array of AUTHORS...
tryReadFile( $filename)
Read the contents of $filename and return it as a string.
Interface for file system support classes.
Definition FFS.php:21