MessageTable.php

Go to the documentation of this file.
00001 <?php
00014 class MessageTable {
00015     /*
00016      * @var bool
00017      */
00018     protected $reviewMode = false;
00019 
00023     protected $collection;
00024 
00028     protected $group;
00029 
00033     protected $editLinkParams = array();
00037     protected $context;
00038 
00042     protected $headers = array(
00043         'table' => array( 'msg', 'allmessagesname' ),
00044         'current' => array( 'msg', 'allmessagescurrent' ),
00045         'default' => array( 'msg', 'allmessagesdefault' ),
00046     );
00047 
00054     public static function newFromContext(
00055         IContextSource $context,
00056         MessageCollection $collection,
00057         MessageGroup $group
00058     ) {
00059         $table = new self( $collection, $group );
00060         $table->setContext( $context );
00061 
00062         wfRunHooks( 'TranslateMessageTableInit', array( &$table, $context, $collection, $group ) );
00063 
00064         return $table;
00065     }
00066 
00067     public function setContext( IContextSource $context ) {
00068         $this->context = $context;
00069     }
00070 
00075     public function __construct( MessageCollection $collection, MessageGroup $group ) {
00076         $this->collection = $collection;
00077         $this->group = $group;
00078         $this->setHeaderText( 'table', $group->getLabel() );
00079         $this->appendEditLinkParams( 'loadgroup', $group->getId() );
00080     }
00081 
00082     public function setEditLinkParams( array $array ) {
00083         $this->editLinkParams = $array;
00084     }
00085 
00086     public function appendEditLinkParams( /*string*/$key, /*string*/$value ) {
00087         $this->editLinkParams[$key] = $value;
00088     }
00089 
00090     public function setReviewMode( $mode = true ) {
00091         $this->reviewMode = $mode;
00092     }
00093 
00094     public function setHeaderTextMessage( $type, $value ) {
00095         if ( !isset( $this->headers[$type] ) ) {
00096             throw new MWException( "Unexpected type $type" );
00097         }
00098 
00099         $this->headers[$type] = array( 'msg', $value );
00100     }
00101 
00102     public function setHeaderText( $type, $value ) {
00103         if ( !isset( $this->headers[$type] ) ) {
00104             throw new MWException( "Unexpected type $type" );
00105         }
00106 
00107         $this->headers[$type] = array( 'raw', htmlspecialchars( $value ) );
00108     }
00109 
00110     public function includeAssets() {
00111         TranslationHelpers::addModules( $this->context->getOutput() );
00112         $pages = array();
00113 
00114         foreach ( $this->collection->getTitles() as $title ) {
00115             $pages[] = $title->getPrefixedDBKey();
00116         }
00117 
00118         $vars = array( 'trlKeys' => $pages );
00119         $this->context->getOutput()->addScript( Skin::makeVariablesScript( $vars ) );
00120     }
00121 
00122     public function header() {
00123         $tableheader = Xml::openElement( 'table', array(
00124             'class' => 'mw-sp-translate-table'
00125         ) );
00126 
00127         if ( $this->reviewMode ) {
00128             $tableheader .= Xml::openElement( 'tr' );
00129             $tableheader .= Xml::element( 'th',
00130                 array( 'rowspan' => '2' ),
00131                 $this->headerText( 'table' )
00132             );
00133             $tableheader .= Xml::tags( 'th', null, $this->headerText( 'default' ) );
00134             $tableheader .= Xml::closeElement( 'tr' );
00135 
00136             $tableheader .= Xml::openElement( 'tr' );
00137             $tableheader .= Xml::tags( 'th', null, $this->headerText( 'current' ) );
00138             $tableheader .= Xml::closeElement( 'tr' );
00139         } else {
00140             $tableheader .= Xml::openElement( 'tr' );
00141             $tableheader .= Xml::tags( 'th', null, $this->headerText( 'table' ) );
00142             $tableheader .= Xml::tags( 'th', null, $this->headerText( 'current' ) );
00143             $tableheader .= Xml::closeElement( 'tr' );
00144         }
00145 
00146         return $tableheader . "\n";
00147     }
00148 
00149     public function contents() {
00150         $optional = $this->context->msg( 'translate-optional' )->escaped();
00151 
00152         $this->doLinkBatch();
00153 
00154         $sourceLang = Language::factory( $this->group->getSourceLanguage() );
00155         $targetLang = Language::factory( $this->collection->getLanguage() );
00156         $titleMap = $this->collection->keys();
00157 
00158         $output = '';
00159 
00160         $this->collection->initMessages(); // Just to be sure
00161 
00165         foreach ( $this->collection as $key => $m ) {
00166             $tools = array();
00170             $title = $titleMap[$key];
00171 
00172             $original = $m->definition();
00173             $translation = $m->translation();
00174 
00175             $hasTranslation = $translation !== null;
00176 
00177             if ( $hasTranslation ) {
00178                 $message = $translation;
00179                 $extraAttribs = self::getLanguageAttributes( $targetLang );
00180             } else {
00181                 $message = $original;
00182                 $extraAttribs = self::getLanguageAttributes( $sourceLang );
00183             }
00184 
00185             wfRunHooks(
00186                 'TranslateFormatMessageBeforeTable',
00187                 array( &$message, $m, $this->group, $targetLang, &$extraAttribs )
00188             );
00189 
00190             // Using Html::element( a ) because Linker::link is memory hog.
00191             // It takes about 20 KiB per call, and that times 5000 is quite
00192             // a lot of memory.
00193             $niceTitle = htmlspecialchars( $this->context->getLanguage()->truncate(
00194                 $title->getPrefixedText(),
00195                 -35
00196             ) );
00197             $linkAttribs = array(
00198                 'href' => $title->getLocalUrl( array( 'action' => 'edit' ) + $this->editLinkParams ),
00199             );
00200             $linkAttribs += TranslationEditPage::jsEdit( $title, $this->group->getId() );
00201 
00202             $tools['edit'] = Html::element( 'a', $linkAttribs, $niceTitle );
00203 
00204             $anchor = 'msg_' . $key;
00205             $anchor = Xml::element( 'a', array( 'id' => $anchor, 'href' => "#$anchor" ), "↓" );
00206 
00207             $extra = '';
00208             if ( $m->hasTag( 'optional' ) ) {
00209                 $extra = '<br />' . $optional;
00210             }
00211 
00212             $tqeData = $extraAttribs + array(
00213                 'data-title' => $title->getPrefixedText(),
00214                 'data-group' => $this->group->getId(),
00215                 'id' => 'tqe-anchor-' . substr( sha1( $title->getPrefixedText() ), 0, 12 ),
00216                 'class' => 'tqe-inlineeditable ' . ( $hasTranslation ? 'translated' : 'untranslated' )
00217             );
00218 
00219             $button = $this->getReviewButton( $m );
00220             $status = $this->getReviewStatus( $m );
00221             $leftColumn = $button . $anchor . $tools['edit'] . $extra . $status;
00222 
00223             if ( $this->reviewMode ) {
00224                 $output .= Xml::tags( 'tr', array( 'class' => 'orig' ),
00225                     Xml::tags( 'td', array( 'rowspan' => '2' ), $leftColumn ) .
00226                         Xml::tags( 'td', self::getLanguageAttributes( $sourceLang ),
00227                             TranslateUtils::convertWhiteSpaceToHTML( $original )
00228                         )
00229                 );
00230 
00231                 $output .= Xml::tags( 'tr', null,
00232                     Xml::tags( 'td', $tqeData, TranslateUtils::convertWhiteSpaceToHTML( $message ) )
00233                 );
00234             } else {
00235                 $output .= Xml::tags( 'tr', array( 'class' => 'def' ),
00236                     Xml::tags( 'td', null, $leftColumn ) .
00237                         Xml::tags( 'td', $tqeData, TranslateUtils::convertWhiteSpaceToHTML( $message ) )
00238                 );
00239             }
00240 
00241             $output .= "\n";
00242         }
00243 
00244         return $output;
00245     }
00246 
00247     public function fullTable( $offsets, $nondefaults ) {
00248         $this->includeAssets();
00249 
00250         $content = $this->header() . $this->contents() . '</table>';
00251         $pager = $this->doStupidLinks( $offsets, $nondefaults );
00252 
00253         if ( $offsets['count'] === 0 ) {
00254             return $pager;
00255         } elseif ( $offsets['count'] === $offsets['total'] ) {
00256             return $content . $pager;
00257         } else {
00258             return $pager . $content . $pager;
00259         }
00260     }
00261 
00262     protected function headerText( $type ) {
00263         if ( !isset( $this->headers[$type] ) ) {
00264             throw new MWException( "Unexpected type $type" );
00265         }
00266 
00267         list( $format, $value ) = $this->headers[$type];
00268         if ( $format === 'msg' ) {
00269             return wfMessage( $value )->escaped();
00270         } elseif ( $format === 'raw' ) {
00271             return $value;
00272         } else {
00273             throw new MWException( "Unexcepted format $format" );
00274         }
00275     }
00276 
00277     protected static function getLanguageAttributes( Language $language ) {
00278         global $wgTranslateDocumentationLanguageCode;
00279 
00280         $code = $language->getCode();
00281         $dir = $language->getDir();
00282 
00283         if ( $code === $wgTranslateDocumentationLanguageCode ) {
00284             // Should be good enough for now
00285             $code = 'en';
00286         }
00287 
00288         return array( 'lang' => $code, 'dir' => $dir );
00289     }
00290 
00291     protected function getReviewButton( TMessage $message ) {
00292         $revision = $message->getProperty( 'revision' );
00293         $user = $this->context->getUser();
00294 
00295         if ( !$this->reviewMode || !$user->isAllowed( 'translate-messagereview' ) || !$revision ) {
00296             return '';
00297         }
00298 
00299         $attribs = array(
00300             'type' => 'button',
00301             'class' => 'mw-translate-messagereviewbutton',
00302             'data-token' => ApiTranslationReview::getToken( 0, '' ),
00303             'data-revision' => $revision,
00304             'name' => 'acceptbutton-' . $revision, // Otherwise Firefox disables buttons on page load
00305         );
00306 
00307         $reviewers = (array)$message->getProperty( 'reviewers' );
00308         if ( in_array( $user->getId(), $reviewers ) ) {
00309             $attribs['value'] = wfMessage( 'translate-messagereview-done' )->text();
00310             $attribs['disabled'] = 'disabled';
00311             $attribs['title'] = wfMessage( 'translate-messagereview-doit' )->text();
00312         } elseif ( $message->hasTag( 'fuzzy' ) ) {
00313             $attribs['value'] = wfMessage( 'translate-messagereview-submit' )->text();
00314             $attribs['disabled'] = 'disabled';
00315             $attribs['title'] = wfMessage( 'translate-messagereview-no-fuzzy' )->text();
00316         } elseif ( $user->getName() === $message->getProperty( 'last-translator-text' ) ) {
00317             $attribs['value'] = wfMessage( 'translate-messagereview-submit' )->text();
00318             $attribs['disabled'] = 'disabled';
00319             $attribs['title'] = wfMessage( 'translate-messagereview-no-own' )->text();
00320         } else {
00321             $attribs['value'] = wfMessage( 'translate-messagereview-submit' )->text();
00322         }
00323 
00324         $review = Html::element( 'input', $attribs );
00325 
00326         return $review;
00327     }
00328 
00330     protected $reviewStatusCache = array();
00331 
00332     protected function getReviewStatus( TMessage $message ) {
00333         if ( !$this->reviewMode ) {
00334             return '';
00335         }
00336 
00337         $reviewers = (array)$message->getProperty( 'reviewers' );
00338         $count = count( $reviewers );
00339 
00340         if ( $count === 0 ) {
00341             return '';
00342         }
00343 
00344         $userId = $this->context->getUser()->getId();
00345         $you = in_array( $userId, $reviewers );
00346         $key = $you ? "y$count" : "n$count";
00347 
00348         // ->text() (and ->parse()) invokes the parser. Each call takes
00349         // about 70 KiB, so it makes sense to cache these messages which
00350         // have high repetition.
00351         if ( isset( $this->reviewStatusCache[$key] ) ) {
00352             return $this->reviewStatusCache[$key];
00353         } elseif ( $you ) {
00354             $msg = wfMessage( 'translate-messagereview-reviewswithyou' )->numParams( $count )->text();
00355         } else {
00356             $msg = wfMessage( 'translate-messagereview-reviews' )->numParams( $count )->text();
00357         }
00358 
00359         $wrap = Html::rawElement( 'div', array( 'class' => 'mw-translate-messagereviewstatus' ), $msg );
00360         $this->reviewStatusCache[$key] = $wrap;
00361 
00362         return $wrap;
00363     }
00364 
00365     protected function doLinkBatch() {
00366         $batch = new LinkBatch();
00367         $batch->setCaller( __METHOD__ );
00368 
00369         foreach ( $this->collection->getTitles() as $title ) {
00370             $batch->addObj( $title );
00371         }
00372 
00373         $batch->execute();
00374     }
00375 
00376     protected function doStupidLinks( $info, $nondefaults ) {
00377         // Total number of messages for this query
00378         $total = $info['total'];
00379         // Messages in this page
00380         $count = $info['count'];
00381 
00382         $allInThisPage = $info['start'] === 0 && $total === $count;
00383 
00384         if ( $info['count'] === 0 ) {
00385             $navigation = wfMessage( 'translate-page-showing-none' )->parse();
00386         } elseif ( $allInThisPage ) {
00387             $navigation = wfMessage( 'translate-page-showing-all' )->numParams( $total )->parse();
00388         } else {
00389             $previous = wfMessage( 'translate-prev' )->escaped();
00390 
00391             if ( $info['backwardsOffset'] !== false ) {
00392                 $previous = $this->makeOffsetLink( $previous, $info['backwardsOffset'], $nondefaults );
00393             }
00394 
00395             $nextious = wfMessage( 'translate-next' )->escaped();
00396             if ( $info['forwardsOffset'] !== false ) {
00397                 $nextious = $this->makeOffsetLink( $nextious, $info['forwardsOffset'], $nondefaults );
00398             }
00399 
00400             $start = $info['start'] + 1;
00401             $stop = $start + $info['count'] - 1;
00402             $total = $info['total'];
00403 
00404             $navigation = wfMessage( 'translate-page-showing' )
00405                 ->numParams( $start, $stop, $total )->parse();
00406             $navigation .= ' ';
00407             $navigation .= wfMessage( 'translate-page-paging-links' )
00408                 ->rawParams( $previous, $nextious )->escaped();
00409         }
00410 
00411         return Html::openElement( 'fieldset' ) .
00412             Html::element( 'legend', array(), wfMessage( 'translate-page-navigation-legend' )->text() ) .
00413             $navigation .
00414             Html::closeElement( 'fieldset' );
00415     }
00416 
00417     protected function makeOffsetLink( $label, $offset, $nondefaults ) {
00418         $query = array_merge(
00419             $nondefaults,
00420             array( 'offset' => $offset )
00421         );
00422 
00423         $link = Linker::link(
00424             $this->context->getTitle(),
00425             $label,
00426             array(),
00427             $query
00428         );
00429 
00430         return $link;
00431     }
00432 }
Generated on Tue Oct 29 00:00:26 2013 for MediaWiki Translate Extension by  doxygen 1.6.3