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 |
1 | 23 | + 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 |
1 | 34 | + 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 | + " " . Xml::inputLabel( wfMsg( 'translatesvg-xcoordinate-pre' ), $language.'-'.$i.'-x', $language.'-'.$i.'-x', 5, $default['x'] ) . |
| 155 | + " " . Xml::inputLabel( wfMsg( 'translatesvg-ycoordinate-pre' ), $language.'-'.$i.'-y', $language.'-'.$i.'-y', 5, $default['y'] ); |
| 156 | + if( trim( $this->getQQQ( $i ) ) !== '' ){ |
| 157 | + $grouphtml .= " " . 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 |
1 | 312 | + 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 |
1 | 25 | + native |