00001 <?php
00022 class SpecialManageGroups extends SpecialPage {
00023 const CHANGEFILE = 'translate_messagechanges.cdb';
00024 const RIGHT = 'translate-manage';
00025
00029 protected $diff;
00030
00031 public function __construct() {
00032
00033 parent::__construct( 'ManageMessageGroups' );
00034 }
00035
00036 public function execute( $par ) {
00037 $this->setHeaders();
00038 $out = $this->getOutput();
00039 $out->addModules( 'ext.translate.special.managegroups' );
00040 TranslateUtils::addSpecialHelpLink( $out, 'Help:Extension:Translate/Group_management' );
00041
00042 $changefile = TranslateUtils::cacheFile( self::CHANGEFILE );
00043 if ( !file_exists( $changefile ) ) {
00044
00045
00046 $out->addWikiMsg( 'translate-smg-nochanges' );
00047
00048 return;
00049 }
00050
00051 $user = $this->getUser();
00052 $allowed = $user->isAllowed( self::RIGHT );
00053
00054 $req = $this->getRequest();
00055 if ( !$req->wasPosted() ) {
00056 $this->showChanges( $allowed, $this->getLimit() );
00057
00058 return;
00059 }
00060
00061 $token = $req->getVal( 'token' );
00062 if ( !$allowed || !$user->matchEditToken( $token ) ) {
00063 throw new PermissionsError( self::RIGHT );
00064 }
00065
00066 $this->processSubmit();
00067 }
00068
00073 protected function getLimit() {
00074 $limits = array(
00075 1000,
00076 ini_get( 'max_input_vars' ),
00077 ini_get( 'suhosin.post.max_vars' ),
00078 ini_get( 'suhosin.request.max_vars' )
00079 );
00080
00081 $limits = array_filter( $limits );
00082 return min( $limits );
00083 }
00084
00085 protected function getLegend() {
00086 $text = $this->diff->addHeader(
00087 '',
00088 $this->msg( 'translate-smg-left' )->escaped(),
00089 $this->msg( 'translate-smg-right' )->escaped()
00090 );
00091
00092 return Html::rawElement( 'div', array( 'class' => "mw-translate-smg-header" ), $text );
00093 }
00094
00095 protected function showChanges( $allowed, $limit ) {
00096 global $wgContLang;
00097
00098 $diff = new DifferenceEngine( $this->getContext() );
00099 $diff->showDiffStyle();
00100 $diff->setReducedLineNumbers();
00101 $this->diff = $diff;
00102
00103 $out = $this->getOutput();
00104 $out->addHtml(
00105 '' .
00106 Html::openElement( 'form', array( 'method' => 'post' ) ) .
00107 Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) .
00108 Html::hidden( 'token', $this->getUser()->getEditToken() ) .
00109 $this->getLegend()
00110 );
00111
00112
00113 $limit = $limit - 2;
00114
00115 $changefile = TranslateUtils::cacheFile( self::CHANGEFILE );
00116 $reader = CdbReader::open( $changefile );
00117 $groups = unserialize( $reader->get( '#keys' ) );
00118 foreach ( $groups as $id ) {
00119 $group = MessageGroups::getGroup( $id );
00120 if ( !$group ) {
00121 continue;
00122 }
00123
00124 $changes = unserialize( $reader->get( $id ) );
00125 $out->addHtml( Html::element( 'h2', array(), $group->getLabel() ) );
00126
00127
00128 $lb = new LinkBatch();
00129 $ns = $group->getNamespace();
00130 $isCap = MWNamespace::isCapitalized( $ns );
00131 foreach ( $changes as $code => $subchanges ) {
00132 foreach ( $subchanges as $messages ) {
00133 foreach ( $messages as $params ) {
00134
00135 $key = $params['key'];
00136 if ( $isCap ) {
00137 $key = $wgContLang->ucfirst( $key );
00138 }
00139 $lb->add( $ns, "$key/$code" );
00140 }
00141 }
00142 }
00143 $lb->execute();
00144
00145 foreach ( $changes as $code => $subchanges ) {
00146 foreach ( $subchanges as $type => $messages ) {
00147 foreach ( $messages as $params ) {
00148 $change = $this->formatChange( $group, $code, $type, $params, $limit );
00149 $out->addHtml( $change );
00150
00151 if ( $limit <= 0 ) {
00152
00153
00154 $out->wrapWikiMsg( "<div class=warning>\n$1\n</div>", 'translate-smg-more' );
00155 break 4;
00156 }
00157 }
00158 }
00159 }
00160 }
00161
00162 $attribs = array( 'type' => 'submit', 'class' => 'mw-translate-smg-submit' );
00163 if ( !$allowed ) {
00164 $attribs['disabled'] = 'disabled';
00165 $attribs['title'] = $this->msg( 'translate-smg-notallowed' )->text();
00166 }
00167 $button = Html::element( 'button', $attribs, $this->msg( 'translate-smg-submit' )->text() );
00168 $out->addHtml( $button );
00169 $out->addHtml( Html::closeElement( 'form' ) );
00170 }
00171
00179 protected function formatChange( MessageGroup $group, $code, $type, $params, &$limit ) {
00180 $key = $params['key'];
00181 $title = Title::makeTitleSafe( $group->getNamespace(), "$key/$code" );
00182 $id = self::changeId( $group->getId(), $code, $type, $key );
00183
00184 if ( $title && $title->exists() && $type === 'addition' ) {
00185
00186
00187
00188
00189
00190
00191
00192 $type = 'change';
00193 } elseif ( $title && !$title->exists() && ( $type === 'deletion' || $type === 'change' ) ) {
00194 return '';
00195 }
00196
00197 $text = '';
00198 if ( $type === 'deletion' ) {
00199 $wiki = Revision::newFromTitle( $title )->getText();
00200 $this->diff->setText( $wiki, '' );
00201 $text = $this->diff->getDiff( Linker::link( $title ), '' );
00202 } elseif ( $type === 'addition' ) {
00203 $this->diff->setText( '', $params['content'] );
00204 $text = $this->diff->getDiff( '', Linker::link( $title ) );
00205 } elseif ( $type === 'change' ) {
00206 $wiki = Revision::newFromTitle( $title )->getText();
00207 $handle = new MessageHandle( $title );
00208 if ( $handle->isFuzzy() ) {
00209 $wiki = '!!FUZZY!!' . str_replace( TRANSLATE_FUZZY, '', $wiki );
00210 }
00211
00212 $label = $this->msg( 'translate-manage-action-ignore' )->text();
00213 $actions = Xml::checkLabel( $label, "i/$id", "i/$id" );
00214 $limit--;
00215
00216 if ( $group->getSourceLanguage() === $code ) {
00217 $label = $this->msg( 'translate-manage-action-fuzzy' )->text();
00218 $actions .= ' ' . Xml::checkLabel( $label, "f/$id", "f/$id" );
00219 $limit--;
00220 }
00221
00222 $this->diff->setText( $wiki, $params['content'] );
00223 $text .= $this->diff->getDiff( Linker::link( $title ), $actions );
00224 }
00225
00226 $hidden = Html::hidden( $id, 1 );
00227 $limit--;
00228 $text .= $hidden;
00229 $classes = "mw-translate-smg-change smg-change-$type";
00230
00231 if ( $limit < 0 ) {
00232
00233 return '';
00234 }
00235
00236 return Html::rawElement( 'div', array( 'class' => $classes ), $text );
00237 }
00238
00239 protected function processSubmit() {
00240 $req = $this->getRequest();
00241 $out = $this->getOutput();
00242
00243 $jobs = array();
00244 $jobs[] = MessageIndexRebuildJob::newJob();
00245
00246 $changefile = TranslateUtils::cacheFile( self::CHANGEFILE );
00247 $reader = CdbReader::open( $changefile );
00248 $groups = unserialize( $reader->get( '#keys' ) );
00249
00250 $postponed = array();
00251
00252 foreach ( $groups as $groupId ) {
00253 $group = MessageGroups::getGroup( $groupId );
00254 $changes = unserialize( $reader->get( $groupId ) );
00255
00256 foreach ( $changes as $code => $subchanges ) {
00257 foreach ( $subchanges as $type => $messages ) {
00258 foreach ( $messages as $index => $params ) {
00259 $id = self::changeId( $groupId, $code, $type, $params['key'] );
00260 if ( $req->getVal( $id ) === null ) {
00261
00262 $postponed[$groupId][$code][$type][$index] = $params;
00263 continue;
00264 }
00265
00266 if ( $type === 'deletion' || $req->getCheck( "i/$id" ) ) {
00267 continue;
00268 }
00269
00270 $fuzzy = $req->getCheck( "f/$id" ) ? 'fuzzy' : false;
00271 $key = $params['key'];
00272 $title = Title::makeTitleSafe( $group->getNamespace(), "$key/$code" );
00273 $jobs[] = MessageUpdateJob::newJob( $title, $params['content'], $fuzzy );
00274 }
00275 }
00276
00277 if ( !isset( $postponed[$groupId][$code] ) ) {
00278 $cache = new MessageGroupCache( $groupId, $code );
00279 $cache->create();
00280 }
00281 }
00282 }
00283
00284 Job::batchInsert( $jobs );
00285
00286 $reader->close();
00287 rename( $changefile, $changefile . '-' . wfTimestamp() );
00288
00289 if ( count( $postponed ) ) {
00290 $changefile = TranslateUtils::cacheFile( self::CHANGEFILE );
00291 $writer = CdbWriter::open( $changefile );
00292 $keys = array_keys( $postponed );
00293 $writer->set( '#keys', serialize( $keys ) );
00294 foreach ( $postponed as $groupId => $changes ) {
00295 $writer->set( $groupId, serialize( $changes ) );
00296 }
00297 $writer->close();
00298 $this->showChanges( true, $this->getLimit() );
00299 } else {
00300 $out->addWikiMsg( 'translate-smg-submitted' );
00301 }
00302 }
00303
00304 protected static function changeId( $groupId, $code, $type, $key ) {
00305 return 'smg/' . substr( sha1( "$groupId/$code/$type/$key" ), 0, 7 );
00306 }
00307
00313 static function tabify( Skin $skin, array &$tabs ) {
00314 $title = $skin->getTitle();
00315 list( $alias, ) = SpecialPageFactory::resolveAlias( $title->getText() );
00316
00317 $pagesInGroup = array(
00318 'ManageMessageGroups' => 'namespaces',
00319 'AggregateGroups' => 'namespaces',
00320 'SupportedLanguages' => 'views',
00321 'TranslationStats' => 'views',
00322 );
00323 if ( !isset( $pagesInGroup[$alias] ) ) {
00324 return true;
00325 }
00326
00327 $skin->getOutput()->addModules( 'ext.translate.tabgroup' );
00328
00329 $tabs['namespaces'] = array();
00330 foreach ( $pagesInGroup as $spName => $section ) {
00331 $spClass = SpecialPageFactory::getPage( $spName );
00332 if ( $spClass === null ) {
00333 continue;
00334 }
00335 $spTitle = $spClass->getTitle();
00336
00337 $tabs[$section][strtolower( $spName )] = array(
00338 'text' => $spClass->getDescription(),
00339 'href' => $spTitle->getLocalUrl(),
00340 'class' => $alias === $spName ? 'selected' : '',
00341 );
00342 }
00343
00344 return true;
00345 }
00346 }