Index: trunk/extensions/Translate/scripts/pagetranslation-test-parser.php |
— | — | @@ -37,11 +37,20 @@ |
38 | 38 | |
39 | 39 | $pattern = realpath( "$testDirectory" ) . "/$pagename"; |
40 | 40 | |
| 41 | + $failureExpected = strpos( $pagename, 'Fail' ) === 0; |
| 42 | + |
41 | 43 | try { |
42 | 44 | $parse = $translatablePage->getParse(); |
| 45 | + if ( $failureExpected ) { |
| 46 | + $target = $parse->getTranslationPageText( new TestMessageCollection( "foo" ) ); |
| 47 | + $this->output( "Testfile $filename should have failed... see $pattern.pttarget.fail" ); |
| 48 | + file_put_contents( "$pattern.pttarget.fail", $target ); |
| 49 | + } |
43 | 50 | } catch ( TPException $e ) { |
44 | | - $this->output( "Testfile $filename failed to parse... see $pattern.ptfile.fail" ); |
45 | | - file_put_contents( "$pattern.ptfile.fail", $e->getMessage() ); |
| 51 | + if ( !$failureExpected ) { |
| 52 | + $this->output( "Testfile $filename failed to parse... see $pattern.ptfile.fail" ); |
| 53 | + file_put_contents( "$pattern.ptfile.fail", $e->getMessage() ); |
| 54 | + } |
46 | 55 | continue; |
47 | 56 | } |
48 | 57 | |
Index: trunk/extensions/Translate/tests/pagetranslation/FailNotAtomic.pttarget |
— | — | @@ -1 +0,0 @@ |
2 | | -This file should not parse |
\ No newline at end of file |
Index: trunk/extensions/Translate/tests/pagetranslation/FailMultipleSectionMarkers.pttarget |
— | — | @@ -1 +0,0 @@ |
2 | | -This parse should fail |
Index: trunk/extensions/Translate/tests/pagetranslation/SimpleWithMarker.pttarget |
— | — | @@ -0,0 +1 @@ |
| 2 | +A cat sleeps. |
Index: trunk/extensions/Translate/tests/pagetranslation/FailSectionMarkerPlace.ptfile |
— | — | @@ -0,0 +1,3 @@ |
| 2 | +<translate> |
| 3 | +Once upon time <!--T:1--> there was a planet called meow. |
| 4 | +</translate> |
\ No newline at end of file |
Index: trunk/extensions/Translate/tests/pagetranslation/FailEmptySection.ptfile |
— | — | @@ -0,0 +1,4 @@ |
| 2 | +<translate> |
| 3 | +<!--T:1--> |
| 4 | + |
| 5 | +</translate> |
\ No newline at end of file |
Index: trunk/extensions/Translate/tests/pagetranslation/SimpleWithMarker.ptfile |
— | — | @@ -0,0 +1,4 @@ |
| 2 | +<translate> |
| 3 | +<!--T:1--> |
| 4 | +A cat sleeps. |
| 5 | +</translate> |
Index: trunk/extensions/Translate/tag/TranslatablePage.php |
— | — | @@ -2,6 +2,10 @@ |
3 | 3 | |
4 | 4 | /** |
5 | 5 | * Class to parse translatable wiki pages. |
| 6 | + * |
| 7 | + * @author Niklas Laxström |
| 8 | + * @copyright Copyright © 2009-2010 Niklas Laxström |
| 9 | + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later |
6 | 10 | */ |
7 | 11 | class TranslatablePage { |
8 | 12 | /** |
— | — | @@ -142,7 +146,6 @@ |
143 | 147 | $re = '~(<translate>)\s*(.*?)(</translate>)~s'; |
144 | 148 | $matches = array(); |
145 | 149 | $ok = preg_match_all( $re, $text, $matches, PREG_OFFSET_CAPTURE ); |
146 | | - if ( $ok === false ) return array( 'pt-error', 'tag', $text ); |
147 | 150 | if ( $ok === 0 ) break; // No matches |
148 | 151 | |
149 | 152 | // Do-placehold for the whole stuff |
— | — | @@ -159,22 +162,28 @@ |
160 | 163 | $len = strlen( $matches[2][0][0] ); // len of the content |
161 | 164 | $end = $start + $len; |
162 | 165 | |
163 | | - $ret = $this->sectionise( $sections, substr( $contents, $start, $len ) ); |
| 166 | + $sectiontext = substr( $contents, $start, $len ); |
164 | 167 | |
| 168 | + if ( strpos( $sectiontext, '<translate>' ) !== false ) { |
| 169 | + throw new TPException( array( 'pt-parse-nested', $sectiontext ) ); |
| 170 | + } |
| 171 | + |
| 172 | + $ret = $this->sectionise( $sections, $sectiontext ); |
| 173 | + |
165 | 174 | $tagPlaceHolders[$ph] = |
166 | 175 | self::index_replace( $contents, $ret, $start, $end ); |
167 | 176 | } |
168 | 177 | |
| 178 | + $prettyTemplate = $text; |
| 179 | + foreach ( $tagPlaceHolders as $ph => $value ) { |
| 180 | + $prettyTemplate = str_replace( $ph, '[...]', $prettyTemplate ); |
| 181 | + } |
169 | 182 | if ( strpos( $text, '<translate>' ) !== false ) { |
170 | | - $text = str_replace( "\n", '\n', $text ); |
171 | | - throw new TPException( array( 'pt-parse-open', $text ) ); |
| 183 | + throw new TPException( array( 'pt-parse-open', $prettyTemplate ) ); |
| 184 | + } elseif ( strpos( $text, '</translate>' ) !== false ) { |
| 185 | + throw new TPException( array( 'pt-parse-close', $prettyTemplate ) ); |
172 | 186 | } |
173 | 187 | |
174 | | - if ( strpos( $text, '</translate>' ) !== false ) { |
175 | | - $text = str_replace( "\n", '\n', $text ); |
176 | | - throw new TPException( array( 'pt-parse-close', $text ) ); |
177 | | - } |
178 | | - |
179 | 188 | foreach ( $tagPlaceHolders as $ph => $value ) { |
180 | 189 | $text = str_replace( $ph, $value, $text ); |
181 | 190 | } |
— | — | @@ -242,7 +251,9 @@ |
243 | 252 | $re = '~<!--T:(.*?)-->~'; |
244 | 253 | $matches = array(); |
245 | 254 | $count = preg_match_all( $re, $content, $matches, PREG_SET_ORDER ); |
246 | | - if ( $count > 1 ) throw new TPException( array( 'pt-shake-multiple', $content ) ); |
| 255 | + if ( $count > 1 ) { |
| 256 | + throw new TPException( array( 'pt-shake-multiple', $content ) ); |
| 257 | + } |
247 | 258 | |
248 | 259 | $section = new TPSection; |
249 | 260 | if ( $count === 1 ) { |
— | — | @@ -250,10 +261,17 @@ |
251 | 262 | list( /*full*/, $id ) = $match; |
252 | 263 | $section->id = $id; |
253 | 264 | |
254 | | - $rer1 = '~^\s*<!--T:(.*?)-->\s*~'; // Normal sections |
255 | | - $rer2 = '~\s*<!--T:(.*?)-->~'; // Sections with title |
| 265 | + // Currently handle only these two standard places. |
| 266 | + // Is this too strict? |
| 267 | + $rer1 = '~^<!--T:(.*?)-->\n~'; // Normal sections |
| 268 | + $rer2 = '~\s*<!--T:(.*?)-->\n~'; // Sections with title |
256 | 269 | $content = preg_replace( $rer1, '', $content ); |
257 | 270 | $content = preg_replace( $rer2, '', $content ); |
| 271 | + if ( preg_match( $re, $content ) === 1 ) { |
| 272 | + throw new TPException( array( 'pt-shake-position', $content ) ); |
| 273 | + } elseif ( trim( $content ) === '' ) { |
| 274 | + throw new TPException( array( 'pt-shake-empty', $id ) ); |
| 275 | + } |
258 | 276 | } |
259 | 277 | } else { |
260 | 278 | // New section |
— | — | @@ -453,6 +471,11 @@ |
454 | 472 | throw new MWException( "Invalid tag $tag requested" ); |
455 | 473 | } |
456 | 474 | |
| 475 | + global $wgTranslateStaticTags; |
| 476 | + if ( is_array($wgTranslateStaticTags) ) { |
| 477 | + return $wgTranslateStaticTags[$tag]; |
| 478 | + } |
| 479 | + |
457 | 480 | // Simple static cache |
458 | 481 | static $tagcache = array(); |
459 | 482 | |
— | — | @@ -482,14 +505,23 @@ |
483 | 506 | return $page; |
484 | 507 | } |
485 | 508 | |
486 | | - public static function changeTitleText( Title $title, $text ) { |
| 509 | + protected static function changeTitleText( Title $title, $text ) { |
487 | 510 | return Title::makeTitleSafe( $title->getNamespace(), $text ); |
488 | 511 | } |
489 | 512 | } |
490 | 513 | |
| 514 | +/** |
| 515 | + * Class to signal translatable page parser exceptions. |
| 516 | + */ |
491 | 517 | class TPException extends MWException { |
| 518 | + protected $msg = null; |
492 | 519 | public function __construct( $msg ) { |
| 520 | + $this->msg = $msg; |
493 | 521 | parent::__construct( call_user_func_array( 'wfMsg', $msg ) ); |
494 | 522 | } |
| 523 | + |
| 524 | + public function getMsg() { |
| 525 | + return $this->msg; |
| 526 | + } |
495 | 527 | } |
496 | 528 | |
Index: trunk/extensions/Translate/tag/TPParse.php |
— | — | @@ -4,7 +4,7 @@ |
5 | 5 | * extracted sections and a template. |
6 | 6 | * |
7 | 7 | * @author Niklas Laxström |
8 | | - * @copyright Copyright © 2009 Niklas Laxström |
| 8 | + * @copyright Copyright © 2009-2010 Niklas Laxström |
9 | 9 | * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later |
10 | 10 | */ |
11 | 11 | class TPParse { |
Index: trunk/extensions/Translate/tag/PageTranslationHooks.php |
— | — | @@ -237,21 +237,32 @@ |
238 | 238 | FOO; |
239 | 239 | } |
240 | 240 | |
241 | | - // When attempting to save |
| 241 | + // To display nice error for editpage |
| 242 | + public static function tpSyntaxCheckForEditPage( $editpage, $text, $section, &$error, $summary ) { |
| 243 | + if ( strpos( $text, '<translate>' ) === false ) return true; |
| 244 | + $page = TranslatablePage::newFromText( $editpage->mTitle, $text ); |
| 245 | + try { |
| 246 | + $page->getParse(); |
| 247 | + } catch ( TPException $e ) { |
| 248 | + $error .= Html::rawElement( 'div', array( 'class' => 'error' ), $e->getMessage() ); |
| 249 | + } |
| 250 | + return true; |
| 251 | + } |
| 252 | + |
| 253 | + /** |
| 254 | + * When attempting to save, last resort. Edit page would only display |
| 255 | + * edit conflict if there wasn't tpSyntaxCheckForEditPage |
| 256 | + */ |
242 | 257 | public static function tpSyntaxCheck( $article, $user, $text, $summary, |
243 | 258 | $minor, $_, $_, $flags, $status ) { |
244 | 259 | // Quick escape on normal pages |
245 | | - if ( strpos( $text, '</translate>' ) === false ) return true; |
| 260 | + if ( strpos( $text, '<translate>' ) === false ) return true; |
246 | 261 | |
247 | 262 | $page = TranslatablePage::newFromText( $article->getTitle(), $text ); |
248 | 263 | try { |
249 | | - /* This does not catch all problems yet, |
250 | | - * like markup spanning between sections. */ |
251 | 264 | $page->getParse(); |
252 | 265 | } catch ( TPException $e ) { |
253 | | - // FIXME: throws "PHP Notice: Undefined variable: ret" when <translate>/</translate> is uneven |
254 | | - // and an 'edit conflict'. |
255 | | - call_user_func_array( array( $status, 'fatal' ), $ret ); |
| 266 | + call_user_func_array( array( $status, 'fatal' ), $e->getMsg() ); |
256 | 267 | return false; |
257 | 268 | } |
258 | 269 | |
— | — | @@ -281,7 +292,6 @@ |
282 | 293 | if ( $group === null ) { |
283 | 294 | // No group means that the page is currently not |
284 | 295 | // registered to any page translation message groups |
285 | | - wfLoadExtensionMessages( 'PageTranslation' ); |
286 | 296 | $result = array( 'tpt-unknown-page' ); |
287 | 297 | return false; |
288 | 298 | } |
— | — | @@ -297,8 +307,6 @@ |
298 | 308 | |
299 | 309 | if ( $page->getMarkedTag() ) { |
300 | 310 | list( , $code ) = TranslateUtils::figureMessage( $title->getText() ); |
301 | | - wfLoadExtensionMessages( 'PageTranslation' ); |
302 | | - // FIXME: Core chokes on this, passing an array as first parameter to wfMsgNoTrans |
303 | 311 | $result = array( |
304 | 312 | 'tpt-target-page', |
305 | 313 | $page->getTitle()->getPrefixedText(), |
Index: trunk/extensions/Translate/Translate.php |
— | — | @@ -77,6 +77,7 @@ |
78 | 78 | |
79 | 79 | $wgEnablePageTranslation = false; |
80 | 80 | $wgPageTranslationNamespace = 1198; |
| 81 | +$wgTranslateStaticTags = false; |
81 | 82 | |
82 | 83 | $wgJobClasses['RenderJob'] = 'RenderJob'; |
83 | 84 | $wgAvailableRights[] = 'translate'; |
— | — | @@ -277,7 +278,9 @@ |
278 | 279 | |
279 | 280 | // Check syntax for <translate> |
280 | 281 | $wgHooks['ArticleSave'][] = 'PageTranslationHooks::tpSyntaxCheck'; |
| 282 | + $wgHooks['EditFilter'][] = 'PageTranslationHooks::tpSyntaxCheckForEditPage'; |
281 | 283 | |
| 284 | + |
282 | 285 | // Add transtag to page props for discovery |
283 | 286 | $wgHooks['ArticleSaveComplete'][] = 'PageTranslationHooks::addTranstag'; |
284 | 287 | |
Index: trunk/extensions/Translate/PageTranslation.i18n.php |
— | — | @@ -74,11 +74,19 @@ |
75 | 75 | |
76 | 76 | 'tpt-download-page' => 'Export page with translations', |
77 | 77 | |
| 78 | + 'pt-parse-open' => 'Unbalanced <translate> tag. |
| 79 | +Translation template: <pre>$1</pre>', |
| 80 | + 'pt-parse-close' => 'Unbalanced </translate> tag. |
| 81 | +Translation template: <pre>$1</pre>', |
| 82 | + 'pt-parse-nested' => 'Nested <translate> sections are not allowed. |
| 83 | +Tag text: <pre>$1</pre>', |
78 | 84 | 'pt-shake-multiple' => 'Multiple section markers for one section. |
79 | | -Section text: |
80 | | -$1 |
81 | | -', |
| 85 | +Section text: <pre>$1</pre>', |
| 86 | + 'pt-shake-position' => 'Section markers in unexpected position. |
| 87 | +Section text: <pre>$1</pre>', |
| 88 | + 'pt-shake-empty' => 'Empty section for marker $1.', |
82 | 89 | |
| 90 | + |
83 | 91 | ); |
84 | 92 | |
85 | 93 | /** Message documentation (Message documentation) |