r106440 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r106439‎ | r106440 | r106441 >
Date:15:34, 16 December 2011
Author:jarry1250
Status:deferred (Comments)
Tags:
Comment:
Add extensions/TranslateSvg
Modified paths:
  • /trunk/extensions/TranslateSvg (added) (history)
  • /trunk/extensions/TranslateSvg/SpecialTranslateSvg.php (added) (history)
  • /trunk/extensions/TranslateSvg/TranslateSvg.i18n.php (added) (history)
  • /trunk/extensions/TranslateSvg/TranslateSvg.php (added) (history)
  • /trunk/extensions/TranslateSvg/ext.translateSvg.core.js (added) (history)

Diff [purge]

Index: trunk/extensions/TranslateSvg/TranslateSvg.i18n.php
@@ -0,0 +1,21 @@
 2+<?php
 3+
 4+ $messages = array();
 5+ $messages['en'] = array(
 6+ 'translatesvg' => 'TranslateSVG',
 7+ 'translatesvg-desc' => 'A MediaWiki extension that creates a Special page to provide a native-style interface for translating SVGs in line with the SVG1.1 specification.',# Special:translatesvg
 8+ 'translatesvg-legend' => 'File path',
 9+ 'translatesvg-page' => 'File:',
 10+ 'translatesvg-submit' => 'Go',
 11+ 'translatesvg-summary' => 'This special page allows you to add, remove and modify translations embedded within a given SVG image file.',
 12+ 'translatesvg-add' => 'If your language is not listed already, you can ',
 13+ 'translatesvg-addlink' => 'add it.',
 14+ 'translatesvg-xcoordinate-pre' => 'X-coordinate (horizontal): ',
 15+ //'translatesvg-xcoordinate-post' => '',
 16+ 'translatesvg-ycoordinate-pre' => 'Y-coordinate (vertical): ',
 17+ //'translatesvg-ycoordinate-post' => ''
 18+ );
 19+ $specialPageAliases['en'] = array(
 20+ 'TranslateSvg' => array( 'TranslateSVG', 'TranslateSvg' ),
 21+ );
 22+ $aliases =& $specialPageAliases; // for backwards compatibility with MediaWiki 1.15 and earlier.
\ No newline at end of file
Property changes on: trunk/extensions/TranslateSvg/TranslateSvg.i18n.php
___________________________________________________________________
Added: svn:eol-style
123 + native
Index: trunk/extensions/TranslateSvg/TranslateSvg.php
@@ -0,0 +1,32 @@
 2+<?php
 3+/*
 4+ TranslateSvg extension (c) 2011 Harry Burt ( http://harryburt.co.uk )
 5+
 6+ Some portions of it forked from MediaWiki core in December 2011; please
 7+ consult http://svn.wikimedia.org/svnroot/mediawiki/trunk/phase3/CREDITS
 8+ for a complete list of authors.
 9+
 10+ Licensed freely under GNU General Public License Version 2, June 1991
 11+ For terms of use, see http://www.opensource.org/licenses/gpl-2.0.php.
 12+*/
 13+
 14+$wgExtensionCredits['specialpage'][] = array(
 15+ 'name' => 'TranslateSVG',
 16+ 'author' => 'Harry Burt',
 17+ 'url' => 'http://www.mediawiki.org/wiki/Extension:TranslateSvg',
 18+ 'descriptionmsg' => 'translatesvg-desc',
 19+ 'version' => '1.0.0',
 20+);
 21+
 22+$wgAutoloadClasses['SpecialTranslateSvg'] = dirname(__FILE__) . '/' . 'SpecialTranslateSvg.php';
 23+$wgExtensionMessagesFiles['TranslateSvg'] = dirname( __FILE__ ) . '/TranslateSvg.i18n.php';
 24+$wgSpecialPages['TranslateSvg'] = 'SpecialTranslateSvg'; # Tell MediaWiki about the new special page and its class name
 25+$wgSpecialPageGroups['TranslateSvg'] = 'media';
 26+
 27+$wgResourceModules['ext.translateSvg'] = array(
 28+ 'scripts' => array( 'ext.translateSvg.core.js' ),
 29+ // 'styles' => 'css/ext.translateSvg.css',
 30+ 'messages' => array( 'translatesvg-add', 'translatesvg-addlink' ),
 31+ 'localBasePath' => dirname( __FILE__ ),
 32+ 'remoteExtPath' => 'translateSvg'
 33+);
