r107937 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r107936‎ | r107937 | r107938 >
Date:20:46, 3 January 2012
Author:grafzahl
Status:deferred
Tags:score 
Comment:
Internal housekeeping:

* Do most configuration in a centralised place, namely Score::render().
* As a result, change parameters/options for the generate functions.
* Delete temporary directory in all cases.
* Create input file to lilypond process earlier instead of juggling the score
code around.
* Rename Score::createFactory() to the more general Score::createDirectory()
and add a chmod mode parameter.
* Consolidate file movement and directory creation.
* As a result, the score-nofactory message is not needed any more.
Modified paths:
  • /trunk/extensions/Score/Score.body.php (modified) (history)
  • /trunk/extensions/Score/Score.i18n.php (modified) (history)

Diff [purge]

Index: trunk/extensions/Score/Score.i18n.php
@@ -39,12 +39,11 @@
4040 'score-invalidlang' => 'Invalid score language lang="$1". Currently recognised languages are lang="lilypond" (the default) and lang="ABC".',
4141 'score-invalidoggoverride' => 'The file you specified with override_ogg is invalid. Please specify the file name only, omit <nowiki>[[…]]</nowiki> and the "File:" prefix.',
4242 'score-noabcinput' => 'ABC source file $1 could not be created.',
43 - 'score-nofactory' => 'Failed to create LilyPond factory directory.',
4443 'score-noimages' => 'No score images were generated. Please check your score code.',
4544 'score-noinput' => 'Failed to create LilyPond input file $1.',
4645 'score-noogghandler' => 'Ogg/Vorbis conversion requires an installed and configured OggHandler extension, see [//www.mediawiki.org/wiki/Extension:OggHandler Extension:OggHandler].',
4746 'score-nomidi' => 'No MIDI file generated despite being requested. If you are working in raw LilyPond mode, make sure to provide a proper \midi block.',
48 - 'score-nooutput' => 'Failed to create LilyPond image directory $1.',
 47+ 'score-nooutput' => 'Failed to create output directory $1.',
4948 'score-notexecutable' => 'Could not execute LilyPond: $1 is not an executable file. Make sure <code>$wgScoreLilyPond</code> is set correctly.',
5049 'score-novorbislink' => 'Unable to generate Ogg/Vorbis link: $1',
5150 'score-oggconversionerr' => 'Unable to convert MIDI to Ogg/Vorbis:
@@ -75,12 +74,11 @@
7675 'score-invalidlang' => 'Displayed if the lang="…" attribute contains an unrecognised score language. $1 is the unrecognised language.',
7776 'score-invalidoggoverride' => 'Displayed if the file specified with the override_ogg="…" attribute is invalid.',
7877 'score-noabcinput' => 'Displayed if an ABC source file could not be created for lang="ABC". $1 is the path to the file that could not be created.',
79 - 'score-nofactory' => 'Displayed if the LilyPond/ImageMagick working directory cannot be created.',
8078 'score-noimages' => 'Displayed if no score images were rendered.',
8179 'score-noinput' => 'Displayed if the LilyPond input file cannot be created. $1 is the path to the input file.',
8280 'score-noogghandler' => 'Displayed if Ogg/Vorbis rendering was requested without the OggHandler extension installed.',
8381 'score-nomidi' => 'Displayed if MIDI file generation was requested but no MIDI file was generated.',
84 - 'score-nooutput' => 'Displayed if the LilyPond image/midi dir cannot be created. $1 is the name of the directory.',
 82+ 'score-nooutput' => 'Displayed if an output directory could not be created. $1 is the name of the directory.',
