Index: trunk/extensions/Score/Score.body.php |
— | — | @@ -62,6 +62,16 @@ |
63 | 63 | */ |
64 | 64 | class Score { |
65 | 65 | /** |
| 66 | + * Cache directory name. |
| 67 | + */ |
| 68 | + const LILYPOND_DIR_NAME = 'lilypond'; |
| 69 | + |
| 70 | + /** |
| 71 | + * Supported score languages |
| 72 | + */ |
| 73 | + private static $supportedLangs = array( 'lilypond', 'ABC' ); |
| 74 | + |
| 75 | + /** |
66 | 76 | * LilyPond version string. |
67 | 77 | * It defaults to null and is set the first time it is required. |
68 | 78 | */ |
— | — | @@ -130,7 +140,7 @@ |
131 | 141 | } |
132 | 142 | |
133 | 143 | /** |
134 | | - * Renders the lilypond code in a <score>…</score> tag. |
| 144 | + * Renders the score code (LilyPond, ABC, etc.) in a <score>…</score> tag. |
135 | 145 | * |
136 | 146 | * @param $code |
137 | 147 | * @param $args |
— | — | @@ -140,63 +150,242 @@ |
141 | 151 | * @return Image link HTML, and possibly anchor to MIDI file. |
142 | 152 | */ |
143 | 153 | public static function render( $code, array $args, Parser $parser, PPFrame $frame ) { |
144 | | - global $wgTmpDirectory; |
145 | | - |
146 | 154 | try { |
147 | | - /* generate name for working environment */ |
148 | | - $factoryPrefix = 'MWLP.'; |
149 | | - $fuzz = md5( mt_rand() ); |
150 | | - $factoryDirectory = $wgTmpDirectory . "/$factoryPrefix$fuzz"; |
| 155 | + /* Score language selection */ |
| 156 | + if ( array_key_exists( 'lang', $args ) ) { |
| 157 | + $lang = $args['lang']; |
| 158 | + } else { |
| 159 | + $lang = 'lilypond'; |
| 160 | + } |
| 161 | + if ( !in_array( $lang, self::$supportedLangs ) ) { |
| 162 | + throw new ScoreException( wfMessage( 'score-invalidlang', $lang ) ); |
| 163 | + } |
151 | 164 | |
152 | 165 | /* Midi rendering? */ |
153 | 166 | if ( array_key_exists( 'midi', $args ) ) { |
154 | | - $renderMidi = $args['midi']; |
| 167 | + $linkMidi = $args['midi']; |
155 | 168 | } else { |
156 | | - $renderMidi = false; |
| 169 | + $linkMidi = false; |
157 | 170 | } |
158 | 171 | |
159 | | - /* Score language selection */ |
160 | | - if ( array_key_exists( 'lang', $args ) ) { |
161 | | - $lang = $args['lang']; |
| 172 | + /* Raw rendering? */ |
| 173 | + if ( array_key_exists( 'raw', $args ) ) { |
| 174 | + $rawLilypond = $args['raw']; |
162 | 175 | } else { |
163 | | - $lang = 'lilypond'; |
| 176 | + $rawLilypond = false; |
164 | 177 | } |
165 | 178 | |
166 | | - /* Create lilypond input file */ |
167 | | - $lilypondFile = $factoryDirectory . '/file.ly'; |
168 | | - switch ( $lang ) { |
169 | | - case 'lilypond': |
170 | | - if ( !array_key_exists( 'raw', $args ) || !$args['raw'] ) { |
171 | | - $lilypondCode = self::embedLilypondCode( $code, $renderMidi ); |
| 179 | + $html = self::generateHTML( $code, $lang, $linkMidi, $rawLilypond ); |
| 180 | + } catch ( ScoreException $e ) { |
| 181 | + $html = "$e"; |
| 182 | + } |
| 183 | + |
| 184 | + return $html; |
| 185 | + } |
| 186 | + |
| 187 | + /** |
| 188 | + * Generates the HTML code for a score tag. |
| 189 | + * |
| 190 | + * @param $code score code. |
| 191 | + * @param $lang score language the code is in. |
| 192 | + * @param $linkMidi whether to generate a link to a MIDI file. |
| 193 | + * @param $rawLilypond whether to assume raw LilyPond code (ignored if $lang is not 'lilypond'). |
| 194 | + * |
| 195 | + * @return HTML. |
| 196 | + * |
| 197 | + * @throws ScoreException if an error occurs. |
| 198 | + */ |
| 199 | + private static function generateHTML( $code, $lang, $linkMidi, $rawLilypond ) { |
| 200 | + global $wgUploadDirectory, $wgUploadPath; |
| 201 | + |
| 202 | + /* Various paths and file names */ |
| 203 | + $lilypondDir = $wgUploadDirectory . '/' . self::LILYPOND_DIR_NAME; |
| 204 | + $lilypondPath = $wgUploadPath . '/' . self::LILYPOND_DIR_NAME; |
| 205 | + $cacheName = md5( $code ); /* always use MD5 of $code, regardless of language */ |
| 206 | + $filePrefix = "$lilypondDir/$cacheName"; |
| 207 | + $pathPrefix = "$lilypondPath/$cacheName"; |
| 208 | + $midi = "$filePrefix.midi"; |
| 209 | + $midiPath = "$pathPrefix.midi"; |
| 210 | + $image = "$filePrefix.png"; |
| 211 | + $imagePath = "$pathPrefix.png"; |
| 212 | + $multiFormat = "$filePrefix-%d.png"; // for multi-page scores |
| 213 | + $multiPathFormat = "$pathPrefix-%d.png"; |
| 214 | + $multi1 = "$filePrefix-1.png"; |
| 215 | + |
| 216 | + /* Make sure $lilypondDir exists */ |
| 217 | + if ( !file_exists( $lilypondDir ) ) { |
| 218 | + $rc = wfMkdirParents( $lilypondDir, null, __METHOD__ ); |
| 219 | + if ( !$rc ) { |
| 220 | + throw new ScoreException( wfMessage( 'score-nooutput', self::LILYPOND_DIR_NAME ) ); |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + /* Generate PNG and MIDI files if necessary */ |
| 225 | + if ( ( !file_exists( $image ) && !file_exists( $multi1 ) ) || ( $linkMidi && !file_exists( $midi ) ) ) { |
| 226 | + self::generatePngAndMidi( $code, $lang, $linkMidi, $rawLilypond, $filePrefix ); |
| 227 | + } |
| 228 | + |
| 229 | + /* return output link(s) */ |
| 230 | + if ( file_exists( $image ) ) { |
| 231 | + $link = Html::rawElement( 'img', array( |
| 232 | + 'src' => $imagePath, |
| 233 | + 'alt' => $code, |
| 234 | + ) ); |
| 235 | + } elseif ( file_exists( $multi1 ) ) { |
| 236 | + $link = ''; |
| 237 | + for ( $i = 1; file_exists( sprintf( $multiFormat, $i ) ); ++$i ) { |
| 238 | + $link .= Html::rawElement( 'img', array( |
| 239 | + 'src' => sprintf( $multiPathFormat, $i ), |
| 240 | + 'alt' => wfMessage( 'score-page' )->inContentLanguage()->numParams( $i )->plain() |
| 241 | + ) ); |
| 242 | + } |
| 243 | + } else { |
| 244 | + /* No images; this may actually happen in raw lilypond mode */ |
| 245 | + self::debug( "No output images $image or $multi1!\n" ); |
| 246 | + $link = 'No image'; |
| 247 | + } |
| 248 | + if ( $linkMidi ) { |
| 249 | + $link = Html::rawElement( 'a', array( 'href' => $midiPath ), $link ); |
| 250 | + } |
| 251 | + |
| 252 | + return $link; |
| 253 | + } |
| 254 | + |
| 255 | + /** |
| 256 | + * Generates score PNG file(s) and possibly a MIDI file. |
| 257 | + * |
| 258 | + * @param $code score code. |
| 259 | + * @param $lang language the score code is in. |
| 260 | + * @param $generateMidi whether to generate a MIDI file. |
| 261 | + * @param $rawLilypond whether to assume raw LilyPond code (ignored if $lang is not 'lilypond'). |
| 262 | + * @param $filePrefix prefix for the generated files. |
| 263 | + * |
| 264 | + * @throws ScoreException on error. |
| 265 | + */ |
| 266 | + private static function generatePngAndMidi( $code, $lang, $generateMidi, $rawLilypond, $filePrefix ) { |
| 267 | + global $wgTmpDirectory, $wgLilyPond, $wgScoreTrim; |
| 268 | + |
| 269 | + wfProfileIn( __METHOD__ ); |
| 270 | + |
| 271 | + try { |
| 272 | + /* Various filenames */ |
| 273 | + $ly = "$filePrefix.ly"; |
| 274 | + $midi = "$filePrefix.midi"; |
| 275 | + $image = "$filePrefix.png"; |
| 276 | + $multiFormat = "$filePrefix-%d.png"; |
| 277 | + |
| 278 | + /* delete old files if necessary */ |
| 279 | + $rc = true; |
| 280 | + if ( file_exists( $midi ) ) { |
| 281 | + $rc = $rc && unlink( $midi ); |
| 282 | + } |
| 283 | + if ( file_exists( $image ) ) { |
| 284 | + $rc = $rc && unlink( $image ); |
| 285 | + } |
| 286 | + for ( $i = 1; file_exists( $f = sprintf( $multiFormat, $i ) ); ++$i ) { |
| 287 | + $rc = $rc && unlink( $f ); |
| 288 | + } |
| 289 | + if ( !$rc ) { |
| 290 | + throw new ScoreException( wfMessage( 'score-cleanerr' ) ); |
| 291 | + } |
| 292 | + |
| 293 | + /* Create a working environment */ |
| 294 | + $fuzz = md5( mt_rand() ); |
| 295 | + $factoryDirectory = $wgTmpDirectory . "/MWLP.$fuzz"; |
| 296 | + self::createFactory( $factoryDirectory ); |
| 297 | + $factoryLy = "$factoryDirectory/file.ly"; |
| 298 | + $factoryMidi = "$factoryDirectory/file.midi"; |
| 299 | + $factoryImage = "$factoryDirectory/file.png"; |
| 300 | + $factoryImageTrimmed = "$factoryDirectory/file-trimmed.png"; |
| 301 | + $factoryMultiFormat = "$factoryDirectory/file-%d.png"; |
| 302 | + $factoryMultiTrimmedFormat = "$factoryDirectory/file-%d-trimmed.png"; |
| 303 | + |
| 304 | + /* Determine which LilyPond code to use */ |
| 305 | + if ( $lang == 'lilypond' ) { |
| 306 | + if ( $rawLilypond ) { |
| 307 | + $lilypondCode = $code; |
172 | 308 | } else { |
173 | | - $lilypondCode = $code; |
| 309 | + $lilypondCode = self::embedLilypondCode( $code, $generateMidi ); |
174 | 310 | } |
175 | | - self::createFactory( $factoryDirectory ); |
176 | | - $rc = file_put_contents( $lilypondFile, $lilypondCode ); |
| 311 | + } else { |
| 312 | + wfSuppressWarnings(); |
| 313 | + $lilypondCode = file_get_contents( $ly ); // may legitimately fail |
| 314 | + wfRestoreWarnings(); |
| 315 | + if ( $lilypondCode === false ) { |
| 316 | + /* (re-)generate .ly file */ |
| 317 | + $lilypondCode = self::generateLilypond( $code, $lang, $filePrefix, $factoryDirectory ); |
| 318 | + } |
| 319 | + } |
| 320 | + |
| 321 | + /* generate lilypond output files in working environment */ |
| 322 | + if ( !file_exists( $factoryLy ) ) { |
| 323 | + $rc = file_put_contents( $factoryLy, $lilypondCode ); |
177 | 324 | if ( $rc === false ) { |
178 | | - throw new ScoreException( wfMessage( 'score-noinput', $lilypondFile ) ); |
| 325 | + throw new ScoreException( wfMessage( 'score-noinput', $factoryLy ) ); |
179 | 326 | } |
180 | | - break; |
181 | | - case 'ABC': |
182 | | - self::runAbc2Ly( $code, $factoryDirectory ); |
183 | | - break; |
184 | | - default: |
185 | | - throw new ScoreException( wfMessage( 'score-invalidlang', $lang ) ); |
186 | 327 | } |
| 328 | + $oldcwd = getcwd(); |
| 329 | + if ( $oldcwd === false ) { |
| 330 | + throw new ScoreException( wfMessage( 'score-getcwderr' ) ); |
| 331 | + } |
| 332 | + $rc = chdir( $factoryDirectory ); |
| 333 | + if ( !$rc ) { |
| 334 | + throw new ScoreException( wfMessage( 'score-chdirerr', $factoryDirectory ) ); |
| 335 | + } |
| 336 | + if ( !is_executable( $wgLilyPond ) ) { |
| 337 | + throw new ScoreException( wfMessage( 'score-notexecutable', $wgLilyPond ) ); |
| 338 | + } |
| 339 | + $cmd = wfEscapeShellArg( $wgLilyPond ) |
| 340 | + . ' -dsafe=' |
| 341 | + . wfEscapeShellArg( '#t' ) |
| 342 | + . ' -dbackend=eps --png --header=texidoc ' |
| 343 | + . wfEscapeShellArg( $factoryLy ) |
| 344 | + . ' 2>&1'; |
| 345 | + $output = wfShellExec( $cmd, $rc2 ); |
| 346 | + $rc = chdir( $oldcwd ); |
| 347 | + if ( !$rc ) { |
| 348 | + throw new ScoreException( wfMessage( 'score-chdirerr', $oldcwd ) ); |
| 349 | + } |
| 350 | + if ( $rc2 != 0 ) { |
| 351 | + self::throwCallException( wfMessage( 'score-compilererr' ), $output ); |
| 352 | + } |
187 | 353 | |
188 | | - /* Run LilyPond */ |
189 | | - $html = self::runLilypond( $factoryDirectory, $renderMidi, $code ); |
190 | | - } catch ( ScoreException $e ) { |
| 354 | + /* trim output images if wanted */ |
| 355 | + if ( $wgScoreTrim ) { |
| 356 | + if ( file_exists( $factoryImage ) ) { |
| 357 | + $rc = self::trimImage( $factoryImage, $factoryImageTrimmed ); |
| 358 | + } |
| 359 | + for ( $i = 1; file_exists( $f = sprintf( $factoryMultiFormat, $i ) ); ++$i ) { |
| 360 | + $rc = self::trimImage( $f, sprintf( $factoryMultiTrimmedFormat, $i ) ); |
| 361 | + } |
| 362 | + } else { |
| 363 | + $factoryImageTrimmed = $factoryImage; |
| 364 | + $factoryMultiTrimmedFormat = $factoryMultiFormat; |
| 365 | + } |
| 366 | + |
| 367 | + /* move files to proper places */ |
| 368 | + $rc = true; |
| 369 | + if ( file_exists( $factoryMidi ) ) { |
| 370 | + $rc = $rc && rename( $factoryMidi, $midi ); |
| 371 | + } |
| 372 | + if ( file_exists( $factoryImageTrimmed ) ) { |
| 373 | + $rc = $rc && rename( $factoryImageTrimmed, $image ); |
| 374 | + } |
| 375 | + for ( $i = 1; file_exists( $f = sprintf( $factoryMultiTrimmedFormat, $i ) ); ++$i ) { |
| 376 | + $rc = $rc && rename( $f, sprintf( $multiFormat, $i ) ); |
| 377 | + } |
| 378 | + if ( !$rc ) { |
| 379 | + throw new ScoreException( wfMessage( 'score-renameerr' ) ); |
| 380 | + } |
| 381 | + } catch ( Exception $e ) { |
191 | 382 | self::eraseFactory( $factoryDirectory ); |
192 | | - return $e; |
| 383 | + wfProfileOut( __METHOD__ ); |
| 384 | + throw $e; |
193 | 385 | } |
194 | 386 | |
195 | 387 | /* tear down working environment */ |
196 | | - if ( !self::eraseFactory( $factoryDirectory ) ) { |
197 | | - self::debug( "Unable to delete temporary working directory.\n" ); |
198 | | - } |
199 | | - |
200 | | - return $html; |
| 388 | + self::eraseFactory( $factoryDirectory ); |
| 389 | + wfProfileOut( __METHOD__ ); |
201 | 390 | } |
202 | 391 | |
203 | 392 | /** |
— | — | @@ -235,25 +424,60 @@ |
236 | 425 | } |
237 | 426 | |
238 | 427 | /** |
| 428 | + * Generates LilyPond code. |
| 429 | + * |
| 430 | + * @param $code score code. |
| 431 | + * @param $lang language the score code is in (a supported language other than 'lilypond'). |
| 432 | + * @param $filePrefix prefix for the generated file. |
| 433 | + * @param $factoryDirectory directory of the working environment. |
| 434 | + * |
| 435 | + * @return the generated LilyPond code. |
| 436 | + * |
| 437 | + * @throws ScoreException if an error occurs. |
| 438 | + */ |
| 439 | + private static function generateLilypond( $code, $lang, $filePrefix, $factoryDirectory ) { |
| 440 | + $ly = "$filePrefix.ly"; |
| 441 | + |
| 442 | + switch ( $lang ) { |
| 443 | + case 'ABC': |
| 444 | + $lilypondCode = self::generateLilypondFromAbc( $code, $factoryDirectory ); |
| 445 | + break; |
| 446 | + case 'lilypond': |
| 447 | + throw new MWException( 'lang="lilypond" in ' . __METHOD__ . ". This should not happen.\n" ); |
| 448 | + default: |
| 449 | + throw new MWException( 'Unknown score language in ' . __METHOD__ . ". This should not happen.\n" ); |
| 450 | + } |
| 451 | + |
| 452 | + $rc = file_put_contents( $ly, $lilypondCode ); |
| 453 | + if ( $rc === false ) { |
| 454 | + self::debug( "Unable to copy $factoryLy to $ly.\n" ); |
| 455 | + } |
| 456 | + |
| 457 | + return $lilypondCode; |
| 458 | + } |
| 459 | + |
| 460 | + /** |
239 | 461 | * Runs abc2ly, creating the LilyPond input file. |
240 | 462 | * |
241 | 463 | * $code ABC code. |
242 | | - * $factoryDirectory Working environment. The LilyPond input file is |
243 | | - * created as "file.ly" in this directory. |
| 464 | + * $factoryDirectory Working environment. As a side-effect, the |
| 465 | + * LilyPond input file is created as "file.ly" in this directory. |
244 | 466 | * |
| 467 | + * @return the generated LilyPond code. |
| 468 | + * |
245 | 469 | * @throws ScoreException if the conversion fails. |
246 | 470 | */ |
247 | | - private function runAbc2Ly( $code, $factoryDirectory ) { |
| 471 | + private function generateLilypondFromAbc( $code, $factoryDirectory ) { |
248 | 472 | global $wgAbc2Ly; |
249 | 473 | |
250 | | - $abcFile = $factoryDirectory . '/file.abc'; |
251 | | - $lyFile = $factoryDirectory . '/file.ly'; |
| 474 | + $factoryAbc = "$factoryDirectory/file.abc"; |
| 475 | + $factoryLy = "$factoryDirectory/file.ly"; |
252 | 476 | |
253 | 477 | /* Create ABC input file */ |
254 | | - self::createFactory( $factoryDirectory ); |
255 | | - $rc = file_put_contents( $abcFile, ltrim( $code ) ); // abc2ly is picky about whitespace at the start of the file |
| 478 | + $rc = file_put_contents( $factoryAbc, $code ); |
| 479 | + // FIXME: ltrim( $code ) ); // abc2ly is picky about whitespace at the start of the file |
256 | 480 | if ( $rc === false ) { |
257 | | - throw new ScoreException( wfMessage( 'score-noabcinput', $abcFile ) ); |
| 481 | + throw new ScoreException( wfMessage( 'score-noabcinput', $factoryAbc ) ); |
258 | 482 | } |
259 | 483 | |
260 | 484 | /* Convert to LilyPond file */ |
— | — | @@ -263,210 +487,33 @@ |
264 | 488 | |
265 | 489 | $cmd = wfEscapeShellArg( $wgAbc2Ly ) |
266 | 490 | . ' -s' |
267 | | - . ' --output=' . wfEscapeShellArg( $lyFile ) |
268 | | - . ' ' . wfEscapeShellArg( $abcFile ) |
| 491 | + . ' --output=' . wfEscapeShellArg( $factoryLy ) |
| 492 | + . ' ' . wfEscapeShellArg( $factoryAbc ) |
269 | 493 | . ' 2>&1'; |
270 | 494 | $output = wfShellExec( $cmd, $rc ); |
271 | 495 | if ( $rc != 0 ) { |
272 | 496 | self::throwCallException( wfMessage( 'score-abcconversionerr' ), $output ); |
273 | 497 | } |
274 | | - if ( !file_exists( $lyFile ) ) { |
| 498 | + if ( !file_exists( $factoryLy ) ) { |
275 | 499 | /* Occasionally, abc2ly will return exit code 0 but not create an output file */ |
276 | 500 | self::throwCallException( wfMessage( 'score-abcconversionerr' ), $output ); |
277 | 501 | } |
278 | 502 | |
279 | 503 | /* The output file has a tagline which should be removed in a wiki context */ |
280 | | - $lyData = file_get_contents( $lyFile ); |
| 504 | + $lyData = file_get_contents( $factoryLy ); |
281 | 505 | if ( $lyData === false ) { |
282 | | - throw new ScoreException( wfMessage( 'score-readerr', $lyFile ) ); |
| 506 | + throw new ScoreException( wfMessage( 'score-readerr', $factoryLy ) ); |
283 | 507 | } |
284 | 508 | $lyData = preg_replace( '/^(\s*tagline\s*=).*/m', '$1 ##f', $lyData ); |
285 | 509 | if ( $lyData === null ) { |
286 | 510 | throw new ScoreException( wfMessage( 'score-pregreplaceerr' ) ); |
287 | 511 | } |
288 | | - $rc = file_put_contents( $lyFile, $lyData ); |
| 512 | + $rc = file_put_contents( $factoryLy, $lyData ); |
289 | 513 | if ( $rc === false ) { |
290 | | - throw new ScoreException( wfMessage( 'score-noinput', $lyFile ) ); |
| 514 | + throw new ScoreException( wfMessage( 'score-noinput', $factoryLy ) ); |
291 | 515 | } |
292 | | - } |
293 | 516 | |
294 | | - /** |
295 | | - * Runs lilypond. |
296 | | - * |
297 | | - * @param $factoryDirectory Directory of the working environment. |
298 | | - * The LilyPond input file "file.ly" is expected to be in |
299 | | - * this directory. |
300 | | - * @param $renderMidi |
301 | | - * @param $altText Alternate text for the score image. |
302 | | - * If set to false, the alt text will contain pagination instead. |
303 | | - * |
304 | | - * @return Image link HTML, and possibly anchor to MIDI file. |
305 | | - */ |
306 | | - private static function runLilypond( $factoryDirectory, $renderMidi, $altText = false ) { |
307 | | - global $wgUploadDirectory, $wgUploadPath, $wgLilyPond, $wgScoreTrim; |
308 | | - |
309 | | - wfProfileIn( __METHOD__ ); |
310 | | - |
311 | | - /* Various paths and filenames */ |
312 | | - $lilypondFile = $factoryDirectory . '/file.ly'; |
313 | | - $factoryMidi = $factoryDirectory . '/file.midi'; |
314 | | - $factoryImage = $factoryDirectory . '/file.png'; |
315 | | - $factoryImageTrimmed = $factoryDirectory . '/file-trimmed.png'; |
316 | | - $factoryMultiFormat = $factoryDirectory . '/file-%d.png'; // for multi-page scores |
317 | | - $factoryMultiTrimmedFormat = $factoryDirectory . '/file-%d-trimmed.png'; |
318 | | - $lilypondDir = 'lilypond'; |
319 | | - $md5 = md5_file( $lilypondFile ); |
320 | | - if ( $md5 === false ) { |
321 | | - throw new ScoreException( wfMessage( 'score-noinput', $lilypondFile ) ); |
322 | | - } |
323 | | - $rel = $lilypondDir . '/' . $md5; // FIXME: Too many files in one directory? |
324 | | - $filePrefix = "$wgUploadDirectory/$rel"; |
325 | | - $pathPrefix = "$wgUploadPath/$rel"; |
326 | | - $midi = "$filePrefix.midi"; |
327 | | - $midiPath = "$pathPrefix.midi"; |
328 | | - $image = "$filePrefix.png"; |
329 | | - $imagePath = "$pathPrefix.png"; |
330 | | - $multiFormat = "$filePrefix-%d.png"; |
331 | | - $multiPathFormat = "$pathPrefix-%d.png"; |
332 | | - $multi1 = "$filePrefix-1.png"; |
333 | | - |
334 | | - /* Check whether the file is already cached */ |
335 | | - $cached = true; |
336 | | - if ( $renderMidi && !file_exists( $midi ) ) { |
337 | | - self::debug( "Cache miss: File $midi does not exist and midi rendering is enabled.\n" ); |
338 | | - $cached = false; |
339 | | - } |
340 | | - if ( !file_exists( $image ) && !file_exists( $multi1 ) ) { |
341 | | - self::debug( "Cache miss: Neither $image nor $multi1 exists.\n" ); |
342 | | - $cached = false; |
343 | | - } |
344 | | - |
345 | | - /* If not cached, create the files */ |
346 | | - if ( !$cached ) { |
347 | | - self::debug( "Regenerating files due to cache miss.\n" ); |
348 | | - |
349 | | - try { |
350 | | - /* delete old files if necessary */ |
351 | | - $rc = true; |
352 | | - if ( file_exists( $midi ) ) { |
353 | | - $rc = $rc && unlink( $midi ); |
354 | | - } |
355 | | - if ( file_exists( $image ) ) { |
356 | | - $rc = $rc && unlink( $image ); |
357 | | - } |
358 | | - for ( $i = 1; file_exists( $f = sprintf( $multiFormat, $i ) ); ++$i ) { |
359 | | - $rc = $rc && unlink( $f ); |
360 | | - } |
361 | | - if ( !$rc ) { |
362 | | - throw new ScoreException( wfMessage( 'score-cleanerr' ) ); |
363 | | - } |
364 | | - |
365 | | - /* create output directory if necessary */ |
366 | | - if ( !file_exists( "$wgUploadDirectory/$lilypondDir" ) ) { |
367 | | - $rc = wfMkdirParents( "$wgUploadDirectory/$lilypondDir", null, __METHOD__ ); |
368 | | - if ( !$rc ) { |
369 | | - throw new ScoreException( wfMessage( 'score-nooutput', $lilypondDir ) ); |
370 | | - } |
371 | | - } |
372 | | - |
373 | | - /* generate lilypond output files in working environment */ |
374 | | - $oldcwd = getcwd(); |
375 | | - if ( $oldcwd === false ) { |
376 | | - throw new ScoreException( wfMessage( 'score-getcwderr' ) ); |
377 | | - } |
378 | | - $rc = chdir( $factoryDirectory ); |
379 | | - if ( !$rc ) { |
380 | | - throw new ScoreException( wfMessage( 'score-chdirerr', $factoryDirectory ) ); |
381 | | - } |
382 | | - if ( !is_executable( $wgLilyPond ) ) { |
383 | | - throw new ScoreException( wfMessage( 'score-notexecutable', $wgLilyPond ) ); |
384 | | - } |
385 | | - $cmd = wfEscapeShellArg( $wgLilyPond ) |
386 | | - . ' -dsafe=' |
387 | | - . wfEscapeShellArg( '#t' ) |
388 | | - . ' -dbackend=eps --png --header=texidoc ' |
389 | | - . wfEscapeShellArg( $lilypondFile ) |
390 | | - . ' 2>&1'; |
391 | | - $output = wfShellExec( $cmd, $rc2 ); |
392 | | - $rc = chdir( $oldcwd ); |
393 | | - if ( !$rc ) { |
394 | | - throw new ScoreException( wfMessage( 'score-chdirerr', $oldcwd ) ); |
395 | | - } |
396 | | - if ( $rc2 != 0 ) { |
397 | | - self::throwCallException( wfMessage( 'score-compilererr' ), $output ); |
398 | | - } |
399 | | - |
400 | | - /* trim output images if wanted */ |
401 | | - if ( $wgScoreTrim ) { |
402 | | - if ( file_exists( $factoryImage ) ) { |
403 | | - $rc = self::trimImage( $factoryImage, $factoryImageTrimmed ); |
404 | | - } |
405 | | - for ( $i = 1; file_exists( $f = sprintf( $factoryMultiFormat, $i ) ); ++$i ) { |
406 | | - $rc = self::trimImage( $f, sprintf( $factoryMultiTrimmedFormat, $i ) ); |
407 | | - } |
408 | | - } else { |
409 | | - $factoryImageTrimmed = $factoryImage; |
410 | | - $factoryMultiTrimmedFormat = $factoryMultiFormat; |
411 | | - } |
412 | | - |
413 | | - /* move files to proper places */ |
414 | | - $rc = true; |
415 | | - if ( file_exists( $factoryMidi ) ) { |
416 | | - $rc = $rc && rename( $factoryMidi, $midi ); |
417 | | - } |
418 | | - if ( file_exists( $factoryImageTrimmed ) ) { |
419 | | - $rc = $rc && rename( $factoryImageTrimmed, $image ); |
420 | | - } |
421 | | - for ( $i = 1; file_exists( $f = sprintf( $factoryMultiTrimmedFormat, $i ) ); ++$i ) { |
422 | | - $rc = $rc && rename( $f, sprintf( $multiFormat, $i ) ); |
423 | | - } |
424 | | - if ( !$rc ) { |
425 | | - throw new ScoreException( wfMessage( 'score-renameerr' ) ); |
426 | | - } |
427 | | - |
428 | | - } catch ( ScoreException $e ) { |
429 | | - wfProfileOut( __METHOD__ ); |
430 | | - throw $e; |
431 | | - } |
432 | | - } |
433 | | - |
434 | | - /* return output link(s) */ |
435 | | - if ( file_exists( $image ) ) { |
436 | | - if ( $altText ) { |
437 | | - $alt = $altText; |
438 | | - } else { |
439 | | - $alt = wfMessage( 'score-page' )->inContentLanguage()->numParams( '1' )->plain(); |
440 | | - } |
441 | | - $link = Html::rawElement( 'img', array( |
442 | | - 'src' => $imagePath, |
443 | | - 'alt' => $alt, |
444 | | - ) ); |
445 | | - } elseif ( file_exists( $multi1 ) ) { |
446 | | - $link = ''; |
447 | | - for ( $i = 1; file_exists( sprintf( $multiFormat, $i ) ); ++$i ) { |
448 | | - if ( $altText ) { |
449 | | - $alt = $altText; |
450 | | - } else { |
451 | | - $alt = wfMessage( 'score-page' )->inContentLanguage()->numParams( $i )->plain(); |
452 | | - } |
453 | | - $link .= Html::rawElement( 'img', array( |
454 | | - 'src' => sprintf( $multiPathFormat, $i ), |
455 | | - 'alt' => $alt, |
456 | | - ) ); |
457 | | - } |
458 | | - } else { |
459 | | - self::debug( "No output images $image or $multi1!\n" ); |
460 | | - $link = 'No image'; |
461 | | - } |
462 | | - if ( $renderMidi ) { |
463 | | - if ( !file_exists( $midi ) ) { |
464 | | - self::debug( "Midi file $midi should exist but does not!\n" ); |
465 | | - } else { |
466 | | - $link = Html::rawElement( 'a', array( 'href' => $midiPath ), $link ); |
467 | | - } |
468 | | - } |
469 | | - wfProfileOut( __METHOD__ ); |
470 | | - return $link; |
| 517 | + return $lyData; |
471 | 518 | } |
472 | 519 | |
473 | 520 | /** |
— | — | @@ -501,7 +548,12 @@ |
502 | 549 | private static function eraseFactory( $dir ) { |
503 | 550 | if( file_exists( $dir ) ) { |
504 | 551 | array_map( 'unlink', glob( "$dir/*", GLOB_NOSORT ) ); |
505 | | - return rmdir( $dir ); |
| 552 | + $rc = rmdir( $dir ); |
| 553 | + if ( !$rc ) { |
| 554 | + self::debug( "Unable to remove directory $dir\n." ); |
| 555 | + } |
| 556 | + return $rc; |
| 557 | + |
506 | 558 | } else { |
507 | 559 | /* Nothing to do */ |
508 | 560 | return true; |