\ No newline at end of file
Property changes on: trunk/extensions/TranslateSvg/TranslateSvg.php
___________________________________________________________________
Added: svn:eol-style
134 + native
Index: trunk/extensions/TranslateSvg/SpecialTranslateSvg.php
@@ -0,0 +1,310 @@
 2+<?php
 3+class SpecialTranslateSvg extends SpecialPage {
 4+ private $svg = '';
 5+ private $translations = array();
 6+ private $number = 0;
 7+ private $num = 0; //iterator
 8+ private $added = array();
 9+ private $modified = array();
 10+
 11+ function __construct() {
 12+ parent::__construct( 'TranslateSvg' );
 13+ }
 14+
 15+ function execute( $par ) {
 16+ $this->setHeaders();
 17+ $this->outputHeader();
 18+
 19+ $request = $this->getRequest();
 20+ $file = !is_null( $par ) ? $par : $request->getText( 'file' );
 21+
 22+ $title = Title::newFromText( $file, NS_FILE );
 23+
 24+ if ( ! $title instanceof Title || $title->getNamespace() != NS_FILE ) {
 25+ $this->showForm( $title );
 26+ } else {
 27+ $file = wfFindFile( $title );
 28+
 29+ if ( $file && $file->exists() ){
 30+ $this->svg = new SimpleXMLElement( $file->getPath(), 0, true );
 31+ $this->svg->registerXPathNamespace( 'svg', 'http://www.w3.org/2000/svg' );
 32+ $this->makeTranslationReady();
 33+ $this->extractTranslations();
 34+ $this->tidyTranslations();
 35+ $params = $request->getQueryValues();
 36+ if( count( $params ) > 2 && isset( $params['title'] ) && isset( $params['file'] ) ){
 37+ unset( $params['title'] );
 38+ $filename = $params['file'];
 39+ unset( $params['file'] );
 40+ $this->updateTranslations( $params );
 41+ $this->updateSVG();
 42+ $this->saveSVG( $file->getPath(), $filename );
 43+ $file->purgeThumbnails();
 44+ } else {
 45+ $this->printTranslations( $file->getName() );
 46+ }
 47+ } else {
 48+ $this->getOutput()->setStatusCode( 404 );
 49+ $this->showForm( $title );
 50+ }
 51+ }
 52+ }
 53+
 54+ /**
 55+ * @param $title Title
 56+ */
 57+ function showForm( $title ) {
 58+ global $wgScript;
 59+
 60+ $this->getOutput()->addHTML(
 61+ Html::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'specialtranslatesvg' ) ) .
 62+ Html::openElement( 'fieldset' ) .
 63+ Html::element( 'legend', null, wfMsg( 'translatesvg-legend' ) ) .
 64+ Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) .
 65+ Xml::inputLabel( wfMsg( 'translatesvg-page' ), 'file', 'file', 25, is_object( $title ) ? $title->getText() : '' ) . ' ' .
 66+ Xml::submitButton( wfMsg( 'translatesvg-submit' ) ) . "\n" .
 67+ Html::closeElement( 'fieldset' ) .
 68+ Html::closeElement( 'form' )
 69+ );
 70+ }
 71+ function makeTranslationReady() {
 72+ $result = $this->svg->xpath("//svg:text");
 73+ foreach( $result as &$text ){
 74+ $text->registerXPathNamespace( 'svg', 'http://www.w3.org/2000/svg' );
 75+ $ancestorswitches = $text->xpath("ancestor::svg:switch");
 76+ if( count($ancestorswitches) == 0 ){
 77+ $parent = $text->xpath("parent::*");
 78+ $parent = $parent[0];
 79+ $switch = $parent->addChild('switch');
 80+ $newtext = $switch->addChild( 'text', $text[0] );
 81+ foreach( $text->attributes() as $attrname=>$attrvalue ){
 82+ $newtext->addAttribute( $attrname, $attrvalue );
 83+ }
 84+ $dom=dom_import_simplexml($text);
 85+ $dom->parentNode->removeChild($dom);
 86+ unset( $dom );
 87+ }
 88+ }
 89+ }
 90+ function extractTranslations(){
 91+ $result = $this->svg->xpath("//svg:switch");
 92+ $this->number = count( $result );
 93+ for( $i = 0; $i < $this->number; $i++ ){
 94+ $switch = $result[$i];
 95+ $switch->registerXPathNamespace( 'svg', 'http://www.w3.org/2000/svg' );
 96+ $existing = $switch->xpath("svg:text");
 97+ foreach( $existing as $child ){
 98+ $child = (array)$child;
 99+ if( isset( $child[0] ) ){
 100+ $text = $child[0];
 101+ } else {
 102+ $text = '';
 103+ }
 104+ if( isset( $child['@attributes'] ) ){
 105+ $attr = $child['@attributes'];
 106+ } else {
 107+ $attr = array();
 108+ }
 109+ $this->translations [ $i ][] = array_merge( $attr, array( 'text'=>$text) );
 110+ }
 111+ }
 112+ }
 113+ function tidyTranslations(){
 114+ $new = array();
 115+ foreach( $this->translations as $number=>$translations ){
 116+ foreach( $translations as $translation ){
 117+ if( isset( $translation['systemLanguage'] ) ){
 118+ $language = $translation['systemLanguage'];
 119+ unset( $translation['systemLanguage'] );
 120+ } else {
 121+ $language='fallback';
 122+ }
 123+ $new[ $language ][ $number ] = $translation;
 124+ }
 125+ }
 126+ if( !isset( $new['qqq'] ) ){
 127+ $new['qqq'] = array();
 128+ }
 129+ $this->translations = $new;
 130+ }
 131+
 132+ function printTranslations( $filename ){
 133+ global $wgScript;
 134+ $this->getOutput()->addModules( 'ext.translateSvg' );
 135+ $html = Html::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'specialtranslatesvg' ) ) .
 136+ Html::hidden( 'file', $filename ) .
 137+ Html::hidden( 'title', $this->getTitle()->getPrefixedText() );
 138+
 139+ $groups = array();
 140+ foreach( $this->translations as $language=>$translations ){
 141+ $languages = Language::getLanguageNames();
 142+ $languages['fallback'] = "Default (no language specified)"; //TODO: localise
 143+ $languages['qqq'] = "Advice to translators"; //TODO: localise
 144+
 145+ $html .= Html::openElement( 'fieldset', array( 'id' => $language ) ) .
 146+ Html::element( 'legend', null, $languages[$language] );
 147+ $groups = array();
 148+ for( $i = 0; $i < $this->number; $i++ ){
 149+ $fallback = $this->getfallback( $i );
 150+ $default = $this->getDefault( $i, $language );
 151+ $grouphtml = Xml::inputLabel( $fallback['text'], $language.'-'.$i.'-text', $language.'-'.$i.'-text', 50, $default['text'] );
 152+ if( $language !== 'qqq' ){
 153+ $grouphtml .= Html::element( 'br' ) .
 154+ "&nbsp;&nbsp;&nbsp;" . Xml::inputLabel( wfMsg( 'translatesvg-xcoordinate-pre' ), $language.'-'.$i.'-x', $language.'-'.$i.'-x', 5, $default['x'] ) .
 155+ "&nbsp;&nbsp;&nbsp;" . Xml::inputLabel( wfMsg( 'translatesvg-ycoordinate-pre' ), $language.'-'.$i.'-y', $language.'-'.$i.'-y', 5, $default['y'] );
 156+ if( trim( $this->getQQQ( $i ) ) !== '' ){
 157+ $grouphtml .= "&nbsp" . Html::element( 'small', null, $this->getQQQ( $i ) );
 158+ }
 159+ }
 160+ $groups[] = $grouphtml;
 161+ }
 162+ $html .= implode( Html::element( 'div', array( 'style' => 'height:10px' ) ), $groups );
 163+ $html .= Html::closeElement( 'fieldset' );
 164+ }
 165+ $html .= Xml::submitButton( 'Submit' ) . "\n" .
 166+ Html::closeElement( 'form' );
 167+ $this->getOutput()->addHTML( $html );
 168+ }
 169+
 170+ function getfallback( $num ){
 171+ if( isset( $this->translations['fallback'][$num] ) ){
 172+ return $this->translations['fallback'][$num];
 173+ } else {
 174+ //TODO
 175+ }
 176+ }
 177+ function getDefault( $num, $language ){
 178+ if( isset( $this->translations[$language][$num] ) ){
 179+ return $this->translations[$language][$num];
 180+ } else {
 181+ //TODO
 182+ }
 183+ }
 184+ function getQQQ( $num ){
 185+ if( isset( $this->translations['qqq'][$num]['text'] ) ){
 186+ return $this->translations['qqq'][$num]['text'];
 187+ } else {
 188+ return '(no description)';
 189+ }
 190+ }
 191+ function updateTranslations( $params ){
 192+ foreach( $params as $name=>$value ){
 193+ list( $lang, $num, $param ) = explode( '-', $name );
 194+ if( !isset( $this->translations[ $lang ][ $num ] ) ){
 195+ $this->translations[ $lang ][ $num ] = $this->getfallback( $num );
 196+ }
 197+ $this->translations[ $lang ][ $num ][$param] = $value;
 198+ }
 199+
 200+ $reverse = array();
 201+ foreach( $this->translations as $language=>$translations ){
 202+ if( $language == 'fallback' ) continue; //We'll come back for it.
 203+ for( $i = 0; $i < $this->number; $i++ ){
 204+ $reverse[ $i ][] = array_merge( $translations[$i], array( 'systemLanguage' => $language ) );
 205+ }
 206+ }
 207+ for( $i = 0; $i < $this->number; $i++ ){
 208+ $reverse[ $i ][] = array_merge( $this->translations['fallback'][$i], array( 'systemLanguage' => 'fallback' ) );
 209+ }
 210+ $this->translations = $reverse;
 211+ }
 212+ function updateSVG(){
 213+ $result = $this->svg->xpath("//svg:switch");
 214+ for( $i = 0; $i < $this->number; $i++ ){
 215+ $switch = $result[$i];
 216+ $switch->registerXPathNamespace( 'svg', 'http://www.w3.org/2000/svg' );
 217+ foreach( $this->translations[$i] as $translation ){
 218+ $language = $translation['systemLanguage'];
 219+ if( $language === 'fallback' ){
 220+ $path = "svg:text[not(@systemLanguage)]";
 221+ } else {
 222+ $path = "svg:text[@systemLanguage='$language']";
 223+ }
 224+ $existing = $switch->xpath( $path );
 225+ if( count( $existing ) == 1 ){
 226+ // Update of existing translation
 227+ $old = array( (string)$existing[0][0], (string)$existing[0]['x'], (string)$existing[0]['y']);
 228+ $new = array( $translation['text'], $translation['x'], $translation['y']);
 229+ if( $old !== $new ){
 230+ $this->modified[$language] = '';
 231+ $existing[0]['x'] = $translation['x'];
 232+ $existing[0]['y'] = $translation['y'];
 233+ $existing[0][0] = $translation['text'];
 234+ }
 235+ } else {
 236+ $this->added[$language] = 'added';
 237+ $newtext = $switch->addChild('text', $translation['text']);
 238+ unset( $translation['text'] );
 239+ if( $translation['systemLanguage'] == 'fallback' ){
 240+ unset( $translation['systemLanguage'] );
 241+ }
 242+ foreach( $translation as $attrkey=>$attrvalue ){
 243+ $newtext->addAttribute( $attrkey, $attrvalue );
 244+ }
 245+ }
 246+ }
 247+ // Always move fallback to end
 248+ $fallback = $switch->xpath("svg:text[not(@systemLanguage)]");
 249+ foreach( $fallback as $canon ){
 250+ $dom=dom_import_simplexml($canon);
 251+ $dom->parentNode->appendChild($dom);
 252+ $dom->parentNode->removeChild($dom);
 253+ unset( $dom );
 254+ }
 255+ }
 256+ }
 257+ function saveSVG( $filepath, $filename ){
 258+
 259+ $mUpload = new TranslateSvgUpload();
 260+ $temp = tempnam( wfTempDir(), 'trans' );
 261+ $dom = new DOMDocument('1.0');
 262+ $dom->preserveWhiteSpace = false;
 263+ $dom->formatOutput = true;
 264+ $dom->loadXML($this->svg->asXML());
 265+ $dom->save( $temp );
 266+ unset( $dom );
 267+
 268+ $mUpload->initializePathInfo( $filename, $temp, filesize( $filepath ), true );
 269+
 270+ $details = $mUpload->verifyUpload();
 271+ if ( $details['status'] != UploadBase::OK ) {
 272+ //TODO
 273+ //$this->processVerificationError( $details );
 274+ return;
 275+ }
 276+
 277+ // Verify permissions for this title
 278+ $permErrors = $mUpload->verifyTitlePermissions( $this->getUser() );
 279+ if( $permErrors !== true ) {
 280+ $code = array_shift( $permErrors[0] );
 281+ //TODO
 282+ //$this->showRecoverableUploadError( wfMsgExt( $code,
 283+ // 'parseinline', $permErrors[0] ) );
 284+ return;
 285+ }
 286+
 287+ $this->mLocalFile = $mUpload->getLocalFile();
 288+ $watch = true; //TODO
 289+ unset( $this->added['fallback'] );
 290+ $comment = "Updating translations:";
 291+ if( count( $this->added ) > 0 ) $comment .= " added " . implode( ", ", array_keys( $this->added ) ) . ";";
 292+ if( count( $this->modified ) > 0 ) $comment .= " modified " . implode( ", ", array_keys( $this->modified ) ) . ";";
 293+ $comment = trim( $comment, ";" ) . ".";
 294+ die( $comment );
 295+ $status = $mUpload->performUpload( $comment, false, $watch, $this->getUser() );
 296+ if ( !$status->isGood() ) {
 297+ //TODO
 298+ //$this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) );
 299+ return;
 300+ }
 301+
 302+ // Success, redirect to description page
 303+ $this->getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() );
 304+ }
 305+}
 306+
 307+class TranslateSvgUpload extends UploadBase {
 308+ public function initializeFromRequest( &$request ) {
 309+
 310+ }
 311+}
