00001 <?php
00017 class MessageWebImporter {
00021 protected $title;
00022
00026 protected $user;
00027
00031 protected $group;
00032 protected $code;
00033 protected $time;
00034
00038 protected $out;
00039
00043 protected $processingTime = 43;
00044
00045 public function __construct( Title $title = null, $group = null, $code = 'en' ) {
00046 $this->setTitle( $title );
00047 $this->setGroup( $group );
00048 $this->setCode( $code );
00049 }
00050
00056 public function getTitle() {
00057 return $this->title;
00058 }
00059
00060 public function setTitle( Title $title ) {
00061 $this->title = $title;
00062 }
00063
00067 public function getUser() {
00068 return $this->user ? $this->user : RequestContext::getMain()->getUser();
00069 }
00070
00071 public function setUser( User $user ) {
00072 $this->user = $user;
00073 }
00074
00078 public function getGroup() {
00079 return $this->group;
00080 }
00081
00086 public function setGroup( $group ) {
00087 if ( $group instanceof MessageGroup ) {
00088 $this->group = $group;
00089 } else {
00090 $this->group = MessageGroups::getGroup( $group );
00091 }
00092 }
00093
00094 public function getCode() {
00095 return $this->code;
00096 }
00097
00098 public function setCode( $code = 'en' ) {
00099 $this->code = $code;
00100 }
00101
00105 protected function getAction() {
00106 return $this->getTitle()->getFullURL();
00107 }
00108
00112 protected function doHeader() {
00113 $formParams = array(
00114 'method' => 'post',
00115 'action' => $this->getAction(),
00116 'class' => 'mw-translate-manage'
00117 );
00118
00119 return
00120 Xml::openElement( 'form', $formParams ) .
00121 Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) .
00122 Html::hidden( 'token', $this->getUser()->getEditToken() ) .
00123 Html::hidden( 'process', 1 );
00124 }
00125
00129 protected function doFooter() {
00130 return '</form>';
00131 }
00132
00136 protected function allowProcess() {
00137 $request = RequestContext::getMain()->getRequest();
00138
00139 if ( $request->wasPosted() &&
00140 $request->getBool( 'process', false ) &&
00141 $this->getUser()->matchEditToken( $request->getVal( 'token' ) )
00142 ) {
00143
00144 return true;
00145 }
00146
00147 return false;
00148 }
00149
00153 protected function getActions() {
00154 if ( $this->code === 'en' ) {
00155 return array( 'import', 'fuzzy', 'ignore' );
00156 } else {
00157 return array( 'import', 'conflict', 'ignore' );
00158 }
00159 }
00160
00166 protected function getDefaultAction( $fuzzy, $action ) {
00167 if ( $action ) {
00168 return $action;
00169 }
00170
00171 return $fuzzy ? 'conflict' : 'import';
00172 }
00173
00174 public function execute( $messages ) {
00175 $context = RequestContext::getMain();
00176 $this->out = $context->getOutput();
00177
00178
00179 $diff = new DifferenceEngine;
00180 $diff->showDiffStyle();
00181 $diff->setReducedLineNumbers();
00182
00183
00184 $process = $this->allowProcess();
00185
00186
00187 $group = $this->getGroup();
00188 $code = $this->getCode();
00189 $collection = $group->initCollection( $code );
00190 $collection->loadTranslations();
00191
00192 $this->out->addHTML( $this->doHeader() );
00193
00194
00195 $alldone = $process;
00196 $changed = array();
00197
00198 foreach ( $messages as $key => $value ) {
00199 $fuzzy = $old = false;
00200
00201 if ( isset( $collection[$key] ) ) {
00202 $old = $collection[$key]->translation();
00203 }
00204
00205
00206 if ( strval( $old ) === strval( $value ) ) {
00207 continue;
00208 }
00209
00210 if ( $old === false ) {
00211 $para = '<code class="mw-tmi-new">' . htmlspecialchars( $key ) . '</code>';
00212 $name = $context->msg( 'translate-manage-import-new' )->rawParams( $para )
00213 ->escaped();
00214 $text = TranslateUtils::convertWhiteSpaceToHTML( $value );
00215 $changed[] = self::makeSectionElement( $name, 'new', $text );
00216 } else {
00217 $diff->setText( $old, $value );
00218 $text = $diff->getDiff( '', '' );
00219 $type = 'changed';
00220
00221 $action = $context->getRequest()
00222 ->getVal( self::escapeNameForPHP( "action-$type-$key" ) );
00223
00224 if ( $process ) {
00225 if ( !count( $changed ) ) {
00226 $changed[] = '<ul>';
00227 }
00228
00229 if ( $action === null ) {
00230 $message = $context->msg(
00231 'translate-manage-inconsistent',
00232 wfEscapeWikiText( "action-$type-$key" )
00233 )->parse();
00234 $changed[] = "<li>$message</li></ul>";
00235 $process = false;
00236 } else {
00237
00238 if ( !isset( $this->time ) ) {
00239 $this->time = wfTimestamp();
00240 }
00241
00242 $message = self::doAction(
00243 $action,
00244 $group,
00245 $key,
00246 $code,
00247 $value
00248 );
00249
00250 $key = array_shift( $message );
00251 $params = $message;
00252 $message = $context->msg( $key, $params )->parse();
00253 $changed[] = "<li>$message</li>";
00254
00255 if ( $this->checkProcessTime() ) {
00256 $process = false;
00257 $message = $context->msg( 'translate-manage-toolong' )
00258 ->numParams( $this->processingTime )->parse();
00259 $changed[] = "<li>$message</li></ul>";
00260 }
00261 continue;
00262 }
00263 }
00264
00265 $alldone = false;
00266
00267 $actions = $this->getActions();
00268 $defaction = $this->getDefaultAction( $fuzzy, $action );
00269
00270 $act = array();
00271
00272
00273
00274
00275 foreach ( $actions as $action ) {
00276 $label = $context->msg( "translate-manage-action-$action" )->text();
00277 $name = self::escapeNameForPHP( "action-$type-$key" );
00278 $id = Sanitizer::escapeId( "action-$key-$action" );
00279 $act[] = Xml::radioLabel( $label, $name, $action, $id, $action === $defaction );
00280 }
00281
00282 $param = '<code class="mw-tmi-diff">' . htmlspecialchars( $key ) . '</code>';
00283 $name = $context->msg( 'translate-manage-import-diff', $param,
00284 implode( ' ', $act )
00285 )->text();
00286
00287 $changed[] = self::makeSectionElement( $name, $type, $text );
00288 }
00289 }
00290
00291 if ( !$process ) {
00292 $collection->filter( 'hastranslation', false );
00293 $keys = $collection->getMessageKeys();
00294
00295 $diff = array_diff( $keys, array_keys( $messages ) );
00296
00297 foreach ( $diff as $s ) {
00298 $para = '<code class="mw-tmi-deleted">' . htmlspecialchars( $s ) . '</code>';
00299 $name = $context->msg( 'translate-manage-import-deleted' )->rawParams( $para )->escaped();
00300 $text = TranslateUtils::convertWhiteSpaceToHTML( $collection[$s]->translation() );
00301 $changed[] = self::makeSectionElement( $name, 'deleted', $text );
00302 }
00303 }
00304
00305 if ( $process || ( !count( $changed ) && $code !== 'en' ) ) {
00306 if ( !count( $changed ) ) {
00307 $this->out->addWikiMsg( 'translate-manage-nochanges-other' );
00308 }
00309
00310 if ( !count( $changed ) || strpos( $changed[count( $changed ) - 1], '<li>' ) !== 0 ) {
00311 $changed[] = '<ul>';
00312 }
00313
00314 $message = $context->msg( 'translate-manage-import-done' )->parse();
00315 $changed[] = "<li>$message</li></ul>";
00316 $this->out->addHTML( implode( "\n", $changed ) );
00317 } else {
00318
00319 if ( count( $changed ) ) {
00320 if ( $code === 'en' ) {
00321 $this->out->addWikiMsg( 'translate-manage-intro-en' );
00322 } else {
00323 $lang = TranslateUtils::getLanguageName(
00324 $code,
00325 $context->getLanguage()->getCode()
00326 );
00327 $this->out->addWikiMsg( 'translate-manage-intro-other', $lang );
00328 }
00329 $this->out->addHTML( Html::hidden( 'language', $code ) );
00330 $this->out->addHTML( implode( "\n", $changed ) );
00331 $this->out->addHTML( Xml::submitButton( $context->msg( 'translate-manage-submit' )->text() ) );
00332 } else {
00333 $this->out->addWikiMsg( 'translate-manage-nochanges' );
00334 }
00335 }
00336
00337 $this->out->addHTML( $this->doFooter() );
00338
00339 return $alldone;
00340 }
00341
00357 public static function doAction( $action, $group, $key, $code, $message, $comment = '',
00358 $user = null, $editFlags = 0
00359 ) {
00360 global $wgTranslateDocumentationLanguageCode;
00361
00362 $title = self::makeTranslationTitle( $group, $key, $code );
00363
00364 if ( $action === 'import' || $action === 'conflict' ) {
00365 if ( $action === 'import' ) {
00366 $comment = wfMessage( 'translate-manage-import-summary' )->inContentLanguage()->plain();
00367 } else {
00368 $comment = wfMessage( 'translate-manage-conflict-summary' )->inContentLanguage()->plain();
00369 $message = self::makeTextFuzzy( $message );
00370 }
00371
00372 return self::doImport( $title, $message, $comment, $user, $editFlags );
00373 } elseif ( $action === 'ignore' ) {
00374 return array( 'translate-manage-import-ignore', $key );
00375 } elseif ( $action === 'fuzzy' && $code !== 'en' &&
00376 $code !== $wgTranslateDocumentationLanguageCode
00377 ) {
00378 $message = self::makeTextFuzzy( $message );
00379
00380 return self::doImport( $title, $message, $comment, $user, $editFlags );
00381 } elseif ( $action === 'fuzzy' && $code == 'en' ) {
00382 return self::doFuzzy( $title, $message, $comment, $user, $editFlags );
00383 } else {
00384 throw new MWException( "Unhandled action $action" );
00385 }
00386 }
00387
00388 protected function checkProcessTime() {
00389 return wfTimestamp() - $this->time >= $this->processingTime;
00390 }
00391
00402 public static function doImport( $title, $message, $comment, $user = null, $editFlags = 0 ) {
00403 $wikiPage = WikiPage::factory( $title );
00404 $status = $wikiPage->doEdit( $message, $comment, $editFlags, false, $user );
00405 $success = $status->isOK();
00406
00407 if ( $success ) {
00408 return array( 'translate-manage-import-ok',
00409 wfEscapeWikiText( $title->getPrefixedText() )
00410 );
00411 } else {
00412 $text = "Failed to import new version of page {$title->getPrefixedText()}\n";
00413 $text .= "{$status->getWikiText()}";
00414 throw new MWException( $text );
00415 }
00416 }
00417
00427 public static function doFuzzy( $title, $message, $comment, $user, $editFlags = 0 ) {
00428 $context = RequestContext::getMain();
00429
00430 if ( !$context->getUser()->isAllowed( 'translate-manage' ) ) {
00431 return $context->msg( 'badaccess-group0' )->text();
00432 }
00433
00434 $dbw = wfGetDB( DB_MASTER );
00435
00436
00437 $handle = new MessageHandle( $title );
00438 $titleText = $handle->getKey();
00439
00440 $conds = array(
00441 'page_namespace' => $title->getNamespace(),
00442 'page_latest=rev_id',
00443 'rev_text_id=old_id',
00444 'page_title' . $dbw->buildLike( "$titleText/", $dbw->anyString() ),
00445 );
00446
00447 $rows = $dbw->select(
00448 array( 'page', 'revision', 'text' ),
00449 array( 'page_title', 'page_namespace', 'old_text', 'old_flags' ),
00450 $conds,
00451 __METHOD__
00452 );
00453
00454
00455 if ( !$user ) {
00456 $user = FuzzyBot::getUser();
00457 }
00458
00459
00460 $changed = array();
00461 foreach ( $rows as $row ) {
00462 global $wgTranslateDocumentationLanguageCode;
00463
00464 $ttitle = Title::makeTitle( $row->page_namespace, $row->page_title );
00465
00466
00467 if ( $ttitle->getSubpageText() === 'en' ||
00468 $ttitle->getSubpageText() === $wgTranslateDocumentationLanguageCode
00469 ) {
00470
00471 $text = $message;
00472 } else {
00473 $text = Revision::getRevisionText( $row );
00474 $text = self::makeTextFuzzy( $text );
00475 }
00476
00477
00478 $changed[] = self::doImport(
00479 $ttitle,
00480 $text,
00481 $comment,
00482 $user,
00483 $editFlags
00484 );
00485 }
00486
00487
00488 $text = '';
00489 foreach ( $changed as $c ) {
00490 $key = array_shift( $c );
00491 $text .= "* " . $context->msg( $key, $c )->plain() . "\n";
00492 }
00493
00494 return array( 'translate-manage-import-fuzzy', "\n" . $text );
00495 }
00496
00506 public static function makeTranslationTitle( $group, $key, $code ) {
00507 $ns = $group->getNamespace();
00508
00509 return Title::makeTitleSafe( $ns, "$key/$code" );
00510 }
00511
00521 public static function makeSectionElement( $legend, $type, $content, $lang = null ) {
00522 $containerParams = array( 'class' => "mw-tpt-sp-section mw-tpt-sp-section-type-{$type}" );
00523 $legendParams = array( 'class' => 'mw-tpt-sp-legend' );
00524 $contentParams = array( 'class' => 'mw-tpt-sp-content' );
00525 if ( $lang ) {
00526 $contentParams['dir'] = wfGetLangObj( $lang )->getDir();
00527 $contentParams['lang'] = wfGetLangObj( $lang )->getCode();
00528 }
00529
00530 $output = Html::rawElement( 'div', $containerParams,
00531 Html::rawElement( 'div', $legendParams, $legend ) .
00532 Html::rawElement( 'div', $contentParams, $content )
00533 );
00534
00535 return $output;
00536 }
00537
00544 public static function makeTextFuzzy( $message ) {
00545 $message = str_replace( TRANSLATE_FUZZY, '', $message );
00546
00547 return TRANSLATE_FUZZY . $message;
00548 }
00549
00557 public static function escapeNameForPHP( $name ) {
00558 $replacements = array(
00559 "(" => '(OP)',
00560 " " => '(SP)',
00561 "\t" => '(TAB)',
00562 "." => '(DOT)',
00563 "'" => '(SQ)',
00564 "\"" => '(DQ)',
00565 "%" => '(PC)',
00566 "&" => '(AMP)',
00567 );
00568
00569
00570
00571
00572 return str_replace( array_keys( $replacements ), array_values( $replacements ), $name );
00573 }
00574 }