00001 <?php
00014 class MessageTable {
00015
00016
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( $key, $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();
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
00191
00192
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
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,
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
00349
00350
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
00378 $total = $info['total'];
00379
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 }