Property changes on: trunk/extensions/TranslateSvg/SpecialTranslateSvg.php
___________________________________________________________________
Added: svn:eol-style
1312 + native
Index: trunk/extensions/TranslateSvg/ext.translateSvg.core.js
@@ -0,0 +1,23 @@
 2+var link = $('<a id="newtrans" href="#"></a>')
 3+ .text( mw.msg('translatesvg-addlink') )
 4+ .click(function() {
 5+ var langcode = prompt("Specify new language code (e.g. en, fr, de, es...)");
 6+ if( langcode !== null ){
 7+ var newfieldset = $('fieldset#fallback').clone();
 8+ newfieldset.find('input').each( function (){
 9+ $(this).attr( 'id', $(this).attr( 'id' ).toString().replace( 'fallback', langcode ) );
 10+ $(this).attr( 'name', $(this).attr( 'name' ).toString().replace( 'fallback', langcode ) );
 11+ });
 12+ newfieldset.find('label').each( function (){
 13+ $(this).attr( 'for', $(this).attr( 'for' ).toString().replace( 'fallback', langcode ) );
 14+ });
 15+ newfieldset.attr( 'id', langcode );
 16+ newfieldset = $('a#newtrans').after( newfieldset );
 17+ $('fieldset#' + langcode + ' legend').remove();
 18+ $('fieldset#' + langcode ).prepend( '<legend>' + langcode + '</legend>' );
 19+ }
 20+ return false;
 21+}); //TODO: localise, validate input, en=>English
 22+var paragraph = $('<p></p>').text( mw.msg('translatesvg-add'))
 23+ .append( link );
 24+$('form#specialtranslatesvg').prepend(paragraph);
