SpecialSupportedLanguages.php

Go to the documentation of this file.
00001 <?php
00022 class SpecialSupportedLanguages extends SpecialPage {
00024     protected $purge = false;
00025 
00027     protected $period = 180;
00028 
00029     public function __construct() {
00030         parent::__construct( 'SupportedLanguages' );
00031     }
00032 
00033     public function execute( $par ) {
00034         $out = $this->getOutput();
00035         $lang = $this->getLanguage();
00036 
00037         $this->purge = $this->getRequest()->getVal( 'action' ) === 'purge';
00038 
00039         $this->setHeaders();
00040         $out->addModules( 'ext.translate.special.supportedlanguages' );
00041 
00042         // Do not add html content to OutputPage before this block of code!
00043         $cache = wfGetCache( CACHE_ANYTHING );
00044         $cachekey = wfMemcKey( 'translate-supportedlanguages', $lang->getCode() );
00045         $data = $cache->get( $cachekey );
00046         if ( !$this->purge && is_string( $data ) ) {
00047             TranslateUtils::addSpecialHelpLink(
00048                 $out,
00049                 'Help:Extension:Translate/Statistics_and_reporting#List_of_languages_and_translators'
00050             );
00051             $out->addHtml( $data );
00052 
00053             return;
00054         }
00055 
00056         TranslateUtils::addSpecialHelpLink(
00057             $out,
00058             'Help:Extension:Translate/Statistics_and_reporting#List_of_languages_and_translators'
00059         );
00060 
00061         $this->outputHeader();
00062         $dbr = wfGetDB( DB_SLAVE );
00063         if ( $dbr->getType() === 'sqlite' ) {
00064             $out->addWikiText( '<div class=errorbox>SQLite is not supported.</div>' );
00065 
00066             return;
00067         }
00068 
00069         $out->addWikiMsg( 'supportedlanguages-colorlegend', $this->getColorLegend() );
00070         $out->addWikiMsg( 'supportedlanguages-localsummary' );
00071 
00072         // Check if CLDR extension has been installed.
00073         $cldrInstalled = class_exists( 'LanguageNames' );
00074 
00075         $locals = array();
00076         if ( $cldrInstalled ) {
00077             $locals = LanguageNames::getNames( $lang->getCode(),
00078                 LanguageNames::FALLBACK_NORMAL,
00079                 LanguageNames::LIST_MW_AND_CLDR
00080             );
00081         }
00082 
00083         $natives = Language::getLanguageNames( false );
00084         ksort( $natives );
00085 
00086         $this->outputLanguageCloud( $natives );
00087 
00088         // Requires NS_PORTAL. If not present, display error text.
00089         if ( !defined( 'NS_PORTAL' ) ) {
00090             $users = $this->fetchTranslatorsAuto();
00091         } else {
00092             $users = $this->fetchTranslatorsPortal( $natives );
00093         }
00094 
00095         $this->preQueryUsers( $users );
00096 
00097         list( $editcounts, $lastedits ) = $this->getUserStats();
00098 
00099         // Information to be used inside the foreach loop.
00100         $linkInfo['rc']['title'] = SpecialPage::getTitleFor( 'Recentchanges' );
00101         $linkInfo['rc']['msg'] = $this->msg( 'supportedlanguages-recenttranslations' )->escaped();
00102         $linkInfo['stats']['title'] = SpecialPage::getTitleFor( 'LanguageStats' );
00103         $linkInfo['stats']['msg'] = $this->msg( 'languagestats' )->escaped();
00104 
00105         foreach ( array_keys( $natives ) as $code ) {
00106             if ( !isset( $users[$code] ) ) {
00107                 continue;
00108             }
00109 
00110             // If CLDR is installed, add localised header and link title.
00111             if ( $cldrInstalled ) {
00112                 $headerText = $this->msg( 'supportedlanguages-portallink' )
00113                     ->params( $code, $locals[$code], $natives[$code] )->escaped();
00114             } else {
00115                 // No CLDR, so a less localised header and link title.
00116                 $headerText = $this->msg( 'supportedlanguages-portallink-nocldr' )
00117                     ->params( $code, $natives[$code] )->escaped();
00118             }
00119 
00120             $headerText = htmlspecialchars( $headerText );
00121 
00122             $out->addHtml( Html::openElement( 'h2', array( 'id' => $code ) ) );
00123             if ( defined( 'NS_PORTAL' ) ) {
00124                 $portalTitle = Title::makeTitleSafe( NS_PORTAL, $code );
00125                 $out->addHtml( Linker::linkKnown( $portalTitle, $headerText ) );
00126             } else {
00127                 $out->addHtml( $headerText );
00128             }
00129 
00130             $out->addHTML( "</h2>" );
00131 
00132             // Add useful links for language stats and recent changes for the language.
00133             $links = array();
00134             $links[] = Linker::link(
00135                 $linkInfo['stats']['title'],
00136                 $linkInfo['stats']['msg'],
00137                 array(),
00138                 array(
00139                     'code' => $code,
00140                     'suppresscomplete' => '1'
00141                 ),
00142                 array( 'known', 'noclasses' )
00143             );
00144             $links[] = Linker::link(
00145                 $linkInfo['rc']['title'],
00146                 $linkInfo['rc']['msg'],
00147                 array(),
00148                 array(
00149                     'translations' => 'only',
00150                     'trailer' => "/" . $code
00151                 ),
00152                 array( 'known', 'noclasses' )
00153             );
00154             $linkList = $lang->listToText( $links );
00155 
00156             $out->addHTML( "<p>" . $linkList . "</p>\n" );
00157             $this->makeUserList( $users[$code], $editcounts, $lastedits );
00158         }
00159 
00160         $out->addHtml( Html::element( 'hr' ) );
00161         $out->addWikiMsg( 'supportedlanguages-count', $lang->formatNum( count( $users ) ) );
00162 
00163         $cache->set( $cachekey, $out->getHTML(), 3600 );
00164     }
00165 
00166     protected function languageCloud() {
00167         global $wgTranslateMessageNamespaces;
00168 
00169         $cache = wfGetCache( CACHE_ANYTHING );
00170         $cachekey = wfMemcKey( 'translate-supportedlanguages-language-cloud' );
00171         $data = $cache->get( $cachekey );
00172         if ( !$this->purge && is_array( $data ) ) {
00173             return $data;
00174         }
00175 
00176         $dbr = wfGetDB( DB_SLAVE );
00177         $tables = array( 'recentchanges' );
00178         $fields = array( 'substring_index(rc_title, \'/\', -1) as lang', 'count(*) as count' );
00179         $timestamp = $dbr->timestamp( TS_DB, wfTimeStamp( TS_UNIX ) - 60 * 60 * 24 * $this->period );
00180         $conds = array(
00181             'rc_title' . $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() ),
00182             'rc_namespace' => $wgTranslateMessageNamespaces,
00183             'rc_timestamp > ' . $timestamp,
00184         );
00185         $options = array( 'GROUP BY' => 'lang', 'HAVING' => 'count > 20' );
00186 
00187         $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $options );
00188 
00189         $data = array();
00190         foreach ( $res as $row ) {
00191             $data[$row->lang] = $row->count;
00192         }
00193 
00194         $cache->set( $cachekey, $data, 3600 );
00195 
00196         return $data;
00197     }
00198 
00199     protected function fetchTranslatorsAuto() {
00200         global $wgTranslateMessageNamespaces;
00201 
00202         $cache = wfGetCache( CACHE_ANYTHING );
00203         $cachekey = wfMemcKey( 'translate-supportedlanguages-translator-list' );
00204         $data = $cache->get( $cachekey );
00205         if ( !$this->purge && is_array( $data ) ) {
00206             return $data;
00207         }
00208 
00209         $dbr = wfGetDB( DB_SLAVE );
00210         $tables = array( 'page', 'revision' );
00211         $fields = array(
00212             'rev_user_text',
00213             'substring_index(page_title, \'/\', -1) as lang',
00214             'count(page_id) as count'
00215         );
00216         $conds = array(
00217             'page_title' . $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() ),
00218             'page_namespace' => $wgTranslateMessageNamespaces,
00219             'page_id=rev_page',
00220         );
00221         $options = array( 'GROUP BY' => 'rev_user_text, lang' );
00222 
00223         $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $options );
00224 
00225         $data = array();
00226         foreach ( $res as $row ) {
00227             $data[$row->lang][$row->rev_user_text] = $row->count;
00228         }
00229 
00230         $cache->set( $cachekey, $data, 3600 );
00231 
00232         return $data;
00233     }
00234 
00235     public function fetchTranslatorsPortal( $natives ) {
00236         $titles = array();
00237         foreach ( $natives as $code => $_ ) {
00238             $titles[] = Title::capitalize( $code, NS_PORTAL ) . '/translators';
00239         }
00240 
00241         $dbr = wfGetDB( DB_SLAVE );
00242         $tables = array( 'page', 'revision', 'text' );
00243         $vars = array_merge(
00244             Revision::selectTextFields(),
00245             array( 'page_title', 'page_namespace' ),
00246             Revision::selectFields()
00247         );
00248         $conds = array(
00249             'page_latest = rev_id',
00250             'rev_text_id = old_id',
00251             'page_namespace' => NS_PORTAL,
00252             'page_title' => $titles,
00253         );
00254 
00255         $res = $dbr->select( $tables, $vars, $conds, __METHOD__ );
00256 
00257         $users = array();
00258         $lb = new LinkBatch;
00259 
00260         foreach ( $res as $row ) {
00261             $rev = new Revision( $row );
00262             $text = $rev->getText();
00263             $code = strtolower( preg_replace( '!/translators$!', '', $row->page_title ) );
00264 
00265             preg_match_all( '!{{[Uu]ser\|([^}|]+)!', $text, $matches, PREG_SET_ORDER );
00266             foreach ( $matches as $match ) {
00267                 $user = Title::capitalize( $match[1], NS_USER );
00268                 $lb->add( NS_USER, $user );
00269                 $lb->add( NS_USER_TALK, $user );
00270                 if ( !isset( $users[$code] ) ) {
00271                     $users[$code] = array();
00272                 }
00273                 $users[$code][strtr( $user, '_', ' ' )] = -1;
00274             }
00275         }
00276 
00277         $lb->execute();
00278 
00279         return $users;
00280     }
00281 
00282     protected function outputLanguageCloud( $names ) {
00283         $out = $this->getOutput();
00284 
00285         $langs = $this->languageCloud();
00286         $out->addHtml( '<div class="tagcloud autonym">' );
00287         $langs = $this->shuffle_assoc( $langs );
00288         foreach ( $langs as $k => $v ) {
00289             $name = isset( $names[$k] ) ? $names[$k] : $k;
00290             $size = round( log( $v ) * 20 ) + 10;
00291 
00292             $params = array(
00293                 'href' => "#$k",
00294                 'class' => 'tag',
00295                 'style' => "font-size:$size%",
00296                 'lang' => $k,
00297             );
00298 
00299             $tag = Html::element( 'a', $params, $name );
00300             $out->addHtml( $tag . "\n" );
00301         }
00302         $out->addHtml( '</div>' );
00303     }
00304 
00305     protected function makeUserList( $users, $editcounts, $lastedits ) {
00306         $day = 60 * 60 * 24;
00307 
00308         // Scale of the activity colors, anything
00309         // longer than this is just inactive
00310         $period = $this->period;
00311 
00312         $links = array();
00313         $statsTable = new StatsTable();
00314 
00315         foreach ( $users as $username => $count ) {
00316             $title = Title::makeTitleSafe( NS_USER, $username );
00317             $enc = htmlspecialchars( $username );
00318 
00319             $attribs = array();
00320             $styles = array();
00321             if ( isset( $editcounts[$username] ) ) {
00322                 if ( $count === -1 ) {
00323                     $count = $editcounts[$username];
00324                 }
00325 
00326                 $styles['font-size'] = round( log( $count, 10 ) * 30 ) + 70 . '%';
00327 
00328                 $last = wfTimestamp( TS_UNIX ) - $lastedits[$username];
00329                 $last = round( $last / $day );
00330                 $attribs['title'] = $this->msg( 'supportedlanguages-activity', $username )
00331                     ->numParams( $count, $last )->text();
00332                 $last = max( 1, min( $period, $last ) );
00333                 $styles['border-bottom'] = '3px solid #' .
00334                     $statsTable->getBackgroundColor( $period - $last, $period );
00335             } else {
00336                 $enc = "<del>$enc</del>";
00337             }
00338 
00339             $stylestr = $this->formatStyle( $styles );
00340             if ( $stylestr ) {
00341                 $attribs['style'] = $stylestr;
00342             }
00343 
00344             $links[] = Linker::link( $title, $enc, $attribs );
00345         }
00346 
00347         $linkList = $this->getLanguage()->listToText( $links );
00348         $html = "<p class='mw-translate-spsl-translators'>";
00349         $html .= $this->msg(
00350             'supportedlanguages-translators',
00351             $linkList,
00352             count( $links )
00353         )->text();
00354         $html .= "</p>\n";
00355         $this->getOutput()->addHTML( $html );
00356     }
00357 
00358     protected function getUserStats() {
00359         $cache = wfGetCache( CACHE_ANYTHING );
00360         $cachekey = wfMemcKey( 'translate-supportedlanguages-userstats' );
00361         $data = $cache->get( $cachekey );
00362         if ( !$this->purge && is_array( $data ) ) {
00363             return $data;
00364         }
00365 
00366         $dbr = wfGetDB( DB_SLAVE );
00367         $editcounts = $lastedits = array();
00368         $tables = array( 'user', 'revision' );
00369         $fields = array( 'user_name', 'user_editcount', 'MAX(rev_timestamp) as lastedit' );
00370         $conds = array( 'user_id = rev_user' );
00371         $options = array( 'GROUP BY' => 'user_name' );
00372 
00373         $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $options );
00374         foreach ( $res as $row ) {
00375             $editcounts[$row->user_name] = $row->user_editcount;
00376             $lastedits[$row->user_name] = wfTimestamp( TS_UNIX, $row->lastedit );
00377         }
00378 
00379         $data = array( $editcounts, $lastedits );
00380         $cache->set( $cachekey, $data, 3600 );
00381 
00382         return $data;
00383     }
00384 
00385     protected function formatStyle( $styles ) {
00386         $stylestr = '';
00387         foreach ( $styles as $key => $value ) {
00388             $stylestr .= "$key:$value;";
00389         }
00390 
00391         return $stylestr;
00392     }
00393 
00394     function shuffle_assoc( $list ) {
00395         if ( !is_array( $list ) ) {
00396             return $list;
00397         }
00398 
00399         $keys = array_keys( $list );
00400         shuffle( $keys );
00401         $random = array();
00402         foreach ( $keys as $key )
00403             $random[$key] = $list[$key];
00404 
00405         return $random;
00406     }
00407 
00408     protected function preQueryUsers( $users ) {
00409         $lb = new LinkBatch;
00410         foreach ( $users as $translators ) {
00411             foreach ( $translators as $user => $count ) {
00412                 $user = Title::capitalize( $user, NS_USER );
00413                 $lb->add( NS_USER, $user );
00414                 $lb->add( NS_USER_TALK, $user );
00415             }
00416         }
00417         $lb->execute();
00418     }
00419 
00420     protected function getColorLegend() {
00421         $legend = '';
00422         $period = $this->period;
00423         $statsTable = new StatsTable();
00424 
00425         for ( $i = 0; $i <= $period; $i += 30 ) {
00426             $iFormatted = htmlspecialchars( $this->getLanguage()->formatNum( $i ) );
00427             $legend .= '<span style="background-color:#' .
00428                 $statsTable->getBackgroundColor( $period - $i, $period ) .
00429                 "\"> $iFormatted</span>";
00430         }
00431 
00432         return $legend;
00433     }
00434 }
Generated on Tue Oct 29 00:00:24 2013 for MediaWiki Translate Extension by  doxygen 1.6.3