MessageWebImporter.php

Go to the documentation of this file.
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         // Set up diff engine
00179         $diff = new DifferenceEngine;
00180         $diff->showDiffStyle();
00181         $diff->setReducedLineNumbers();
00182 
00183         // Check whether we do processing
00184         $process = $this->allowProcess();
00185 
00186         // Initialise collection
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         // Determine changes
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             // No changes at all, ignore
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                         // Check processing time
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                 // Give grep a chance to find the usages:
00273                 // translate-manage-action-import, translate-manage-action-conflict,
00274                 // translate-manage-action-ignore, translate-manage-action-fuzzy
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             // END
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         // Work on all subpages of base title.
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         // Edit with fuzzybot if there is no user.
00455         if ( !$user ) {
00456             $user = FuzzyBot::getUser();
00457         }
00458 
00459         // Process all rows.
00460         $changed = array();
00461         foreach ( $rows as $row ) {
00462             global $wgTranslateDocumentationLanguageCode;
00463 
00464             $ttitle = Title::makeTitle( $row->page_namespace, $row->page_title );
00465 
00466             // No fuzzy for English original or documentation language code.
00467             if ( $ttitle->getSubpageText() === 'en' ||
00468                 $ttitle->getSubpageText() === $wgTranslateDocumentationLanguageCode
00469             ) {
00470                 // Use imported text, not database text.
00471                 $text = $message;
00472             } else {
00473                 $text = Revision::getRevisionText( $row );
00474                 $text = self::makeTextFuzzy( $text );
00475             }
00476 
00477             // Do actual import
00478             $changed[] = self::doImport(
00479                 $ttitle,
00480                 $text,
00481                 $comment,
00482                 $user,
00483                 $editFlags
00484             );
00485         }
00486 
00487         // Format return text
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         /* How nice of you PHP. No way to split array into keys and values in one
00570          * function or have str_replace which takes one array? */
00571 
00572         return str_replace( array_keys( $replacements ), array_values( $replacements ), $name );
00573     }
00574 }
Generated on Tue Oct 29 00:00:26 2013 for MediaWiki Translate Extension by  doxygen 1.6.3