8583 'score-notexecutable' => 'Displayed if LilyPond binary cannot be executed. $1 is the path to the LilyPond binary.',
8684 'score-novorbislink' => 'Displayed if an Ogg/Vorbis link could not be generated. $1 is the explanation why.',
8785 'score-oggconversionerr' => 'Displayed if the MIDI to Ogg/Vorbis conversion failed. $1 is the error (generally big block of text in a pre tag)',
Index: trunk/extensions/Score/Score.body.php
@@ -90,7 +90,7 @@
9191 }
9292
9393 /**
94 - * Score class
 94+ * Score class.
9595 */
9696 class Score {
9797 /**
@@ -165,15 +165,17 @@
166166 * Creates the specified directory if it does not exist yet.
167167 * Otherwise does nothing.
168168 *
169 - * @param $path string path to directory to be created.
 169+ * @param $path string Path to directory to be created.
 170+ * @param $mode integer Chmod value of the new directory.
170171 *
171 - * @throws ScoreException if the directory does not exist and could not be created.
 172+ * @throws ScoreException if the directory does not exist and could not
 173+ * be created.
172174 */
173 - private static function createFactory( $path ) {
 175+ private static function createDirectory( $path, $mode = null ) {
174176 if ( !is_dir( $path ) ) {
175 - $rc = wfMkdirParents( $path, 0700, __METHOD__ );
 177+ $rc = wfMkdirParents( $path, $mode, __METHOD__ );
176178 if ( !$rc ) {
177 - throw new ScoreException( wfMessage( 'score-nofactory' ) );
 179+ throw new ScoreException( wfMessage( 'score-nooutput', $path ) );
178180 }
179181 }
180182 }
@@ -189,11 +191,17 @@
190192 * @return Image link HTML, and possibly anchor to MIDI file.
191193 */
192194 public static function render( $code, array $args, Parser $parser, PPFrame $frame ) {
 195+ global $wgTmpDirectory, $wgUploadDirectory, $wgUploadPath;
 196+
193197 $prof = new ScopedProfiling( __METHOD__ );
194198
195199 try {
196 - $options = array();
 200+ $options = array(); // options to self::generateHTML()
197201
 202+ /* temporary working directory to use */
 203+ $fuzz = md5( mt_rand() );
 204+ $options['factory_directory'] = $wgTmpDirectory . "/MWLP.$fuzz";
 205+
198206 /* Score language selection */
199207 if ( array_key_exists( 'lang', $args ) ) {
200208 $options['lang'] = $args['lang'];
@@ -204,6 +212,24 @@
205213 throw new ScoreException( wfMessage( 'score-invalidlang', $options['lang'] ) );
206214 }
207215
 216+ /* image file path and URL prefixes */
 217+ $imageCacheName = wfBaseConvert( sha1( $code ), 16, 36, 31 );
 218+ $imagePrefixEnd = self::LILYPOND_DIR_NAME . '/'
 219+ . "{$imageCacheName[0]}/{$imageCacheName[0]}{$imageCacheName[1]}/$imageCacheName";
 220+ $options['image_path_prefix'] = "$wgUploadDirectory/$imagePrefixEnd";
 221+ $options['image_url_prefix'] = "$wgUploadPath/$imagePrefixEnd";
 222+
 223+ /* Midi linking? */
 224+ if ( array_key_exists( 'midi', $args ) ) {
 225+ $options['link_midi'] = $args['midi'];
 226+ } else {
 227+ $options['link_midi'] = false;
 228+ }
 229+ if ( $options['link_midi'] ) {
 230+ $options['midi_path'] = "{$options['image_path_prefix']}.midi";
 231+ $options['midi_url'] = "{$options['image_url_prefix']}.midi";
 232+ }
 233+
208234 /* Override OGG file? */
209235 if ( array_key_exists( 'override_ogg', $args ) ) {
210236 $t = Title::newFromText( $args['override_ogg'], NS_FILE );
@@ -213,29 +239,27 @@
214240 if ( !$t->isKnown() ) {
215241 throw new ScoreException( wfMessage( 'score-oggoverridenotfound' ) );
216242 }
217 - $options['override_ogg'] = $args['override_ogg'];
 243+ $options['override_ogg'] = true;
 244+ $options['ogg_name'] = $args['override_ogg'];
218245 } else {
219246 $options['override_ogg'] = false;
220247 }
221248
222249 /* Vorbis rendering? */
223250 if ( array_key_exists( 'vorbis', $args ) ) {
224 - $options['vorbis'] = $args['vorbis'];
 251+ $options['generate_vorbis'] = $args['vorbis'];
225252 } else {
226 - $options['vorbis'] = false;
 253+ $options['generate_vorbis'] = false;
227254 }
228 - if ( $options['vorbis'] && !( class_exists( 'OggHandler' ) && class_exists( 'OggAudioDisplay' ) ) ) {
 255+ if ( $options['generate_vorbis'] && !( class_exists( 'OggHandler' ) && class_exists( 'OggAudioDisplay' ) ) ) {
229256 throw new ScoreException( wfMessage( 'score-noogghandler' ) );
230257 }
231 - if ( $options['vorbis'] && ( $options['override_ogg'] !== false ) ) {
 258+ if ( $options['generate_vorbis'] && ( $options['override_ogg'] !== false ) ) {
232259 throw new ScoreException( wfMessage( 'score-vorbisoverrideogg' ) );
233260 }
234 -
235 - /* Midi rendering? */
236 - if ( array_key_exists( 'midi', $args ) ) {
237 - $options['midi'] = $args['midi'];
238 - } else {
239 - $options['midi'] = false;
 261+ if ( $options['generate_vorbis'] ) {
 262+ $options['ogg_path'] = "{$options['image_path_prefix']}.ogg";
 263+ $options['ogg_url'] = "{$options['image_url_prefix']}.ogg";
240264 }
241265
242266 /* Raw rendering? */
@@ -257,74 +281,78 @@
258282 * Generates the HTML code for a score tag.
259283 *
260284 * @param $parser Parser MediaWiki parser.
261 - * @param $code score code.
262 - * @param $options array of music rendering options. Available options keys are:
263 - * * lang: score language,
264 - * * vorbis: whether to create an Ogg/Vorbis file in an OggHandler,
265 - * * midi: whether to link to a MIDI file,
266 - * * raw: whether to assume raw LilyPond code.
 285+ * @param $code string Score code.
 286+ * @param $options array of rendering options.
 287+ * The options keys are:
 288+ * * factory_directory: string Path to directory in which files
 289+ * may be generated without stepping on someone else's
 290+ * toes. The directory may not exist yet. Required.
 291+ * * generate_vorbis: bool Whether to create an Ogg/Vorbis file in
 292+ * an OggHandler. If set to true, the override_ogg option
 293+ * must be set to false. Required.
 294+ * * image_path_prefix: string Prefix to the local image path.
 295+ * Required.
 296+ * * image_url_prefix: string Prefix to the image URL. Required.
 297+ * * lang: string Score language. Required.
 298+ * * link_midi: bool Whether to link to a MIDI file. Required.
 299+ * * midi_path: string Local MIDI path. Required if the link_midi
 300+ * option is set to true, ignored otherwise.
 301+ * * midi_url: string MIDI URL. Required if the link_midi option
 302+ * is set to true, ignored otherwise.
 303+ * * ogg_name: string Name of the OGG file. Required if the
 304+ * override_ogg option is set to true, ignored otherwise.
 305+ * * ogg_path: string Local Ogg/Vorbis path. Required if the
 306+ * generate_vorbis option is set to true, ignored
 307+ * otherwise.
 308+ * * ogg_url: string Ogg/Vorbis URL. Required if the
 309+ * generate_vorbis option is set to true, ignored
 310+ * otherwise.
 311+ * * override_ogg: bool Whether to generate a wikilink to a
 312+ * user-provided OGG file. If set to true, the vorbis
 313+ * option must be set to false. Required.
 314+ * * raw: bool Whether to assume raw LilyPond code. Ignored if the
 315+ * language is not lilypond, required otherwise.
267316 *
268317 * @return string HTML.
269318 *
270319 * @throws ScoreException if an error occurs.
271320 */
272321 private static function generateHTML( &$parser, $code, $options ) {
273 - global $wgUploadDirectory, $wgUploadPath, $wgTmpDirectory, $wgOut;
 322+ global $wgOut;
274323
275324 $prof = new ScopedProfiling( __METHOD__ );
276325
277 - /* Various paths and file names */
278 - $cacheName = md5( $code ); /* always use MD5 of $code, regardless of language */
279 - $cacheSubdir = "{$cacheName[0]}/{$cacheName[0]}{$cacheName[1]}";
280 - $lilypondDir = $wgUploadDirectory . '/' . self::LILYPOND_DIR_NAME . '/' . $cacheSubdir;
281 - $lilypondPath = $wgUploadPath . '/' . self::LILYPOND_DIR_NAME . '/' . $cacheSubdir;
282 - $filePrefix = "$lilypondDir/$cacheName";
283 - $pathPrefix = "$lilypondPath/$cacheName";
284 - $midi = "$filePrefix.midi";
285 - $midiPath = "$pathPrefix.midi";
286 - $image = "$filePrefix.png";
287 - $imagePath = "$pathPrefix.png";
288 - $multiFormat = "$filePrefix-%d.png"; // for multi-page scores
289 - $multiPathFormat = "$pathPrefix-%d.png";
290 - $multi1 = "$filePrefix-1.png";
291 - $ogg = "$filePrefix.ogg";
292 - $oggPath = "$pathPrefix.ogg";
293 -
294 - /* Make sure $lilypondDir exists */
295 - if ( !file_exists( $lilypondDir ) ) {
296 - $rc = wfMkdirParents( $lilypondDir, null, __METHOD__ );
297 - if ( !$rc ) {
298 - throw new ScoreException( wfMessage( 'score-nooutput', self::LILYPOND_DIR_NAME ) );
299 - }
300 - }
301 -
302 - /* Generate working environment data */
303 - $fuzz = md5( mt_rand() );
304 - $factoryDirectory = $wgTmpDirectory . "/MWLP.$fuzz";
305 -
306326 try {
307327 /* Generate PNG and MIDI files if necessary */
308 - if ( ( !file_exists( $image ) && !file_exists( $multi1 ) )
309 - || ( ( $options['midi'] || $options['vorbis'] ) && !file_exists( $midi ) ) ) {
310 - self::generatePngAndMidi( $code, $options, $filePrefix, $factoryDirectory );
 328+ $imagePath = "{$options['image_path_prefix']}.png";
 329+ $multi1Path = "{$options['image_path_prefix']}-1.png";
 330+ if ( !$options['link_midi'] && $options['generate_vorbis'] && !file_exists( $options['ogg_path'] ) ) {
 331+ /* We need a MIDI file to generate a Vorbis file */
 332+ $options['midi_path'] = "{$options['factory_directory']}/file.midi";
311333 }
 334+ if ( ( !file_exists( $imagePath ) && !file_exists( $multi1Path ) )
 335+ || ( array_key_exists( 'midi_path', $options ) && !file_exists( $options['midi_path'] ) ) ) {
 336+ self::generatePngAndMidi( $code, $options );
 337+ }
312338
313339 /* Generate Ogg/Vorbis file if necessary */
314 - if ( $options['vorbis'] && !file_exists( $ogg ) ) {
315 - self::generateOgg( $options, $filePrefix, $factoryDirectory );
 340+ if ( $options['generate_vorbis'] && !file_exists( $options['ogg_path'] ) ) {
 341+ self::generateOgg( $options );
316342 }
317343
318344 /* return output link(s) */
319 - if ( file_exists( $image ) ) {
 345+ if ( file_exists( $imagePath ) ) {
320346 $link = Html::rawElement( 'img', array(
321 - 'src' => $imagePath,
 347+ 'src' => "{$options['image_url_prefix']}.png",
322348 'alt' => $code,
323349 ) );
324 - } elseif ( file_exists( $multi1 ) ) {
 350+ } elseif ( file_exists( $multi1Path ) ) {
 351+ $multiPathFormat = "{$options['image_path_prefix']}-%d.png";
 352+ $multiUrlFormat = "{$options['image_url_prefix']}-%d.png";
325353 $link = '';
326 - for ( $i = 1; file_exists( sprintf( $multiFormat, $i ) ); ++$i ) {
 354+ for ( $i = 1; file_exists( sprintf( $multiPathFormat, $i ) ); ++$i ) {
327355 $link .= Html::rawElement( 'img', array(
328 - 'src' => sprintf( $multiPathFormat, $i ),
 356+ 'src' => sprintf( $multiUrlFormat, $i ),
329357 'alt' => wfMessage( 'score-page' )->inContentLanguage()->numParams( $i )->plain()
330358 ) );
331359 }
@@ -332,14 +360,20 @@
333361 /* No images; this may happen in raw mode or when the user omits the score code */
334362 throw new ScoreException( wfMessage( 'score-noimages' ) );
335363 }
336 - if ( $options['midi'] ) {
337 - $link = Html::rawElement( 'a', array( 'href' => $midiPath ), $link );
 364+ if ( $options['link_midi'] ) {
 365+ $link = Html::rawElement( 'a', array( 'href' => $options['midi_url'] ), $link );
338366 }
339 - if ( $options['vorbis'] ) {
 367+ if ( $options['generate_vorbis'] ) {
340368 try {
341369 $oh = new OggHandler();
342370 $oh->setHeaders( $wgOut );
343 - $oad = new OggAudioDisplay( new UnregisteredLocalFile( false, false, $ogg ), $oggPath, self::DEFAULT_PLAYER_WIDTH, 0, 0, $oggPath, false );
 371+ $oad = new OggAudioDisplay(
 372+ new UnregisteredLocalFile( false, false, $options['ogg_path'] ),
 373+ $options['ogg_url'],
 374+ self::DEFAULT_PLAYER_WIDTH, 0, 0,
 375+ $options['ogg_url'],
 376+ false
 377+ );
344378 $link .= $oad->toHtml( array( 'alt' => $code ) );
345379 } catch ( Exception $e ) {
346380 throw new ScoreException( wfMessage( 'score-novorbislink', $e->getMessage() ), 0, $e );
@@ -347,49 +381,53 @@
348382 }
349383 if ( $options['override_ogg'] !== false ) {
350384 try {
351 - $link .= $parser->recursiveTagParse( "[[File:{$options['override_ogg']}]]" );
 385+ $link .= $parser->recursiveTagParse( "[[File:{$options['ogg_name']}]]" );
352386 } catch ( Exception $e ) {
353387 throw new ScoreException( wfMessage( 'score-novorbislink', $e->getMessage() ), 0, $e );
354388 }
355389 }
356390 } catch ( Exception $e ) {
357 - self::eraseFactory( $factoryDirectory );
 391+ self::eraseFactory( $options['factory_directory'] );
358392 throw $e;
359393 }
360394
 395+ self::eraseFactory( $options['factory_directory'] );
 396+
361397 return $link;
362398 }
363399
364400 /**
365401 * Generates score PNG file(s) and possibly a MIDI file.
366402 *
367 - * @param $code string score code.
368 - * @param $options array rendering options, see Score::generateHTML() for explanation.
369 - * @param $filePrefix string prefix for the generated files.
370 - * @param $factoryDirectory string directory of the working environment.
 403+ * @param $code string Score code.
 404+ * @param $options array Rendering options. They are the same as for
 405+ * Score::generateHTML() with the exception that the link_midi
 406+ * option is ignored and the midi file is generated if the
 407+ * midi_path option is present.
371408 *
372409 * @throws ScoreException on error.
373410 */
374 - private static function generatePngAndMidi( $code, $options, $filePrefix, $factoryDirectory ) {
 411+ private static function generatePngAndMidi( $code, $options ) {
375412 global $wgScoreLilyPond, $wgScoreTrim;
376413
377414 $prof = new ScopedProfiling( __METHOD__ );
378415
379416 /* Various filenames */
380 - $ly = "$filePrefix.ly";
381 - $midi = "$filePrefix.midi";
382 - $image = "$filePrefix.png";
383 - $multiFormat = "$filePrefix-%d.png";
 417+ $image = "{$options['image_path_prefix']}.png";
 418+ $multiFormat = "{$options['image_path_prefix']}-%d.png";
384419
385420 /* delete old files if necessary */
386 - self::cleanupFile( $midi );
 421+ if ( array_key_exists( 'midi_path', $options ) ) {
 422+ self::cleanupFile( $options['midi_path'] );
 423+ }
387424 self::cleanupFile( $image );
388425 for ( $i = 1; file_exists( $f = sprintf( $multiFormat, $i ) ); ++$i ) {
389426 self::cleanupFile( $f );
390427 }
391428
392429 /* Create the working environment */
393 - self::createFactory( $factoryDirectory );
 430+ $factoryDirectory = $options['factory_directory'];
 431+ self::createDirectory( $factoryDirectory, 0700 );
394432 $factoryLy = "$factoryDirectory/file.ly";
395433 $factoryMidi = "$factoryDirectory/file.midi";
396434 $factoryImage = "$factoryDirectory/file.png";
@@ -397,30 +435,23 @@
398436 $factoryMultiFormat = "$factoryDirectory/file-%d.png";
399437 $factoryMultiTrimmedFormat = "$factoryDirectory/file-%d-trimmed.png";
400438
401 - /* Determine which LilyPond code to use */
 439+ /* Generate LilyPond input file */
402440 if ( $options['lang'] == 'lilypond' ) {
403441 if ( $options['raw'] ) {
404442 $lilypondCode = $code;
405443 } else {
406444 $lilypondCode = self::embedLilypondCode( $code, $options );
407445 }
408 - } else {
409 - wfSuppressWarnings();
410 - $lilypondCode = file_get_contents( $ly ); // may legitimately fail
411 - wfRestoreWarnings();
412 - if ( $lilypondCode === false ) {
413 - /* (re-)generate .ly file */
414 - $lilypondCode = self::generateLilypond( $code, $options, $filePrefix, $factoryDirectory );
415 - }
416 - }
417 -
418 - /* generate lilypond output files in working environment */
419 - if ( !file_exists( $factoryLy ) ) {
420446 $rc = file_put_contents( $factoryLy, $lilypondCode );
421447 if ( $rc === false ) {
422448 throw new ScoreException( wfMessage( 'score-noinput', $factoryLy ) );
423449 }
 450+ } else {
 451+ $options['lilypond_path'] = $factoryLy;
 452+ self::generateLilypond( $code, $options );
424453 }
 454+
 455+ /* generate lilypond output files in working environment */
425456 $oldcwd = getcwd();
426457 if ( $oldcwd === false ) {
427458 throw new ScoreException( wfMessage( 'score-getcwderr' ) );
@@ -446,7 +477,7 @@
447478 if ( $rc2 != 0 ) {
448479 self::throwCallException( wfMessage( 'score-compilererr' ), $output );
449480 }
450 - if ( ( $options['midi'] || $options['vorbis'] ) && !file_exists( $factoryMidi ) ) {
 481+ if ( array_key_exists( 'midi_path', $options ) && !file_exists( $factoryMidi ) ) {
451482 throw new ScoreException( wfMessage( 'score-nomidi' ) );
452483 }
453484
@@ -465,25 +496,25 @@
466497
467498 /* move files to proper places */
468499 $rc = true;
469 - if ( file_exists( $factoryMidi ) ) {
470 - $rc = $rc && rename( $factoryMidi, $midi );
 500+ if ( array_key_exists( 'midi_path', $options ) ) {
 501+ self::renameFile( $factoryMidi, $options['midi_path'] );
471502 }
472503 if ( file_exists( $factoryImageTrimmed ) ) {
473 - $rc = $rc && rename( $factoryImageTrimmed, $image );
 504+ self::renameFile( $factoryImageTrimmed, $image );
474505 }
475506 for ( $i = 1; file_exists( $f = sprintf( $factoryMultiTrimmedFormat, $i ) ); ++$i ) {
476 - $rc = $rc && rename( $f, sprintf( $multiFormat, $i ) );
 507+ self::renameFile( $f, sprintf( $multiFormat, $i ) );
477508 }
478 - if ( !$rc ) {
479 - throw new ScoreException( wfMessage( 'score-renameerr' ) );
480 - }
481509 }
482510
483511 /**
484512 * Embeds simple LilyPond code in a score block.
485513 *
486 - * @param $lilypondCode string simple LilyPond code.
487 - * @param $options array rendering options, see Score::generateHTML() for explanation.
 514+ * @param $lilypondCode string Simple LilyPond code.
 515+ * @param $options array Rendering options. The are the same as for
 516+ * Score::generateHTML() except that the link_midi option is
 517+ * ignored and a MIDI block is embedded if and only if the
 518+ * midi_path option is present.
488519 *
489520 * @return string Raw lilypond code.
490521 *
@@ -509,7 +540,7 @@
510541 . "\\score {\n"
511542 . $lilypondCode
512543 . "\t\\layout { }\n"
513 - . ( ( $options['midi'] || $options['vorbis'] ) ? "\t\\midi { }\n" : "" )
 544+ . ( array_key_exists( 'midi_path', $options ) ? "\t\\midi { }\n" : "" )
514545 . "}\n";
515546 return $raw;
516547 }
@@ -517,25 +548,23 @@
518549 /**
519550 * Generates an Ogg/Vorbis file from a MIDI file using timidity.
520551 *
521 - * @param $options array rendering options, see Score::generateHTML() for explanation.
522 - * @param $filePrefix string prefix for the generated Ogg file.
523 - * @param $factoryDirectory string directory of the working environment.
 552+ * @param $options array Rendering options. They are the same as for
 553+ * Score::generateHTML(), except that the midi_path option must
 554+ * be present.
524555 *
525556 * @throws ScoreException if an error occurs.
526557 */
527 - private static function generateOgg( $options, $filePrefix, $factoryDirectory ) {
 558+ private static function generateOgg( $options ) {
528559 global $wgScoreTimidity;
529560
530561 $prof = new ScopedProfiling( __METHOD__ );
531562
532563 /* Working environment */
533 - self::createFactory( $factoryDirectory );
534 - $factoryOgg = "$factoryDirectory/file.ogg";
535 - $midi = "$filePrefix.midi";
536 - $ogg = "$filePrefix.ogg";
 564+ self::createDirectory( $options['factory_directory'], 0700 );
 565+ $factoryOgg = "{$options['factory_directory']}/file.ogg";
537566
538567 /* Delete old file if necessary */
539 - self::cleanupFile( $ogg );
 568+ self::cleanupFile( $options['ogg_path'] );
540569
541570 /* Run timidity */
542571 if ( !is_executable( $wgScoreTimidity ) ) {
@@ -544,7 +573,7 @@
545574 $cmd = wfEscapeShellArg( $wgScoreTimidity )
546575 . ' -Ov' // Vorbis output
547576 . ' --output-file=' . wfEscapeShellArg( $factoryOgg )
548 - . ' ' . wfEscapeShellArg( $midi )
 577+ . ' ' . wfEscapeShellArg( $options['midi_path'] )
549578 . ' 2>&1';
550579 $output = wfShellExec( $cmd, $rc );
551580 if ( ( $rc != 0 ) || !file_exists( $factoryOgg ) ) {
@@ -552,36 +581,30 @@
553582 }
554583
555584 /* Move resultant file to proper place */
556 - $rc = rename( $factoryOgg, $ogg );
557 - if ( !$rc ) {
558 - throw new ScoreException( wfMessage( 'score-renameerr' ) );
559 - }
 585+ self::renameFile( $factoryOgg, $options['ogg_path'] );
560586 }
561587
562588 /**
563589 * Generates LilyPond code.
564590 *
565 - * @param $code string score code.
566 - * @param $options array rendering options, see Score::generateHTML() for explanation.
567 - * @param $filePrefix string prefix for the generated file.
568 - * @param $factoryDirectory string directory of the working environment.
 591+ * @param $code string Score code.
 592+ * @param $options array Rendering options. They are the same as for
 593+ * Score::generateHTML(), with the following addition:
 594+ * * lilypond_path: path to the LilyPond file that is to be
 595+ * generated.
569596 *
570 - * @return the generated LilyPond code.
571 - *
572597 * @throws ScoreException if an error occurs.
573598 */
574 - private static function generateLilypond( $code, $options, $filePrefix, $factoryDirectory ) {
 599+ private static function generateLilypond( $code, $options ) {
575600 $prof = new ScopedProfiling( __METHOD__ );
576601
577 - $ly = "$filePrefix.ly";
578 -
579602 /* Delete old file if necessary */
580 - self::cleanupFile( $ly );
 603+ self::cleanupFile( $options['lilypond_path'] );
581604
582605 /* Generate LilyPond code by score language */
583606 switch ( $options['lang'] ) {
584607 case 'ABC':
585 - $lilypondCode = self::generateLilypondFromAbc( $code, $factoryDirectory );
 608+ self::generateLilypondFromAbc( $code, $options );
586609 break;
587610 case 'lilypond':
588611 throw new MWException( 'lang="lilypond" in ' . __METHOD__ . ". This should not happen.\n" );
@@ -589,33 +612,24 @@
590613 throw new MWException( 'Unknown score language in ' . __METHOD__ . ". This should not happen.\n" );
591614 }
592615
593 - /* Create LilyPond file and return the code */
594 - $rc = file_put_contents( $ly, $lilypondCode );
595 - if ( $rc === false ) {
596 - self::debug( "Unable to write LilyPond code to $ly.\n" );
597 - }
598 -
599 - return $lilypondCode;
600616 }
601617
602618 /**
603619 * Runs abc2ly, creating the LilyPond input file.
604620 *
605 - * $code ABC code.
606 - * $factoryDirectory Working environment. As a side-effect, the
607 - * LilyPond input file is created as "file.ly" in this directory.
 621+ * @param $code string ABC code.
 622+ * @param $options array Rendering options. They are the same as for
 623+ * Score::generateLilypond().
608624 *
609 - * @param $code string
610 - * @param $factoryDirectory string
611 - * @return string the generated LilyPond code.
612 - *
613625 * @throws ScoreException if the conversion fails.
614626 */
615 - private function generateLilypondFromAbc( $code, $factoryDirectory ) {
 627+ private static function generateLilypondFromAbc( $code, $options ) {
616628 global $wgScoreAbc2Ly;
617629
618630 $prof = new ScopedProfiling( __METHOD__ );
619631
 632+ /* File names */
 633+ $factoryDirectory = $options['factory_directory'];
620634 $factoryAbc = "$factoryDirectory/file.abc";
621635 $factoryLy = "$factoryDirectory/file.ly";
622636
@@ -636,13 +650,9 @@
637651 . ' ' . wfEscapeShellArg( $factoryAbc )
638652 . ' 2>&1';
639653 $output = wfShellExec( $cmd, $rc );
640 - if ( $rc != 0 ) {
 654+ if ( ( $rc != 0 ) || !file_exists( $factoryLy ) ) {
641655 self::throwCallException( wfMessage( 'score-abcconversionerr' ), $output );
642656 }
643 - if ( !file_exists( $factoryLy ) ) {
644 - /* Occasionally, abc2ly will return exit code 0 but not create an output file */
645 - self::throwCallException( wfMessage( 'score-abcconversionerr' ), $output );
646 - }
647657
648658 /* The output file has a tagline which should be removed in a wiki context */
649659 $lyData = file_get_contents( $factoryLy );
@@ -658,7 +668,8 @@
659669 throw new ScoreException( wfMessage( 'score-noinput', $factoryLy ) );
660670 }
661671
662 - return $lyData;
 672+ /* Move resultant file to proper place */
 673+ self::renameFile( $factoryLy, $options['lilypond_path'] );
663674 }
664675
665676 /**
@@ -708,6 +719,23 @@
709720 }
710721
711722 /**
 723+ * Renames a file, making sure that the target directory exists.
 724+ * Do not use with uncanonicalised paths.
 725+ *
 726+ * @param $oldname string Old file name.
 727+ * @param $newname string New file name.
 728+ *
 729+ * @throws ScoreException if an error occurs.
 730+ */
 731+ private static function renameFile( $oldname, $newname ) {
 732+ self::createDirectory( dirname( $newname ) );
 733+ $rc = rename( $oldname, $newname );
 734+ if ( !$rc ) {
 735+ throw new ScoreException( wfMessage( 'score-renameerr' ) );
 736+ }
 737+ }
 738+
 739+ /**
712740 * Deletes a file if it exists.
713741 *
714742 * @param $path string path to the file to be deleted.

Status & tagging log