\ No newline at end of file
Property changes on: trunk/extensions/TranslateSvg/ext.translateSvg.core.js
___________________________________________________________________
Added: svn:eol-style
125 + native

Comments

#Comment by Reedy (talk | contribs)   15:44, 16 December 2011

1.15 back compat!?

Also, I think aliases usually go in a separate file

#Comment by Nikerabbit (talk | contribs)   16:20, 16 December 2011

Yeah they need to be in a different file if want to have support for translatewiki.net

#Comment by Jarry1250 (talk | contribs)   16:23, 16 December 2011

Oakily doakily. But you might want to update the documentation page.

#Comment by Reedy (talk | contribs)   16:44, 16 December 2011

It's a wiki, fix it yourself? :p

#Comment by Jarry1250 (talk | contribs)   16:47, 16 December 2011

Well I knew what the correct version was, I wouldn't have copied an incorrect documentation page, would I? :D Is r106445 (well, the relevant part of it) the correct way?

#Comment by Raymond (talk | contribs)   19:29, 18 December 2011

Please add message documentation for the newly added messages. Thanks.

#Comment by Jarry1250 (talk | contribs)   19:34, 18 December 2011

Yeah. I was waiting for TranslateWiki.net to pick it up before doing that (because I like the interface), but should I not do so?

#Comment by Johnduhart (talk | contribs)   19:35, 18 December 2011

It's needed before going to TranslateWiki.

#Comment by Jarry1250 (talk | contribs)   19:36, 18 December 2011

Ah, I see, thanks. Will commit over the next couple of hours.

#Comment by Jarry1250 (talk | contribs)   16:27, 26 December 2011

Did indeed do that at some point.

Status & tagging log