<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[3002] sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes: Translate: Refactor the Custom Routes plugin to use an autoloader.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://meta.trac.wordpress.org/changeset/3002">3002</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://meta.trac.wordpress.org/changeset/3002","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>ocean90</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2016-04-25 15:38:34 +0000 (Mon, 25 Apr 2016)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Translate: Refactor the Custom Routes plugin to use an autoloader.

See <a href="http://meta.trac.wordpress.org/ticket/1682">#1682</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggprouteswporggproutesphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/wporg-gp-routes.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincclasspluginphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/class-plugin.php</a></li>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/cli/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesinccliclassupdatecachesphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/cli/class-update-caches.php</a></li>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclassindexphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-index.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclasslocalephp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-locale.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclassredirectorphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-redirector.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclassstatsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-stats.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclasswpdirectoryphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-directory.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclasswppluginsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-plugins.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclasswpthemesphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-themes.php</a></li>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/vendor/</li>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/vendor/wordpressdotorg/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesvendorwordpressdotorgclassautoloaderphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/vendor/wordpressdotorg/class-autoloader.php</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/cli/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesinccliupdatecachesphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/cli/update-caches.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesindexphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/index.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincrouteslocalephp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/locale.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesredirectorphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/redirector.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesstatsoverviewphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/stats-overview.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincrouteswpdirectoryphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/wp-directory.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincrouteswppluginsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/wp-plugins.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincrouteswpthemesphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/wp-themes.php</a></li>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/</li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincclasspluginphpfromrev2999sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggprouteswporggproutesphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/class-plugin.php (from rev 2999, sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/wporg-gp-routes.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/class-plugin.php                             (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/class-plugin.php       2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,126 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace WordPressdotorg\GlotPress\Routes;
+
+use GP;
+use GP_Locales;
+use WP_CLI;
+
+class Plugin {
+
+       /**
+        * @var Plugin The singleton instance.
+        */
+       private static $instance;
+
+       /**
+        *
+        * @var Sync\Translation_Sync
+        */
+       public $translation_sync = null;
+
+       /**
+        * Returns always the same instance of this plugin.
+        *
+        * @return Plugin
+        */
+       public static function get_instance() {
+               if ( ! ( self::$instance instanceof Plugin ) ) {
+                       self::$instance = new Plugin();
+               }
+               return self::$instance;
+       }
+
+       /**
+        * Instantiates a new Plugin object.
+        */
+       private function __construct() {
+               add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) );
+       }
+
+       /**
+        * Initializes the plugin.
+        */
+       public function plugins_loaded() {
+               if ( file_exists( WPORGPATH . 'extend/plugins-plugins/_plugin-icons.php' ) ) {
+                       include_once WPORGPATH . 'extend/plugins-plugins/_plugin-icons.php';
+               }
+
+               add_action( 'template_redirect', array( $this, 'register_routes' ), 5 );
+
+               if ( defined( 'WP_CLI' ) && WP_CLI ) {
+                       $this->register_cli_commands();
+               }
+       }
+
+       /**
+        * Registers custom routes and removes default routes.
+        *
+        * Removes:
+        *  - API: /languages/$locale
+        *  - /languages/$locale
+        *  - /languages/$locale
+        *  - /languages/$locale/$path
+        *  - /profile/$path
+        *  - /projects/wp-plugins/?
+        *  - /projects/wp-themes/?
+        *
+        * Adds:
+        *  - /
+        *  - /locale/$locale
+        *  - /locale/$locale/$path
+        *  - /locale/$locale/$path/$path
+        *  - /locale/$locale/$path/$path/$path
+        *  - /stats/?
+        *  - /projects/wp-plugins/$project
+        *  - /projects/wp-plugins/$project/contributors
+        *  - /projects/wp-plugins/$project/language-packs
+        *  - /projects/wp-themes/$project
+        *  - /projects/wp-themes/$project/contributors
+        *  - /projects/wp-themes/$project/language-packs
+        */
+       public function register_routes() {
+               $request_uri = GP::$router->request_uri();
+               $path = '(.+?)';
+               $locale = '(' . implode( '|', array_map( function( $locale ) { return $locale->slug; }, GP_Locales::locales() ) ) . ')';
+
+               if ( gp_startswith( $request_uri, '/' . GP::$router->api_prefix . '/' ) ) { // API requests.
+                       // Delete default routes.
+                       GP::$router->remove( "/languages/$locale" );
+               } else {
+                       // Delete default routes.
+                       GP::$router->remove( "/languages/$locale" );
+                       GP::$router->remove( "/languages/$locale/$path" );
+                       GP::$router->remove( '/profile' );
+                       GP::$router->remove( "/profile/$path" );
+
+                       // Redirect routes.
+                       GP::$router->prepend( '/languages', array( __NAMESPACE__ . '\Routes\Redirector', 'redirect_languages' ) );
+                       GP::$router->prepend( "/languages/$path", array( __NAMESPACE__ . '\Routes\Redirector', 'redirect_languages' ) );
+                       GP::$router->prepend( '/projects/wp-plugins/?', array( __NAMESPACE__ . '\Routes\Redirector', 'redirect_index' ) );
+                       GP::$router->prepend( '/projects/wp-themes/?', array( __NAMESPACE__ . '\Routes\Redirector', 'redirect_index' ) );
+
+                       // Register custom routes.
+                       GP::$router->prepend( '/', array( __NAMESPACE__ . '\Routes\Index', 'get_locales' ) );
+                       GP::$router->prepend( "/locale/$locale", array( __NAMESPACE__ . '\Routes\Locale', 'get_locale_projects' ) );
+                       GP::$router->prepend( "/locale/$locale/$path", array( __NAMESPACE__ . '\Routes\Locale', 'get_locale_projects' ) );
+                       GP::$router->prepend( "/locale/$locale/$path/$path", array( __NAMESPACE__ . '\Routes\Locale', 'get_locale_projects' ) );
+                       GP::$router->prepend( "/locale/$locale/$path/$path/$path", array( __NAMESPACE__ . '\Routes\Locale', 'get_locale_project' ) );
+                       GP::$router->prepend( '/stats/?', array( __NAMESPACE__ . '\Routes\Stats', 'get_stats_overview' ) );
+                       $project = '([^/]*)/?';
+                       GP::$router->prepend( "/projects/wp-plugins/$project", array( __NAMESPACE__ . '\Routes\WP_Plugins', 'get_plugin_projects' ) );
+                       GP::$router->prepend( "/projects/wp-plugins/$project/contributors", array( __NAMESPACE__ . '\Routes\WP_Plugins', 'get_plugin_contributors' ) );
+                       GP::$router->prepend( "/projects/wp-plugins/$project/language-packs", array( __NAMESPACE__ . '\Routes\WP_Plugins', 'get_plugin_language_packs' ) );
+                       GP::$router->prepend( "/projects/wp-themes/$project", array( __NAMESPACE__ . '\Routes\WP_Themes', 'get_theme_projects' ) );
+                       GP::$router->prepend( "/projects/wp-themes/$project/contributors", array( __NAMESPACE__ . '\Routes\WP_Themes', 'get_theme_contributors' ) );
+                       GP::$router->prepend( "/projects/wp-themes/$project/language-packs", array( __NAMESPACE__ . '\Routes\WP_Themes', 'get_theme_language_packs' ) );
+               }
+       }
+
+       /**
+        * Registers CLI commands if WP-CLI is loaded.
+        */
+       function register_cli_commands() {
+               WP_CLI::add_command( 'wporg-translate update-cache', __NAMESPACE__ . '\CLI\Update_Caches' );
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesinccliclassupdatecachesphpfromrev2999sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutescliupdatecachesphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/cli/class-update-caches.php (from rev 2999, sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/cli/update-caches.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/cli/class-update-caches.php                          (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/cli/class-update-caches.php    2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,89 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace WordPressdotorg\GlotPress\Routes\CLI;
+
+use GP;
+use WP_CLI;
+use WP_CLI_Command;
+
+class Update_Caches extends WP_CLI_Command {
+
+       private $cache_group = 'wporg-translate';
+
+       /**
+        * Update all caches.
+        */
+       public function all() {
+               $this->contributors_count();
+               $this->translation_status();
+               $this->existing_locales();
+       }
+
+       /**
+        * Update contributors count per locale.
+        *
+        * @subcommand contributors-count
+        */
+       public function contributors_count() {
+               global $wpdb;
+
+               if ( ! isset( $wpdb->user_translations_count ) ) {
+                       WP_CLI::error( 'The stats plugin seems not to be activated.' );
+                       return;
+               }
+
+               $locales = GP::$translation_set->existing_locales();
+               $db_counts = $wpdb->get_results(
+                       "SELECT `locale`, COUNT( DISTINCT user_id ) as `count` FROM {$wpdb->user_translations_count} WHERE `accepted` > 0 GROUP BY `locale`",
+                       OBJECT_K
+               );
+
+               $counts = array();
+               foreach ( $locales as $locale ) {
+                       if ( isset( $db_counts[ $locale ] ) ) {
+                               $counts[ $locale ] = (int) $db_counts[ $locale ]->count;
+                       } else {
+                               $counts[ $locale ] = 0;
+                       }
+               }
+
+               wp_cache_set( 'contributors-count', $counts, $this->cache_group );
+               WP_CLI::success( 'Contributors count was updated.' );
+       }
+
+       /**
+        * Calculate the translation status of the WordPress project per locale.
+        *
+        * @subcommand translation-status
+        */
+       public function translation_status() {
+               global $wpdb;
+
+               if ( ! isset( $wpdb->project_translation_status ) ) {
+                       WP_CLI::error( 'The stats plugin seems not to be activated.' );
+                       return;
+               }
+
+               $translation_status = $wpdb->get_results( $wpdb->prepare(
+                       "SELECT `locale`, `all` AS `all_count`, `waiting` AS `waiting_count`, `current` AS `current_count`, `fuzzy` AS `fuzzy_count`
+                       FROM {$wpdb->project_translation_status}
+                       WHERE `project_id` = %d AND `locale_slug` = %s",
+                       2, 'default' // 2 = wp/dev
+               ), OBJECT_K );
+
+               wp_cache_set( 'translation-status', $translation_status, $this->cache_group );
+               WP_CLI::success( 'Translation status was updated.' );
+       }
+
+       /**
+        * Update cache for existing locales.
+        *
+        * @subcommand existing-locales
+        */
+       public function existing_locales() {
+               $existing_locales = GP::$translation_set->existing_locales();
+
+               wp_cache_set( 'existing-locales', $existing_locales, $this->cache_group );
+               WP_CLI::success( 'Existing locales were updated.' );
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesinccliupdatecachesphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/cli/update-caches.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/cli/update-caches.php    2016-04-22 07:25:24 UTC (rev 2999)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/cli/update-caches.php  2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,83 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-class WPorg_GP_CLI_Update_Caches extends WP_CLI_Command {
-
-       private $cache_group = 'wporg-translate';
-
-       /**
-        * Update all caches.
-        */
-       public function all() {
-               $this->contributors_count();
-               $this->translation_status();
-               $this->existing_locales();
-       }
-
-       /**
-        * Update contributors count per locale.
-        *
-        * @subcommand contributors-count
-        */
-       public function contributors_count() {
-               global $wpdb;
-
-               if ( ! isset( $wpdb->user_translations_count ) ) {
-                       WP_CLI::error( 'The stats plugin seems not to be activated.' );
-                       return;
-               }
-
-               $locales = GP::$translation_set->existing_locales();
-               $db_counts = $wpdb->get_results(
-                       "SELECT `locale`, COUNT( DISTINCT user_id ) as `count` FROM {$wpdb->user_translations_count} WHERE `accepted` > 0 GROUP BY `locale`",
-                       OBJECT_K
-               );
-
-               $counts = array();
-               foreach ( $locales as $locale ) {
-                       if ( isset( $db_counts[ $locale ] ) ) {
-                               $counts[ $locale ] = (int) $db_counts[ $locale ]->count;
-                       } else {
-                               $counts[ $locale ] = 0;
-                       }
-               }
-
-               wp_cache_set( 'contributors-count', $counts, $this->cache_group );
-               WP_CLI::success( 'Contributors count was updated.' );
-       }
-
-       /**
-        * Calculate the translation status of the WordPress project per locale.
-        *
-        * @subcommand translation-status
-        */
-       public function translation_status() {
-               global $wpdb;
-
-               if ( ! isset( $wpdb->project_translation_status ) ) {
-                       WP_CLI::error( 'The stats plugin seems not to be activated.' );
-                       return;
-               }
-
-               $translation_status = $wpdb->get_results( $wpdb->prepare(
-                       "SELECT `locale`, `all` AS `all_count`, `waiting` AS `waiting_count`, `current` AS `current_count`, `fuzzy` AS `fuzzy_count`
-                       FROM {$wpdb->project_translation_status}
-                       WHERE `project_id` = %d AND `locale_slug` = %s",
-                       2, 'default' // 2 = wp/dev
-               ), OBJECT_K );
-
-               wp_cache_set( 'translation-status', $translation_status, $this->cache_group );
-               WP_CLI::success( 'Translation status was updated.' );
-       }
-
-       /**
-        * Update cache for existing locales.
-        *
-        * @subcommand existing-locales
-        */
-       public function existing_locales() {
-               $existing_locales = GP::$translation_set->existing_locales();
-
-               wp_cache_set( 'existing-locales', $existing_locales, $this->cache_group );
-               WP_CLI::success( 'Existing locales were updated.' );
-       }
-}
</del></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclassindexphpfromrev2999sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesroutesindexphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-index.php (from rev 2999, sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/index.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-index.php                               (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-index.php 2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,51 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace WordPressdotorg\GlotPress\Routes\Routes;
+
+use GP_Locales;
+use GP_Route;
+
+/**
+ * Index Route Class.
+ *
+ * Provides the route for translate.wordpress.org/.
+ */
+class Index extends GP_Route {
+
+       private $cache_group = 'wporg-translate';
+
+       /**
+        * Prints all exisiting locales as cards.
+        *
+        * Note: Cache gets refreshed via `WPorg_GP_CLI_Update_Caches`.
+        */
+       public function get_locales() {
+               $existing_locales = wp_cache_get( 'existing-locales', $this->cache_group );
+               if ( false === $existing_locales ) {
+                       $existing_locales = array();
+               }
+
+               $locales = array();
+               foreach ( $existing_locales as $locale ) {
+                       $locales[] = GP_Locales::by_slug( $locale );
+               }
+               usort( $locales, array( $this, '_sort_english_name_callback' ) );
+               unset( $existing_locales );
+
+               $contributors_count = wp_cache_get( 'contributors-count', $this->cache_group );
+               if ( false === $contributors_count ) {
+                       $contributors_count = array();
+               }
+
+               $translation_status = wp_cache_get( 'translation-status', $this->cache_group );
+               if ( false === $translation_status ) {
+                       $translation_status = array();
+               }
+
+               $this->tmpl( 'index-locales', get_defined_vars() );
+       }
+
+       private function _sort_english_name_callback( $a, $b ) {
+               return $a->english_name > $b->english_name;
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclasslocalephpfromrev2999sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesrouteslocalephp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-locale.php (from rev 2999, sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/locale.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-locale.php                              (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-locale.php        2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,787 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace WordPressdotorg\GlotPress\Routes\Routes;
+
+// wporg_get_plugin_icon()
+use GP;
+use GP_Locales;
+use GP_Route;
+use stdClass;
+
+/**
+ * Locale Route Class.
+ *
+ * Provides the route for translate.wordpress.org/locale/$locale.
+ */
+class Locale extends GP_Route {
+
+       /**
+        * Prints projects/translation sets of a top level project.
+        *
+        * @param string      $locale_slug  Slug of the locale.
+        * @param string      $set_slug     Slug of the translation set.
+        * @param bool|string $project_path Path of a project.
+        */
+       public function get_locale_projects( $locale_slug, $set_slug = 'default', $project_path = false ) {
+               global $wpdb;
+
+               $per_page = 20;
+               $page = (int) gp_get( 'page', 1 );
+               $search = gp_get( 's', '' );
+               $filter = gp_get( 'filter', false );
+
+               $locale = GP_Locales::by_slug( $locale_slug );
+               if ( ! $locale ) {
+                       return $this->die_with_404();
+               }
+
+               // Grab the top level projects to show in the menu first, so as to be able to handle the default Waiting / WP tab selection
+               $top_level_projects = $this->get_active_top_level_projects();
+               usort( $top_level_projects, array( $this, '_sort_reverse_name_callback' ) );
+
+               // Default to the Waiting or WordPress tabs
+               $default_project_tab = 'waiting';
+               $user_id = get_current_user_id();
+               if (
+                       ! is_user_logged_in() ||
+                       ! function_exists( 'wporg_gp_rosetta_roles' ) || // Rosetta Roles plugin is not enabled
+                       ! (
+                               wporg_gp_rosetta_roles()->is_global_administrator( $user_id ) || // Not a global admin
+                               wporg_gp_rosetta_roles()->is_approver_for_locale( $user_id, $locale_slug ) // Doesn't have project-level access either
+                       )
+                       // Add check to see if there are any waiting translations for this locale?
+                       ) {
+                       $default_project_tab = 'wp';
+               }
+
+               // Filter out the Waiting Tab if the current user cannot validate strings
+               if ( 'waiting' != $default_project_tab ) {
+                       foreach ( $top_level_projects as $i => $project ) {
+                               if ( 'waiting' == $project->slug ) {
+                                       unset( $top_level_projects[ $i ] );
+                                       break;
+                               }
+                       }
+               }
+
+               $project_path = $project_path ?: $default_project_tab;
+
+               $project = GP::$project->by_path( $project_path );
+               if ( ! $project ) {
+                       return $this->die_with_404();
+               }
+
+
+               $paged_sub_projects = $this->get_paged_active_sub_projects(
+                       $project,
+                       array(
+                               'page' => $page,
+                               'per_page' => $per_page,
+                               'search' => $search,
+                               'filter' => $filter,
+                               'set_slug' => $set_slug,
+                               'locale' => $locale_slug,
+                       )
+               );
+
+               if ( ! $paged_sub_projects ) {
+                       return $this->die_with_404();
+               }
+
+               $sub_projects = $paged_sub_projects['projects'];
+               $pages        = $paged_sub_projects['pages'];
+               $filter       = $paged_sub_projects['filter'];
+               unset( $paged_sub_projects );
+
+               $project_status = $project_icons = array();
+               foreach ( $sub_projects as $key => $sub_project ) {
+                       $project_status[ $sub_project->id ] = $this->get_project_status( $sub_project, $locale_slug, $set_slug );
+                       $project_icons[ $sub_project->id ] = $this->get_project_icon( $project, $sub_project );
+               }
+
+               $project_ids = array_keys( $project_status );
+               $project_ids[] = $project->id;
+               $project_ids = array_merge(
+                       $project_ids,
+                       $wpdb->get_col( "SELECT id FROM {$wpdb->gp_projects} WHERE parent_project_id IN( " . implode( ', ', $project_ids ) . ')' )
+               );
+
+               $contributors_count = wp_cache_get( 'contributors-count', 'wporg-translate' );
+               if ( false === $contributors_count ) {
+                       $contributors_count = array();
+               }
+
+               $variants = $this->get_locale_variants( $locale_slug, $project_ids );
+               // If there were no results for the current variant in the current project branch, it should still show it.
+               if ( ! in_array( $set_slug, $variants, true ) ) {
+                       $variants[] = $set_slug;
+               }
+
+               $this->tmpl( 'locale-projects', get_defined_vars() );
+       }
+
+       /**
+        * Prints projects/translation sets of a sub project.
+        *
+        * @param string $locale_slug      Slug of the locale.
+        * @param string $set_slug         Slug of the translation set.
+        * @param string $project_path     Path of a project.
+        * @param string $sub_project_path Path of a sub project.
+        */
+       public function get_locale_project( $locale_slug, $set_slug, $project_path, $sub_project_path ) {
+               $locale = GP_Locales::by_slug( $locale_slug );
+               if ( ! $locale ) {
+                       return $this->die_with_404();
+               }
+
+               $project = GP::$project->by_path( $project_path );
+               if ( ! $project ) {
+                       return $this->die_with_404();
+               }
+
+               $sub_project = GP::$project->by_path( $project_path . '/' . $sub_project_path );
+               if ( ! $sub_project ) {
+                       return $this->die_with_404();
+               }
+
+               $project_status = $this->get_project_status( $sub_project, $locale_slug, $set_slug );
+               $sub_project_status = $this->get_project_status( $sub_project, $locale_slug, $set_slug, null, false );
+
+               $project_icon = $this->get_project_icon( $project, $sub_project, 64 );
+
+               $contributors_count = wp_cache_get( 'contributors-count', 'wporg-translate' );
+               if ( false === $contributors_count ) {
+                       $contributors_count = array();
+               }
+
+               $sub_projects = $this->get_active_sub_projects( $sub_project, true );
+               $sub_project_slugs = array();
+               if ( $sub_projects ) {
+                       $sub_project_statuses = array();
+                       foreach ( $sub_projects as $key => $_sub_project ) {
+                               $sub_project_slugs[] = $_sub_project->slug;
+                               $status = $this->get_project_status( $_sub_project, $locale_slug, $set_slug, null, false );
+
+                               $sub_project_statuses[ $_sub_project->id ] = $status;
+                       }
+
+                       $variants = $this->get_locale_variants( $locale_slug, array_keys( $sub_project_statuses ) );
+               } else {
+                       $variants = $this->get_locale_variants( $locale_slug, array( $sub_project->id ) );
+               }
+
+               $locale_contributors = $this->get_locale_contributors( $sub_project, $locale_slug, $set_slug );
+
+               $this->tmpl( 'locale-project', get_defined_vars() );
+       }
+
+       /**
+        * Returns markup for project icons.
+        *
+        * @param GP_Project $project     A GlotPress project.
+        * @param GP_Project $sub_project A sub project of a GlotPress project.
+        * @param int        $size        Size of icon.
+        * @return string HTML markup of an icon.
+        */
+       private function get_project_icon( $project, $sub_project, $size = 128 ) {
+               // The Waiting tab will have $sub_project's which are not sub-projects of $project
+               if ( $sub_project->parent_project_id && $sub_project->parent_project_id !== $project->id ) {
+                       $project = GP::$project->get( $sub_project->parent_project_id );
+                       // In the case of Plugins, we may need to go up another level yet
+                       if ( $project->parent_project_id ) {
+                               $sub_project = $project;
+                               $project = GP::$project->get( $sub_project->parent_project_id );
+                       }
+               }
+
+               switch ( $project->slug ) {
+                       case 'wp':
+                               return '<div class="wordpress-icon"><span class="dashicons dashicons-wordpress-alt"></span></div>';
+                       case 'meta':
+                               switch ( $sub_project->slug ) {
+                                       case 'forums':
+                                               return '<div class="default-icon"><span class="dashicons dashicons-format-chat"></span></div>';
+                                       case 'rosetta':
+                                               return '<div class="default-icon"><span class="dashicons dashicons-admin-site"></span></div>';
+                                       case 'plugins':
+                                               return '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
+                                       case 'themes':
+                                               return '<div class="default-icon"><span class="dashicons dashicons-admin-appearance"></span></div>';
+                                       case 'wordcamp':
+                                               return '<div class="default-icon"><span class="dashicons dashicons-tickets"></span></div>';
+                                       case 'browsehappy':
+                                               return '<div class="icon"><img src="https://translate.wordpress.org/translate/gp-templates-new/images/browsehappy.png" width="' . $size . '" height="' . $size . '"></div>';
+                                       default:
+                                               return '<div class="default-icon"><span class="dashicons dashicons-networking"></span></div>';
+                               }
+                       case 'wp-themes':
+                               $screenshot = gp_get_meta( 'wp-themes', $sub_project->id, 'screenshot' );
+                               if ( $screenshot ) {
+                                       return '<div class="theme icon"><img src="https://i0.wp.com/' . $screenshot . '?w=' . ( $size * 2 ) . '&amp;strip=all" width="' . $size . '" height="' . $size . '"></div>';
+                               } else {
+                                       return '<div class="default-icon"><span class="dashicons dashicons-admin-appearance"></span></div>';
+                               }
+                       case 'bbpress':
+                       case 'buddypress':
+                               if ( function_exists( 'wporg_get_plugin_icon' ) ) {
+                                       $screenshot = wporg_get_plugin_icon( $project->slug, $size );
+                                       if ( $screenshot ) {
+                                               return $screenshot;
+                                       }
+                               }
+                               return '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
+                       case 'wp-plugins':
+                               if ( function_exists( 'wporg_get_plugin_icon' ) ) {
+                                       $screenshot = wporg_get_plugin_icon( $sub_project->slug, $size );
+                                       if ( $screenshot ) {
+                                               return $screenshot;
+                                       }
+                               }
+                               return '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
+                       case 'glotpress':
+                               return '<div class="icon"><img src="https://translate.wordpress.org/translate/gp-templates-new/images/glotpress.png" width="' . $size . '" height="' . $size . '"></div>';
+                       default:
+                               return '<div class="default-icon"><span class="dashicons dashicons-translation"></span></div>';
+               }
+       }
+
+       /**
+        * Retrieves non-default slugs of translation sets for a list of
+        * project IDs.
+        *
+        * @param string $locale     Slug of a GlotPress locale.
+        * @param array $project_ids List of project IDs.
+        * @return array List of non-default slugs.
+        */
+       private function get_locale_variants( $locale, $project_ids ) {
+               global $wpdb;
+
+               $project_ids = implode( ',', $project_ids );
+               $slugs = $wpdb->get_col( $wpdb->prepare( "
+                       SELECT DISTINCT slug
+                       FROM {$wpdb->gp_translation_sets}
+                       WHERE
+                               project_id IN( $project_ids )
+                               AND locale = %s
+               ", $locale ) );
+
+               return $slugs;
+       }
+
+       /**
+        * Retrieves contributors of a project.
+        *
+        * @param GP_Project $project     A GlotPress project.
+        * @param string     $locale_slug Slug of the locale.
+        * @param string     $set_slug    Slug of the translation set.
+        * @return array Contributors.
+        */
+       private function get_locale_contributors( $project, $locale_slug, $set_slug ) {
+               global $wpdb;
+
+               $locale_contributors = array(
+                       'editors'      => array(),
+                       'contributors' => array(),
+               );
+
+               // Get the translation editors of the project.
+               $editors = $wpdb->get_col( $wpdb->prepare( "
+                       SELECT
+                               `user_id`
+                       FROM {$wpdb->wporg_translation_editors}
+                       WHERE
+                               `project_id` = %d
+                               AND `locale` = %s
+               ", $project->id, $locale_slug ) );
+
+               // Get the names of the translation editors.
+               foreach ( $editors as $editor_id ) {
+                       $user = get_user_by( 'id', $editor_id );
+                       if ( ! $user ) {
+                               continue;
+                       }
+
+                       $locale_contributors['editors'][ $editor_id ] = (object) array(
+                               'nicename'     => $user->user_nicename,
+                               'display_name' => $this->_encode( $user->display_name ),
+                               'email'        => $user->user_email,
+                       );
+               }
+               unset( $editors );
+
+               // Get the contributors of the project.
+               $contributors = array();
+
+               // In case the project has a translation set, like /wp-themes/twentysixteen.
+               $translation_set = GP::$translation_set->by_project_id_slug_and_locale( $project->id, $set_slug, $locale_slug );
+               if ( $translation_set ) {
+                       $contributors = array_merge(
+                               $contributors,
+                               $this->get_locale_contributors_by_translation_set( $translation_set )
+                       );
+               }
+
+               // Check if the project has sub-projects, like /wp-plugins/wordpress-importer.
+               $sub_projects = $wpdb->get_col( $wpdb->prepare( "
+                       SELECT id
+                       FROM {$wpdb->gp_projects}
+                       WHERE
+                               parent_project_id = %d
+                               AND active = 1
+               ", $project->id ) );
+
+               foreach ( $sub_projects as $sub_project ) {
+                       $translation_set = GP::$translation_set->by_project_id_slug_and_locale( $sub_project, $set_slug, $locale_slug );
+                       if ( ! $translation_set ) {
+                               continue;
+                       }
+
+                       $contributors = array_merge(
+                               $contributors,
+                               $this->get_locale_contributors_by_translation_set( $translation_set )
+                       );
+               }
+
+               // Get the names of the contributors.
+               foreach ( $contributors as $contributor ) {
+                       if ( isset( $locale_contributors['editors'][ $contributor->user_id ] ) ) {
+                               continue;
+                       }
+
+
+                       if ( isset( $locale_contributors['contributors'][ $contributor->user_id ] ) ) {
+                               // Update last updated and counts per status.
+                               $locale_contributors['contributors'][ $contributor->user_id ]->last_update = max(
+                                       $locale_contributors['contributors'][ $contributor->user_id ]->last_update,
+                                       $contributor->last_update
+                               );
+
+                               $locale_contributors['contributors'][ $contributor->user_id ]->total_count   += $contributor->total_count;
+                               $locale_contributors['contributors'][ $contributor->user_id ]->current_count += $contributor->current_count;
+                               $locale_contributors['contributors'][ $contributor->user_id ]->waiting_count += $contributor->waiting_count;
+                               $locale_contributors['contributors'][ $contributor->user_id ]->fuzzy_count   += $contributor->fuzzy_count;
+                               continue;
+                       }
+
+                       $user = get_user_by( 'id', $contributor->user_id );
+                       if ( ! $user ) {
+                               continue;
+                       }
+
+                       $locale_contributors['contributors'][ $contributor->user_id ] = (object) array(
+                               'nicename'      => $user->user_nicename,
+                               'display_name'  => $this->_encode( $user->display_name ),
+                               'email'         => $user->user_email,
+                               'last_update'   => $contributor->last_update,
+                               'total_count'   => $contributor->total_count,
+                               'current_count' => $contributor->current_count,
+                               'waiting_count' => $contributor->waiting_count,
+                               'fuzzy_count'   => $contributor->fuzzy_count,
+                       );
+               }
+               unset( $contributors );
+
+               uasort( $locale_contributors['contributors'], array( $this, '_sort_contributors_by_total_count_callback' ) );
+
+               return $locale_contributors;
+       }
+
+       /**
+        * Retrieves contributors of a translation set.
+        *
+        * @param GP_Translation_Set $translation_set A translation set.
+        * @return array List of user IDs.
+        */
+       private function get_locale_contributors_by_translation_set( $translation_set ) {
+               global $wpdb;
+
+               $contributors = $wpdb->get_results( $wpdb->prepare( "
+                       SELECT
+                               `user_id`,
+                               MAX( `date_added` ) AS `last_update`,
+                               COUNT( * ) as `total_count`,
+                               COUNT( CASE WHEN `status` = 'current' THEN `status` END ) AS `current_count`,
+                               COUNT( CASE WHEN `status` = 'waiting' THEN `status` END ) AS `waiting_count`,
+                               COUNT( CASE WHEN `status` = 'fuzzy' THEN `status` END ) AS `fuzzy_count`
+                       FROM `{$wpdb->gp_translations}`
+                       WHERE
+                               `translation_set_id` = %d
+                               AND `user_id` IS NOT NULL AND `user_id` != 0
+                               AND `status` IN( 'current', 'waiting', 'fuzzy' )
+                               AND `date_modified` > %s
+                       GROUP BY `user_id`
+               ", $translation_set->id, date( 'Y-m-d', time() - YEAR_IN_SECONDS ) ) );
+
+               return $contributors;
+       }
+
+       /**
+        * Calculates the status of a project.
+        *
+        * @param GP_Project $project           The GlotPress project.
+        * @param string     $locale            Slug of GlotPress locale.
+        * @param string     $set_slug          Slug of the translation set.
+        * @param object     $status            The status object.
+        * @param bool       $calc_sub_projects Whether sub projects should be calculated too.
+        *                                      Default true.
+        * @return object The status of a project.
+        */
+       private function get_project_status( $project, $locale, $set_slug, $status = null, $calc_sub_projects = true ) {
+               if ( null === $status ) {
+                       $status = new stdClass;
+                       $status->sub_projects_count = 0;
+                       $status->waiting_count      = 0;
+                       $status->current_count      = 0;
+                       $status->fuzzy_count        = 0;
+                       $status->all_count          = 0;
+                       $status->percent_complete   = 0;
+               }
+
+               $set = GP::$translation_set->by_project_id_slug_and_locale(
+                       $project->id,
+                       $set_slug,
+                       $locale
+               );
+
+               if ( $set ) {
+                       $status->sub_projects_count += 1;
+                       $status->waiting_count      += (int) $set->waiting_count();
+                       $status->current_count      += (int) $set->current_count();
+                       $status->fuzzy_count        += (int) $set->fuzzy_count();
+                       $status->all_count          += (int) $set->all_count();
+
+                       if ( $status->all_count ) {
+                               /*
+                                * > 50% round down, so that a project with all strings except 1 translated shows 99%, instead of 100%.
+                                * < 50% round up, so that a project with just a few strings shows 1%, instead of 0%.
+                                */
+                               $percent_complete = ( $status->current_count / $status->all_count * 100 );
+                               $status->percent_complete = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
+                       }
+               }
+
+               if ( $calc_sub_projects ) {
+                       $sub_projects = $this->get_active_sub_projects( $project, true );
+                       if ( $sub_projects ) {
+                               foreach ( $sub_projects as $sub_project ) {
+                                       $this->get_project_status( $sub_project, $locale, $set_slug, $status, false );
+                               }
+                       }
+               }
+
+               return $status;
+       }
+
+       /**
+        * Retrieves active sub projects.
+        *
+        * @param  GP_Project $project           The parent project
+        * @param  bool       $with_sub_projects Whether sub projects should be fetched too.
+        *                                       Default false.
+        * @return array List of sub projects.
+        */
+       private function get_active_sub_projects( $project, $with_sub_projects = false ) {
+               global $wpdb;
+
+               $_projects = $project->many( "
+                       SELECT *
+                       FROM {$wpdb->gp_projects}
+                       WHERE
+                               parent_project_id = %d
+                               AND active = 1
+                       ORDER BY id ASC
+               ", $project->id );
+
+               $projects = array();
+               foreach ( $_projects as $project ) {
+                       $projects[ $project->id ] = $project;
+
+                       if ( $with_sub_projects ) {
+                               // e.g. wp/dev/admin/network
+                               $sub_projects = $project->many( "
+                                       SELECT *
+                                       FROM {$wpdb->gp_projects}
+                                       WHERE
+                                               parent_project_id = %d
+                                               AND active = 1
+                                       ORDER BY id ASC
+                               ", $project->id );
+
+                               foreach ( $sub_projects as $sub_project ) {
+                                       $projects[ $sub_project->id ] = $sub_project;
+                               }
+                               unset( $sub_projects);
+                       }
+               }
+               unset( $_projects );
+
+               return $projects;
+       }
+
+       /**
+        * Retrieves active sub projects with paging.
+        *
+        * This method is horribly inefficient when there exists many sub-projects, as it can't use SQL.
+        *
+        * @param GP_Project $project           The parent project
+        * @param array $args {
+        *      @type int    $per_page Number of items per page. Default 20
+        *      @type int    $page     The page of results to view. Default 1.
+        *      @type string $orderby  The field to order by, id or name. Default id.
+        *      @type string $order    The sorting order, ASC or DESC. Default ASC.
+        *      @type string $search   The search string
+        *      @type string $set_slug The translation set to view.
+        *      @type string $locale   The locale of the translation set to view.
+        * }
+        * @return array List of sub projects.
+        */
+       private function get_paged_active_sub_projects( $project, $args = array() ) {
+               global $wpdb;
+
+               $defaults = array(
+                       'per_page' => 20,
+                       'page'     => 1,
+                       'search'   => false,
+                       'set_slug' => '',
+                       'locale'   => '',
+                       'filter'   => false,
+               );
+               $r = wp_parse_args( $args, $defaults );
+               extract( $r, EXTR_SKIP );
+
+               $limit_sql = '';
+               if ( $per_page ) {
+                       $limit_sql = $wpdb->prepare( 'LIMIT %d, %d', ( $page - 1 ) * $per_page, $per_page );
+               }
+
+               $parent_project_sql = $wpdb->prepare( 'AND tp.parent_project_id = %d', $project->id );
+
+               $search_sql = '';
+               if ( $search ) {
+                       $esc_search = '%%' . like_escape( $search ) . '%%';
+                       $search_sql = $wpdb->prepare( 'AND ( tp.name LIKE %s OR tp.slug LIKE %s )', $esc_search, $esc_search );
+               }
+
+               // Special Waiting Project Tab
+               // This removes the parent_project_id restriction and replaces it with all-translation-editer-projects
+               if ( 'waiting' == $project->slug && is_user_logged_in() && function_exists( 'wporg_gp_rosetta_roles' ) ) {
+
+                       if ( ! $filter ) {
+                               $filter = 'strings-waiting-and-fuzzy';
+                       }
+
+                       $user_id = get_current_user_id();
+
+                       // Global Admin or Locale-specific admin
+                       $can_approve_for_all = wporg_gp_rosetta_roles()->is_global_administrator( $user_id );
+
+                       // Check to see if they have any special approval permissions
+                       $allowed_projects = array();
+                       if ( ! $can_approve_for_all && wporg_gp_rosetta_roles()->is_approver_for_locale( $user_id, $locale ) ) {
+                               $allowed_projects = wporg_gp_rosetta_roles()->get_project_id_access_list( $user_id, $locale, true );
+
+                               // Check to see if they can approve for all projects in this locale.
+                               if ( in_array( 'all', $allowed_projects ) ) {
+                                       $can_approve_for_all = true;
+                                       $allowed_projects = array();
+                               }
+                       }
+
+                       $parent_project_sql = '';
+                       if ( $can_approve_for_all ) {
+                               // The current user can approve for all projects, so just grab all with any waiting strings.
+                               $parent_project_sql = 'AND ( stats.waiting > 0 OR stats.fuzzy > 0 )';
+
+                       } elseif ( $allowed_projects ) {
+                               // The current user can approve for a small set of projects.
+                               // We only need to check against tp.id and not tp_sub.id in this case as we've overriding the parent_project_id check
+                               $ids = implode( ', ', array_map( 'intval', $allowed_projects ) );
+                               $parent_project_sql = "AND tp.id IN( $ids ) AND stats.waiting > 0";
+
+                       } else {
+                               // The current user can't approve for any locale projects, or is logged out.
+                               $parent_project_sql = 'AND 0=1';
+
+                       }
+
+                       // Limit to only showing base-level projects
+                       $parent_project_sql .= " AND tp.parent_project_id IN( (SELECT id FROM {$wpdb->gp_projects} WHERE parent_project_id IS NULL AND active = 1) )";
+
+               }
+
+               $filter_order_by = $filter_where = '';
+               $sort_order = 'DESC';
+               $filter_name = $filter;
+               if ( $filter && '-asc' == substr( $filter, -4 ) ) {
+                       $sort_order = 'ASC';
+                       $filter_name = substr( $filter, 0, -4 );
+               }
+               switch ( $filter_name ) {
+                       default:
+                       case 'special':
+                               // Float favorites to the start, but only if they have untranslated strings
+                               $user_fav_projects = array_map( 'esc_sql', $this->get_user_favorites( $project->slug ) );
+
+                               // Float Favorites to the start, float fully translated to the bottom, order the rest by name
+                               if ( $user_fav_projects ) {
+                                       $filter_order_by = 'FIELD( tp.path, "' . implode( '", "', $user_fav_projects ) . '" ) > 0 AND stats.untranslated > 0 DESC, stats.untranslated > 0 DESC, stats.untranslated DESC, tp.name ASC';
+                               } else {
+                                       $filter_order_by = 'stats.untranslated > 0 DESC, stats.untranslated DESC, tp.name ASC';
+                               }
+                               break;
+
+                       case 'favorites':
+                               // Only list favorites
+                               $user_fav_projects = array_map( 'esc_sql', $this->get_user_favorites( $project->slug ) );
+
+                               if ( $user_fav_projects ) {
+                                       $filter_where = 'AND tp.path IN( "' . implode( '", "', $user_fav_projects ) . '" )';
+                               } else {
+                                       $filter_where = 'AND 0=1';
+                               }
+                               $filter_order_by = 'stats.untranslated > 0 DESC, tp.name ASC';
+
+                               break;
+
+                       case 'strings-remaining':
+                               $filter_where = 'AND stats.untranslated > 0';
+                               $filter_order_by = "stats.untranslated $sort_order, tp.name ASC";
+                               break;
+
+                       case 'strings-waiting-and-fuzzy':
+                               $filter_where = 'AND (stats.waiting > 0 OR stats.fuzzy > 0 )';
+                               $filter_order_by = "tp.path LIKE 'wp/%%' AND (stats.fuzzy + stats.waiting) > 0 DESC, (stats.fuzzy + stats.waiting) $sort_order, tp.name ASC";
+                               break;
+
+                       case 'percent-completed':
+                               $filter_where = 'AND stats.untranslated > 0';
+                               $filter_order_by = "( stats.current / stats.all ) $sort_order, tp.name ASC";
+                               break;
+               }
+
+               /*
+                * Find all child projects with translation sets that match the current locale/slug.
+                *
+                * 1. We need to fetch all sub-projects of the current project (so, if we're at wp-plugins, we want akismet, debug bar, importers, etc)
+                * 2. Next, we fetch the sub-projects of those sub-projects, that gets us things like Development, Readme, etc.
+                * 3. Next, we fetch the translation sets of both the sub-projects(1), and any sub-sub-projects(2).
+                * Once we have the sets in 3, we can then check to see if there exists any translation sets for the current (locale, slug) (ie. en-au/default)
+                * If not, we can simply filter them out, so that paging only has items returned that actually exist.
+                */
+               $_projects = $project->many( "
+                       SELECT SQL_CALC_FOUND_ROWS tp.*
+                       FROM {$wpdb->gp_projects} tp
+                               LEFT JOIN {$wpdb->project_translation_status} stats ON stats.project_id = tp.id AND stats.locale = %s AND stats.locale_slug = %s
+                       WHERE
+                               tp.active = 1
+                               $parent_project_sql
+                               $search_sql
+                               $filter_where
+                       GROUP BY tp.id
+                       ORDER BY $filter_order_by
+                       $limit_sql
+               ", $locale, $set_slug );
+
+               $results = (int) $project->found_rows();
+               $pages = (int) ceil( $results / $per_page );
+
+               $projects = array();
+               foreach ( $_projects as $project ) {
+                       $projects[ $project->id ] = $project;
+               }
+
+               return array(
+                       'pages' => compact( 'pages', 'page', 'per_page', 'results' ),
+                       'projects' => $projects,
+                       'filter' => $filter,
+               );
+       }
+
+       /**
+        * Retrieves a list of projects which the current user has favorited.
+        *
+        * @return array List of favorited items, eg [ 'wp-themes/twentyten', 'wp-themes/twentyeleven' ]
+        */
+       function get_user_favorites( $project_slug = false ) {
+               global $wpdb;
+
+               if ( ! is_user_logged_in() ) {
+                       return array();
+               }
+
+               $user_id = get_current_user_id();
+
+               switch ( $project_slug ) {
+                       default:
+                               // Fall through to include both Themes and Plugins
+                       case 'wp-themes':
+                               // Theme favorites are stored as theme slugs, these map 1:1 to GlotPress projects
+                               $theme_favorites = array_map( function( $slug ) {
+                                       return "wp-themes/$slug";
+                               }, (array) get_user_meta( $user_id, 'theme_favorites', true ) );
+
+                               if ( 'wp-themes' === $project_slug ) {
+                                       return $theme_favorites;
+                               }
+
+                       case 'wp-plugins':
+                               // Plugin favorites are stored as topic ID's
+                               $plugin_fav_ids = array_keys( (array) get_user_meta( $user_id, PLUGINS_TABLE_PREFIX . 'plugin_favorite', true ) );
+                               $plugin_fav_slugs = array();
+                               if ( $plugin_fav_ids ) {
+                                       $plugin_fav_ids = implode( ',', array_map( 'intval', $plugin_fav_ids ) );
+                                       $plugin_fav_slugs = $wpdb->get_col( "SELECT topic_slug FROM " . PLUGINS_TABLE_PREFIX . "topics WHERE topic_id IN( $plugin_fav_ids )" );
+                               }
+
+                               $plugin_favorites = array_map( function( $slug ) {
+                                       return "wp-plugins/$slug";
+                               }, $plugin_fav_slugs );
+
+                               if ( 'wp-plugins' === $project_slug ) {
+                                       return $plugin_favorites;
+                               }
+               }
+
+               // Return all favorites, for uses in things like the Waiting tab
+               return array_merge( $theme_favorites, $plugin_favorites );
+       }
+
+       /**
+        * Retrieves active top level projects.
+        *
+        * @return array List of top level projects.
+        */
+       public function get_active_top_level_projects() {
+               global $wpdb;
+
+               return GP::$project->many( "
+                       SELECT *
+                       FROM {$wpdb->gp_projects}
+                       WHERE
+                               parent_project_id IS NULL
+                               AND active = 1
+                       ORDER BY name ASC
+               " );
+       }
+
+       private function _sort_contributors_by_total_count_callback( $a, $b ) {
+               return $a->total_count < $b->total_count;
+       }
+
+       private function _sort_reverse_name_callback( $a, $b ) {
+               // The Waiting project should always be first.
+               if ( $a->slug == 'waiting' ) {
+                       return -1;
+               }
+               return - strcasecmp( $a->name, $b->name );
+       }
+
+       private function _sort_name_callback( $a, $b ) {
+               return strcasecmp( $a->name, $b->name );
+       }
+
+       private function _encode( $raw ) {
+               $raw = mb_convert_encoding( $raw, 'UTF-8', 'ASCII, JIS, UTF-8, Windows-1252, ISO-8859-1' );
+               return ent2ncr( htmlspecialchars_decode( htmlentities( $raw, ENT_NOQUOTES, 'UTF-8' ), ENT_NOQUOTES ) );
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclassredirectorphpfromrev2999sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesroutesredirectorphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-redirector.php (from rev 2999, sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/redirector.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-redirector.php                          (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-redirector.php    2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,25 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace WordPressdotorg\GlotPress\Routes\Routes;
+
+use GP_Route;
+
+/**
+ * Redirector Route Class.
+ *
+ * Provides redirection routes.
+ */
+class Redirector extends GP_Route {
+
+       public function redirect_languages( $path = '' ) {
+               if ( empty( $path ) ) {
+                       $this->redirect( '/' );
+               } else {
+                       $this->redirect( "/locale/$path" );
+               }
+       }
+
+       public function redirect_index() {
+               $this->redirect( '/' );
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclassstatsphpfromrev2999sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesroutesstatsoverviewphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-stats.php (from rev 2999, sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/stats-overview.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-stats.php                               (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-stats.php 2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,136 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace WordPressdotorg\GlotPress\Routes\Routes;
+
+use GP;
+use GP_Route;
+
+/**
+ * Stats Route Class.
+ *
+ * Provides the route for translate.wordpress.org/stats.
+ */
+class Stats extends GP_Route {
+
+       public function get_stats_overview() {
+               global $wpdb;
+
+               $projects = array(
+                       'meta/rosetta' => false,
+                       'meta/browsehappy' => false,
+                       'meta/themes' => false,
+                       'meta/plugins' => false,
+                       'meta/forums' => false,
+                       'apps/android' => false,
+                       'apps/ios' => false,
+                       'waiting' => false,
+               );
+
+               // I'm sure there's somewhere to fetch these from statically defined
+               $wp_project = GP::$project->by_path( 'wp' );
+               foreach ( GP::$project->find_many( array( 'parent_project_id' => $wp_project->id, 'active' => 1 ), 'name ASC' ) as $wp_sub_project ) {
+                       // Prefix the WordPress projects...
+                       $wp_sub_project->name = $wp_project->name . ' ' . $wp_sub_project->name;
+                       $projects = array_merge( array( $wp_sub_project->path => $wp_sub_project ), $projects );
+               }
+
+               // Load the projects for each display item
+               array_walk( $projects, function( &$project, $project_slug ) {
+                       if ( ! $project ) {
+                               $project = GP::$project->by_path( $project_slug );
+                       }
+               } );
+
+               $all_project_paths_sql = '"' . implode( '", "', array_keys( $projects ) ) . '"';
+               $sql = "SELECT
+                               path, locale, locale_slug,
+                               (100 * stats.current/stats.all) as percent_complete,
+                               stats.waiting+stats.fuzzy as waiting_strings
+                       FROM {$wpdb->project_translation_status} stats
+                               LEFT JOIN {$wpdb->gp_projects} p ON stats.project_id = p.id
+                       WHERE
+                               p.path IN ( $all_project_paths_sql )
+                               AND p.active = 1";
+
+               $rows = $wpdb->get_results( $sql );
+
+               // Split out into $[Locale][Project] = %
+               $translation_locale_statuses = array();
+               foreach ( $rows as $set ) {
+                       $locale_key = $set->locale;
+                       if ( 'default' != $set->locale_slug ) {
+                               $locale_key = $set->locale . '/' . $set->locale_slug;
+                       }
+
+                       /*
+                        * > 50% round down, so that a project with all strings except 1 translated shows 99%, instead of 100%.
+                        * < 50% round up, so that a project with just a few strings shows 1%, instead of 0%.
+                        */
+                       $percent_complete = (float) $set->percent_complete;
+                       $percent_complete = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
+                       $translation_locale_statuses[ $locale_key ][ $set->path ] = $percent_complete;
+
+                       if ( ! isset( $translation_locale_statuses[ $locale_key ]['waiting'] ) ) {
+                               $translation_locale_statuses[ $locale_key ]['waiting'] = 0;
+                       }
+                       $translation_locale_statuses[ $locale_key ]['waiting'] += (int) $set->waiting_strings;
+               }
+               unset( $rows, $locale_key, $set );
+
+               // Append the Plugins/Themes waiting strings
+               $parent_project_ids = implode(',', array(
+                       GP::$project->by_path( 'wp-plugins' )->id,
+                       GP::$project->by_path( 'wp-themes' )->id,
+               ) );
+               $sql = "SELECT
+                               locale, locale_slug,
+                               SUM( stats.waiting ) + SUM( stats.fuzzy ) as waiting_strings
+                       FROM {$wpdb->project_translation_status} stats
+                               LEFT JOIN {$wpdb->gp_projects} p ON stats.project_id = p.id
+                       WHERE
+                               p.parent_project_id IN ( $parent_project_ids )
+                               AND p.active = 1
+                       GROUP BY locale, locale_slug";
+
+               $rows = $wpdb->get_results( $sql );
+               foreach ( $rows as $set ) {
+                       $locale_key = $set->locale;
+                       if ( 'default' != $set->locale_slug ) {
+                               $locale_key = $set->locale . '/' . $set->locale_slug;
+                       }
+
+                       $translation_locale_statuses[ $locale_key ]['waiting'] += (int) $set->waiting_strings;
+               }
+
+               // Calculate a list of [Locale] = % subtotals
+               $translation_locale_complete = array();
+               foreach ( $translation_locale_statuses as $locale => $sets ) {
+                       unset( $sets['waiting'] );
+                       $sets_count = count( $sets );
+                       if ( $sets_count ) {
+                               $translation_locale_complete[ $locale ] = round( array_sum( $sets ) / $sets_count, 3 );
+                       } else {
+                               $translation_locale_complete[ $locale ] = 0;
+                       }
+               }
+               unset( $locale, $sets );
+
+               // Sort by translation completeness, least number of waiting strings, and locale slug.
+               uksort( $translation_locale_complete, function ( $a, $b ) use ( $translation_locale_complete, $translation_locale_statuses ) {
+                       if ( $translation_locale_complete[ $a ] < $translation_locale_complete[ $b ] ) {
+                               return 1;
+                       } elseif ( $translation_locale_complete[ $a ] == $translation_locale_complete[ $b ] ) {
+                               if ( $translation_locale_statuses[ $a ]['waiting'] != $translation_locale_statuses[ $b ]['waiting'] ) {
+                                       return strnatcmp( $translation_locale_statuses[ $a ]['waiting'], $translation_locale_statuses[ $b ]['waiting'] );
+                               } else {
+                                       return strnatcmp( $a, $b );
+                               }
+                       } else {
+                               return -1;
+                       }
+               } );
+
+               $this->tmpl( 'stats-overview', get_defined_vars() );
+       }
+
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclasswpdirectoryphpfromrev2999sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesrouteswpdirectoryphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-directory.php (from rev 2999, sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/wp-directory.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-directory.php                                (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-directory.php  2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,245 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace WordPressdotorg\GlotPress\Routes\Routes;
+
+use DateInterval;
+use DatePeriod;
+use DateTime;
+use GP_Route;
+
+class WP_Directory extends GP_Route {
+
+       /**
+        * Prints stats about contributors of a specific project.
+        *
+        * @param GP_Project $project The project.
+        * @return array|false False if project not found, otherwise array with contributors.
+        */
+       public function get_contributors( $project ) {
+               global $wpdb;
+
+               $contributors_by_locale = array();
+               $default_value = array(
+                       'count' => 0,
+                       'editors' => array(),
+                       'contributors' => array(),
+               );
+
+               $translation_editors = $wpdb->get_results( $wpdb->prepare( "
+                       SELECT
+                               `user_id`, `locale`
+                       FROM {$wpdb->wporg_translation_editors}
+                       WHERE `project_id` = %d
+               ", $project->id ), OBJECT );
+
+               foreach ( $translation_editors as $translation_editor ) {
+                       if ( ! isset( $contributors_by_locale[ $translation_editor->locale ] ) ) {
+                               $contributors_by_locale[ $translation_editor->locale ] = $default_value;
+                       }
+
+                       $user = get_user_by( 'id', $translation_editor->user_id );
+                       if ( ! $user ) {
+                               continue;
+                       }
+
+                       $contributors_by_locale[ $translation_editor->locale ]['editors'][ $translation_editor->user_id ] = (object) array(
+                               'nicename'     => $user->user_nicename,
+                               'display_name' => $this->_encode( $user->display_name ),
+                       );
+
+                       $contributors_by_locale[ $translation_editor->locale ]['count']++;
+               }
+
+               unset( $translation_editors );
+
+               foreach ( $this->get_translation_contributors_by_locale( $project->id ) as $row ) {
+                       if ( ! isset( $contributors_by_locale[ $row->locale ] ) ) {
+                               $contributors_by_locale[ $row->locale ] = $default_value;
+                       }
+
+                       if ( isset( $contributors_by_locale[ $row->locale ]['editors'][ $row->user_id ] ) ) {
+                               continue;
+                       }
+
+                       if ( isset( $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] ) ) {
+                               continue;
+                       }
+
+                       $user = get_user_by( 'id', $row->user_id );
+                       if ( ! $user ) {
+                               continue;
+                       }
+
+                       $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] = (object) array(
+                               'nicename'     => $user->user_nicename,
+                               'display_name' => $this->_encode( $user->display_name ),
+                       );
+
+                       $contributors_by_locale[ $row->locale ]['count']++;
+               }
+
+               $sub_projects = $wpdb->get_col( $wpdb->prepare( "
+                       SELECT id
+                       FROM {$wpdb->gp_projects}
+                       WHERE parent_project_id = %d
+               ", $project->id ) );
+
+               foreach ( $sub_projects as $sub_project ) {
+                       foreach ( $this->get_translation_contributors_by_locale( $sub_project ) as $row ) {
+                               if ( ! isset( $contributors_by_locale[ $row->locale ] ) ) {
+                                       $contributors_by_locale[ $row->locale ] = $default_value;
+                               }
+
+                               if ( isset( $contributors_by_locale[ $row->locale ]['editors'][ $row->user_id ] ) ) {
+                                       continue;
+                               }
+
+                               if ( isset( $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] ) ) {
+                                       continue;
+                               }
+
+                               $user = get_user_by( 'id', $row->user_id );
+                               if ( ! $user ) {
+                                       continue;
+                               }
+
+                               $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] = (object) array(
+                                       'nicename'     => $user->user_nicename,
+                                       'display_name' => $this->_encode( $user->display_name ),
+                               );
+
+                               $contributors_by_locale[ $row->locale ]['count']++;
+                       }
+               }
+
+               return $contributors_by_locale;
+       }
+
+       /**
+        * Generates the chart data for contributors activity.
+        *
+        * @param \GP_Project $project The project.
+        * @return array The data to build a chart via Chartist.js.
+        */
+       protected function get_contributors_chart_data( $project ) {
+               global $wpdb;
+
+               $sub_projects = $wpdb->get_col( $wpdb->prepare( "
+                       SELECT id
+                       FROM {$wpdb->gp_projects}
+                       WHERE parent_project_id = %d
+               ", $project->id ) );
+
+               $project_ids = array_merge( array( $project->id ), $sub_projects );
+               $translation_set_ids = $wpdb->get_col( "
+                       SELECT `id` FROM {$wpdb->gp_translation_sets} WHERE `project_id` IN ( " . implode( ',', $project_ids ) . ")
+               " );
+
+               if ( ! $translation_set_ids ) {
+                       return array();
+               }
+
+               $date_begin = new DateTime( '-6 day' );
+               $date_end = new DateTime( 'NOW' );
+               $date_interval = new DateInterval( 'P1D' );
+               $date_range = new DatePeriod( $date_begin, $date_interval, $date_end );
+
+               $days = array();
+               foreach ( $date_range as $date ) {
+                       $days[] = $date->format( 'Y-m-d' );
+               }
+               $days[] = $date_end->format( 'Y-m-d' );
+
+               $counts = $wpdb->get_results( "
+                       SELECT
+                               DATE(date_modified) AS `day`, COUNT(*) AS `count`, `status`
+                       FROM {$wpdb->gp_translations}
+                       WHERE
+                               `translation_set_id` IN (" . implode( ',', $translation_set_ids ) . ")
+                               AND date_modified >= ( CURDATE() - INTERVAL 7 DAY )
+                       GROUP BY `status`, `day`
+                       ORDER BY `day` DESC
+               " );
+
+               $status = array( 'current', 'waiting', 'rejected' );
+               $data = [];
+               foreach ( $days as $day ) {
+                       $data[ $day ] = array_fill_keys( $status, 0 );
+                       foreach ( $counts as $count ) {
+                               if ( $count->day !== $day || ! in_array( $count->status, $status ) ) {
+                                       continue;
+                               }
+
+                               $data[ $day ][ $count->status ] = (int) $count->count;
+                       }
+               }
+
+               $labels = array_keys( $data );
+               array_pop( $labels );
+               $labels[] = ''; // Don't show a label for today
+
+               $series = array();
+               $series_data = array_values( $data );
+               foreach ( $status as $stati ) {
+                       $series[] = (object) array(
+                               'name' => $stati,
+                               'data' => wp_list_pluck( $series_data, $stati ),
+                       );
+               }
+
+               $chart_data = compact( 'labels', 'series' );
+
+               return $chart_data;
+       }
+
+       /**
+        * Prints stats about language packs of a specific project.
+        *
+        * @param string $type Type of the language pack, plugin or theme.
+        * @param string $slug Slug of a project.
+        */
+       public function get_language_packs( $type, $slug ) {
+               $http_context = stream_context_create( array(
+                       'http' => array(
+                               'user_agent' => 'WordPress.org Translate',
+                       ),
+               ) );
+               if ( 'plugin' === $type ) {
+                       $type = 'plugins';
+               } else {
+                       $type = 'themes';
+               }
+               $json = file_get_contents( "https://api.wordpress.org/translations/$type/1.0/?slug={$slug}", null, $http_context );
+               $language_packs = $json && '{' == $json[0] ? json_decode( $json ) : null;
+
+               return $language_packs;
+       }
+
+       /**
+        * Retrieves translators of a specific project.
+        *
+        * @param int $project_id Project ID.
+        * @return object Translators of the project.
+        */
+       private function get_translation_contributors_by_locale( $project_id ) {
+               global $wpdb;
+
+               $sql = $wpdb->prepare( "
+                       SELECT ts.`locale`, ts.`slug` AS `locale_slug`, t.`user_id`
+                       FROM `{$wpdb->gp_translations}` t, `{$wpdb->gp_translation_sets}` ts
+                       WHERE t.`translation_set_id` = ts.`id`
+                           AND t.`user_id` IS NOT NULL AND t.`user_id` != 0
+                           AND t.`date_modified` > %s
+                           AND ts.`project_id` = %d
+                           AND t.`status` <> 'rejected'
+                       GROUP BY ts.`locale`, ts.`slug`, t.`user_id`
+               ", date( 'Y-m-d', time() - YEAR_IN_SECONDS ), $project_id );
+
+               return $wpdb->get_results( $sql );
+       }
+
+       private function _encode( $raw ) {
+               $raw = mb_convert_encoding( $raw, 'UTF-8', 'ASCII, JIS, UTF-8, Windows-1252, ISO-8859-1' );
+               return ent2ncr( htmlspecialchars_decode( htmlentities( $raw, ENT_NOQUOTES, 'UTF-8' ), ENT_NOQUOTES ) );
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclasswppluginsphpfromrev2999sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesrouteswppluginsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-plugins.php (from rev 2999, sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/wp-plugins.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-plugins.php                          (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-plugins.php    2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,184 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace WordPressdotorg\GlotPress\Routes\Routes;
+
+use GP;
+
+class WP_Plugins extends WP_Directory {
+
+       /**
+        * Prints stats about sub-project of a specific project.
+        *
+        * @param string $project_slug Slug of a project.
+        */
+       public function get_plugin_projects( $project_slug ) {
+               global $wpdb;
+
+               $project_path = 'wp-plugins/' . $project_slug;
+               $project = GP::$project->by_path( $project_path );
+               if ( ! $project ) {
+                       return $this->die_with_404();
+               }
+
+               $rows = $wpdb->get_results( "
+                       SELECT
+                               path, locale, locale_slug,
+                               (100 * stats.current/stats.all) as percent_complete,
+                               stats.waiting+stats.fuzzy as waiting_strings,
+                               stats.untranslated as untranslated
+                       FROM {$wpdb->project_translation_status} stats
+                               LEFT JOIN {$wpdb->gp_projects} p ON stats.project_id = p.id
+                       WHERE
+                               p.parent_project_id = '{$project->id}'
+               " );
+
+               // Split out into $[Locale][Project] = %
+               $translation_locale_statuses = array();
+               $sub_projects = array();
+               foreach ( $rows as $set ) {
+
+                       // Find unique locale key.
+                       $locale_key = $set->locale;
+                       if ( 'default' != $set->locale_slug ) {
+                               $locale_key = $set->locale . '/' . $set->locale_slug;
+                       }
+                       $sub_project = str_replace( "$project_path/", '', $set->path );
+                       $sub_projects[ $sub_project ] = true;
+
+                       /*
+                        * > 50% round down, so that a project with all strings except 1 translated shows 99%, instead of 100%.
+                        * < 50% round up, so that a project with just a few strings shows 1%, instead of 0%.
+                        */
+                       $percent_complete = (float) $set->percent_complete;
+                       $translation_locale_statuses[ $locale_key ][ $sub_project ] = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
+
+                       // Increment the amount of waiting and untranslated strings.
+                       if ( ! isset( $translation_locale_statuses[ $locale_key ]['waiting'] ) ) {
+                               $translation_locale_statuses[ $locale_key ]['waiting'] = 0;
+                       }
+                       if ( ! isset( $translation_locale_statuses[ $locale_key ]['untranslated'] ) ) {
+                               $translation_locale_statuses[ $locale_key ]['untranslated'] = 0;
+                       }
+                       $translation_locale_statuses[ $locale_key ]['waiting'] += (int) $set->waiting_strings;
+                       $translation_locale_statuses[ $locale_key ]['untranslated'] += (int) $set->untranslated;
+
+
+                       ksort( $translation_locale_statuses[ $locale_key ], SORT_NATURAL );
+               }
+
+               // Check if the plugin has at least one code project. These won't be created if a plugin
+               // has no text domain defined.
+               $sub_projects = array_keys( $sub_projects );
+               $has_error = ( ! in_array( 'dev', $sub_projects ) && ! in_array( 'stable', $sub_projects ) );
+
+               unset( $project_path, $locale_key, $rows, $set, $sub_project, $sub_projects );
+
+               // Calculate a list of [Locale] = % subtotals
+               $translation_locale_complete = array();
+               foreach ( $translation_locale_statuses as $locale => $sets ) {
+                       unset( $sets['waiting'], $sets['untranslated'] );
+                       $translation_locale_complete[ $locale ] = round( array_sum( $sets ) / count( $sets ), 3 );
+               }
+               unset( $locale, $sets );
+
+
+               // Sort by translation completeness, least number of waiting strings, and locale slug.
+               uksort( $translation_locale_complete, function ( $a, $b ) use ( $translation_locale_complete, $translation_locale_statuses ) {
+                       if ( $translation_locale_complete[ $a ] < $translation_locale_complete[ $b ] ) {
+                               return 1;
+                       } elseif ( $translation_locale_complete[ $a ] == $translation_locale_complete[ $b ] ) {
+                               if ( $translation_locale_statuses[ $a ]['waiting'] != $translation_locale_statuses[ $b ]['waiting'] ) {
+                                       return strnatcmp( $translation_locale_statuses[ $a ]['waiting'], $translation_locale_statuses[ $b ]['waiting'] );
+                               } else {
+                                       return strnatcmp( $a, $b );
+                               }
+                       } else {
+                               return -1;
+                       }
+               } );
+
+               $project->icon = $this->get_plugin_icon( $project, 64 );
+
+               $this->tmpl( 'projects-wp-plugins', get_defined_vars() );
+       }
+
+       /**
+        * Prints stats about contributors of a specific project.
+        *
+        * @param string $project_slug Slug of a project.
+        */
+       public function get_plugin_contributors( $project_slug ) {
+               $project_path = 'wp-plugins/' . $project_slug;
+               $project = GP::$project->by_path( $project_path );
+               if ( ! $project ) {
+                       return $this->die_with_404();
+               }
+
+               $project->icon = $this->get_plugin_icon( $project, 64 );
+
+               $contributors_by_locale = gp_get_meta( 'wp-plugins', $project->id, 'contributors-by-locale' );
+               if ( ! $contributors_by_locale || $contributors_by_locale['last_updated'] + HOUR_IN_SECONDS < time() ) {
+                       $contributors_by_locale = $this->get_contributors( $project );
+                       $contributors_by_locale['last_updated'] = time();
+                       gp_update_meta( $project->id, 'contributors-by-locale', $contributors_by_locale, 'wp-plugins' );
+               }
+
+               $chart_data = gp_get_meta( 'wp-plugins', $project->id, 'contributors-chart-data' );
+               if ( ! $chart_data || $chart_data['last_updated'] + DAY_IN_SECONDS < time() ) {
+                       $chart_data = $this->get_contributors_chart_data( $project );
+                       $chart_data['last_updated'] = time();
+                       gp_update_meta( $project->id, 'contributors-chart-data', $chart_data, 'wp-plugins' );
+               }
+
+               unset( $contributors_by_locale['last_updated'], $chart_data['last_updated'] );
+
+               $this->tmpl( 'projects-wp-plugins-contributors', get_defined_vars() );
+       }
+
+       /**
+        * Prints stats about language packs of a specific project.
+        *
+        * @param string $project_slug Slug of a project.
+        */
+       public function get_plugin_language_packs( $project_slug ) {
+               $project_path = 'wp-plugins/' . $project_slug;
+               $project = GP::$project->by_path( $project_path );
+               if ( ! $project ) {
+                       return $this->die_with_404();
+               }
+
+               $project->icon = $this->get_plugin_icon( $project, 64 );
+
+               $http_context = stream_context_create( array(
+                       'http' => array(
+                               'user_agent' => 'WordPress.org Translate',
+                       ),
+               ) );
+               $json = file_get_contents( "https://api.wordpress.org/translations/plugins/1.0/?slug={$project_slug}", null, $http_context );
+               $language_packs = $json && '{' == $json[0] ? json_decode( $json ) : null;
+
+               $this->tmpl( 'projects-wp-plugins-language-packs', get_defined_vars() );
+       }
+
+       /**
+        * Retrieves the icon of a plugin.
+        *
+        * @param GP_Project $project The plugin project.
+        * @param int        $size    Optional. The size of the icon. Default 64.
+        * @return string HTML markup for the icon.
+        */
+       private function get_plugin_icon( $project, $size = 64 ) {
+               $default = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
+
+               $icon = '';
+               if ( function_exists( 'wporg_get_plugin_icon' ) ) {
+                       $icon = wporg_get_plugin_icon( $project->slug, $size );
+               }
+
+               if ( $icon ) {
+                       return $icon;
+               }
+
+               return $default;
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesclasswpthemesphpfromrev2999sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesrouteswpthemesphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-themes.php (from rev 2999, sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/wp-themes.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-themes.php                           (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-themes.php     2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,174 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace WordPressdotorg\GlotPress\Routes\Routes;
+
+use GP;
+
+class WP_Themes extends WP_Directory {
+
+       /**
+        * Prints stats about sub-project of a specific project.
+        *
+        * @param string $project_slug Slug of a project.
+        */
+       public function get_theme_projects( $project_slug ) {
+               global $wpdb;
+
+               $project_path = 'wp-themes/' . $project_slug;
+               $project = GP::$project->by_path( $project_path );
+               if ( ! $project ) {
+                       return $this->die_with_404();
+               }
+
+               $rows = $wpdb->get_results( "
+                       SELECT
+                               path, locale, locale_slug,
+                               (100 * stats.current/stats.all) as percent_complete,
+                               stats.waiting+stats.fuzzy as waiting_strings,
+                               stats.untranslated as untranslated
+                       FROM {$wpdb->project_translation_status} stats
+                               LEFT JOIN {$wpdb->gp_projects} p ON stats.project_id = p.id
+                       WHERE
+                               p.id = '{$project->id}'
+               " );
+
+               // Split out into $[Locale][Project] = %
+               $translation_locale_statuses = array();
+               $sub_projects = array();
+               foreach ( $rows as $set ) {
+
+                       // Find unique locale key.
+                       $locale_key = $set->locale;
+                       if ( 'default' != $set->locale_slug ) {
+                               $locale_key = $set->locale . '/' . $set->locale_slug;
+                       }
+
+                       /*
+                        * > 50% round down, so that a project with all strings except 1 translated shows 99%, instead of 100%.
+                        * < 50% round up, so that a project with just a few strings shows 1%, instead of 0%.
+                        */
+                       $percent_complete = (float) $set->percent_complete;
+                       $translation_locale_statuses[ $locale_key ]['stable'] = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
+
+                       // Increment the amount of waiting and untranslated strings.
+                       if ( ! isset( $translation_locale_statuses[ $locale_key ]['waiting'] ) ) {
+                               $translation_locale_statuses[ $locale_key ]['waiting'] = 0;
+                       }
+                       if ( ! isset( $translation_locale_statuses[ $locale_key ]['untranslated'] ) ) {
+                               $translation_locale_statuses[ $locale_key ]['untranslated'] = 0;
+                       }
+                       $translation_locale_statuses[ $locale_key ]['waiting'] += (int) $set->waiting_strings;
+                       $translation_locale_statuses[ $locale_key ]['untranslated'] += (int) $set->untranslated;
+
+
+                       ksort( $translation_locale_statuses[ $locale_key ], SORT_NATURAL );
+               }
+
+               unset( $project_path, $locale_key, $rows, $set, $sub_project, $sub_projects );
+
+               // Calculate a list of [Locale] = % subtotals
+               $translation_locale_complete = array();
+               foreach ( $translation_locale_statuses as $locale => $sets ) {
+                       unset( $sets['waiting'], $sets['untranslated'] );
+                       $translation_locale_complete[ $locale ] = round( array_sum( $sets ) / count( $sets ), 3 );
+               }
+               unset( $locale, $sets );
+
+
+               // Sort by translation completeness, least number of waiting strings, and locale slug.
+               uksort( $translation_locale_complete, function ( $a, $b ) use ( $translation_locale_complete, $translation_locale_statuses ) {
+                       if ( $translation_locale_complete[ $a ] < $translation_locale_complete[ $b ] ) {
+                               return 1;
+                       } elseif ( $translation_locale_complete[ $a ] == $translation_locale_complete[ $b ] ) {
+                               if ( $translation_locale_statuses[ $a ]['waiting'] != $translation_locale_statuses[ $b ]['waiting'] ) {
+                                       return strnatcmp( $translation_locale_statuses[ $a ]['waiting'], $translation_locale_statuses[ $b ]['waiting'] );
+                               } else {
+                                       return strnatcmp( $a, $b );
+                               }
+                       } else {
+                               return -1;
+                       }
+               } );
+
+               $project->icon = $this->get_theme_icon( $project, 64 );
+
+               $this->tmpl( 'projects-wp-themes', get_defined_vars() );
+       }
+
+       /**
+        * Prints stats about contributors of a specific project.
+        *
+        * @param string $project_slug Slug of a project.
+        */
+       public function get_theme_contributors( $project_slug ) {
+               global $wpdb;
+
+               $project_path = 'wp-themes/' . $project_slug;
+               $project = GP::$project->by_path( $project_path );
+               if ( ! $project ) {
+                       return $this->die_with_404();
+               }
+
+               $project->icon = $this->get_theme_icon( $project, 64 );
+
+               $contributors_by_locale = gp_get_meta( 'wp-themes', $project->id, 'contributors-by-locale' );
+               if ( ! $contributors_by_locale || $contributors_by_locale['last_updated'] + HOUR_IN_SECONDS < time() ) {
+                       $contributors_by_locale = $this->get_contributors( $project );
+                       $contributors_by_locale['last_updated'] = time();
+                       gp_update_meta( $project->id, 'contributors-by-locale', $contributors_by_locale, 'wp-themes' );
+               }
+
+               $chart_data = gp_get_meta( 'wp-themes', $project->id, 'contributors-chart-data' );
+               if ( ! $chart_data || $chart_data['last_updated'] + DAY_IN_SECONDS < time() ) {
+                       $chart_data = $this->get_contributors_chart_data( $project );
+                       $chart_data['last_updated'] = time();
+                       gp_update_meta( $project->id, 'contributors-chart-data', $chart_data, 'wp-themes' );
+               }
+
+               unset( $contributors_by_locale['last_updated'], $chart_data['last_updated'] );
+
+               $this->tmpl( 'projects-wp-themes-contributors', get_defined_vars() );
+       }
+
+       /**
+        * Prints stats about language packs of a specific project.
+        *
+        * @param string $project_slug Slug of a project.
+        */
+       public function get_theme_language_packs( $project_slug ) {
+               $project_path = 'wp-themes/' . $project_slug;
+               $project = GP::$project->by_path( $project_path );
+               if ( ! $project ) {
+                       return $this->die_with_404();
+               }
+
+               $project->icon = $this->get_theme_icon( $project, 64 );
+
+               $language_packs = $this->get_language_packs( 'theme', $project_slug );
+
+               $this->tmpl( 'projects-wp-themes-language-packs', get_defined_vars() );
+       }
+
+       /**
+        * Retrieves the icon of a theme.
+        *
+        * @param GP_Project $project The theme project.
+        * @param int        $size    Optional. The size of the icon. Default 64.
+        * @return string HTML markup for the icon.
+        */
+       private function get_theme_icon( $project, $size = 64 ) {
+               $default = '<div class="default-icon"><span class="dashicons dashicons-admin-themes"></span></div>';
+
+               $screenshot = gp_get_meta( 'wp-themes', $project->id, 'screenshot' );
+               if ( $screenshot ) {
+                       return sprintf(
+                               '<div class="icon"><img src="%s" alt="" width="%d" height="%d"></div>',
+                               esc_url( 'https://i0.wp.com/' . $screenshot . '?w=' . $size * 2 . '&strip=all' ),
+                               $size,
+                               $size
+                       );
+               }
+
+               return $default;
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesindexphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/index.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/index.php 2016-04-22 07:25:24 UTC (rev 2999)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/index.php       2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,45 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Index Route Class.
- *
- * Provides the route for translate.wordpress.org/.
- */
-class WPorg_GP_Route_Index extends GP_Route {
-
-       private $cache_group = 'wporg-translate';
-
-       /**
-        * Prints all exisiting locales as cards.
-        *
-        * Note: Cache gets refreshed via `WPorg_GP_CLI_Update_Caches`.
-        */
-       public function get_locales() {
-               $existing_locales = wp_cache_get( 'existing-locales', $this->cache_group );
-               if ( false === $existing_locales ) {
-                       $existing_locales = array();
-               }
-
-               $locales = array();
-               foreach ( $existing_locales as $locale ) {
-                       $locales[] = GP_Locales::by_slug( $locale );
-               }
-               usort( $locales, array( $this, '_sort_english_name_callback') );
-               unset( $existing_locales );
-
-               $contributors_count = wp_cache_get( 'contributors-count', $this->cache_group );
-               if ( false === $contributors_count ) {
-                       $contributors_count = array();
-               }
-
-               $translation_status = wp_cache_get( 'translation-status', $this->cache_group );
-               if ( false === $translation_status ) {
-                       $translation_status = array();
-               }
-
-               $this->tmpl( 'index-locales', get_defined_vars() );
-       }
-
-       private function _sort_english_name_callback( $a, $b ) {
-               return $a->english_name > $b->english_name;
-       }
-}
</del></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincrouteslocalephp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/locale.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/locale.php        2016-04-22 07:25:24 UTC (rev 2999)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/locale.php      2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,784 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-// wporg_get_plugin_icon()
-if ( file_exists( WPORGPATH . 'extend/plugins-plugins/_plugin-icons.php' ) ) {
-       include_once WPORGPATH . 'extend/plugins-plugins/_plugin-icons.php';
-}
-
-/**
- * Locale Route Class.
- *
- * Provides the route for translate.wordpress.org/locale/$locale.
- */
-class WPorg_GP_Route_Locale extends GP_Route {
-
-       /**
-        * Prints projects/translation sets of a top level project.
-        *
-        * @param string $locale_slug      Slug of the locale.
-        * @param string $set_slug         Slug of the translation set.
-        * @param string $project_path     Path of a project.
-        */
-       public function get_locale_projects( $locale_slug, $set_slug = 'default', $project_path = false ) {
-               global $wpdb;
-
-               $per_page = 20;
-               $page = (int) gp_get( 'page', 1 );
-               $search = gp_get( 's', '' );
-               $filter = gp_get( 'filter', false );
-
-               $locale = GP_Locales::by_slug( $locale_slug );
-               if ( ! $locale ) {
-                       return $this->die_with_404();
-               }
-
-               // Grab the top level projects to show in the menu first, so as to be able to handle the default Waiting / WP tab selection
-               $top_level_projects = $this->get_active_top_level_projects();
-               usort( $top_level_projects, array( $this, '_sort_reverse_name_callback' ) );
-
-               // Default to the Waiting or WordPress tabs
-               $default_project_tab = 'waiting';
-               $user_id = get_current_user_id();
-               if (
-                       ! is_user_logged_in() ||
-                       ! function_exists( 'wporg_gp_rosetta_roles' ) || // Rosetta Roles plugin is not enabled
-                       ! (
-                               wporg_gp_rosetta_roles()->is_global_administrator( $user_id ) || // Not a global admin
-                               wporg_gp_rosetta_roles()->is_approver_for_locale( $user_id, $locale_slug ) // Doesn't have project-level access either
-                       )
-                       // Add check to see if there are any waiting translations for this locale?
-                       ) {
-                       $default_project_tab = 'wp';
-               }
-
-               // Filter out the Waiting Tab if the current user cannot validate strings
-               if ( 'waiting' != $default_project_tab ) {
-                       foreach ( $top_level_projects as $i => $project ) {
-                               if ( 'waiting' == $project->slug ) {
-                                       unset( $top_level_projects[ $i ] );
-                                       break;
-                               }
-                       }
-               }
-
-               $project_path = $project_path ?: $default_project_tab;
-
-               $project = GP::$project->by_path( $project_path );
-               if ( ! $project ) {
-                       return $this->die_with_404();
-               }
-
-
-               $paged_sub_projects = $this->get_paged_active_sub_projects(
-                       $project,
-                       array(
-                               'page' => $page,
-                               'per_page' => $per_page,
-                               'search' => $search,
-                               'filter' => $filter,
-                               'set_slug' => $set_slug,
-                               'locale' => $locale_slug,
-                       )
-               );
-
-               if ( ! $paged_sub_projects ) {
-                       return $this->die_with_404();
-               }
-
-               $sub_projects = $paged_sub_projects['projects'];
-               $pages        = $paged_sub_projects['pages'];
-               $filter       = $paged_sub_projects['filter'];
-               unset( $paged_sub_projects );
-
-               $project_status = $project_icons = array();
-               foreach ( $sub_projects as $key => $sub_project ) {
-                       $project_status[ $sub_project->id ] = $this->get_project_status( $sub_project, $locale_slug, $set_slug );
-                       $project_icons[ $sub_project->id ] = $this->get_project_icon( $project, $sub_project );
-               }
-
-               $project_ids = array_keys( $project_status );
-               $project_ids[] = $project->id;
-               $project_ids = array_merge(
-                       $project_ids,
-                       $wpdb->get_col( "SELECT id FROM {$wpdb->gp_projects} WHERE parent_project_id IN(" . implode(', ', $project_ids  ) . ")" )
-               );
-
-               $contributors_count = wp_cache_get( 'contributors-count', 'wporg-translate' );
-               if ( false === $contributors_count ) {
-                       $contributors_count = array();
-               }
-
-               $variants = $this->get_locale_variants( $locale_slug, $project_ids );
-               // If there were no results for the current variant in the current project branch, it should still show it.
-               if ( ! in_array( $set_slug, $variants, true ) ) {
-                       $variants[] = $set_slug;
-               }
-
-               $this->tmpl( 'locale-projects', get_defined_vars() );
-       }
-
-       /**
-        * Prints projects/translation sets of a sub project.
-        *
-        * @param string $locale_slug      Slug of the locale.
-        * @param string $set_slug         Slug of the translation set.
-        * @param string $project_path     Path of a project.
-        * @param string $sub_project_path Path of a sub project.
-        */
-       public function get_locale_project( $locale_slug, $set_slug, $project_path, $sub_project_path ) {
-               $locale = GP_Locales::by_slug( $locale_slug );
-               if ( ! $locale ) {
-                       return $this->die_with_404();
-               }
-
-               $project = GP::$project->by_path( $project_path );
-               if ( ! $project ) {
-                       return $this->die_with_404();
-               }
-
-               $sub_project = GP::$project->by_path( $project_path . '/' . $sub_project_path );
-               if ( ! $sub_project ) {
-                       return $this->die_with_404();
-               }
-
-               $project_status = $this->get_project_status( $sub_project, $locale_slug, $set_slug );
-               $sub_project_status = $this->get_project_status( $sub_project, $locale_slug, $set_slug, null, false );
-
-               $project_icon = $this->get_project_icon( $project, $sub_project, 64 );
-
-               $contributors_count = wp_cache_get( 'contributors-count', 'wporg-translate' );
-               if ( false === $contributors_count ) {
-                       $contributors_count = array();
-               }
-
-               $sub_projects = $this->get_active_sub_projects( $sub_project, true );
-               $sub_project_slugs = array();
-               if ( $sub_projects ) {
-                       $sub_project_statuses = array();
-                       foreach ( $sub_projects as $key => $_sub_project ) {
-                               $sub_project_slugs[] = $_sub_project->slug;
-                               $status = $this->get_project_status( $_sub_project, $locale_slug, $set_slug, null, false );
-
-                               $sub_project_statuses[ $_sub_project->id ] = $status;
-                       }
-
-                       $variants = $this->get_locale_variants( $locale_slug, array_keys( $sub_project_statuses ) );
-               } else {
-                       $variants = $this->get_locale_variants( $locale_slug, array( $sub_project->id ) );
-               }
-
-               $locale_contributors = $this->get_locale_contributors( $sub_project, $locale_slug, $set_slug );
-
-               $this->tmpl( 'locale-project', get_defined_vars() );
-       }
-
-       /**
-        * Returns markup for project icons.
-        *
-        * @param GP_Project $project     A GlotPress project.
-        * @param GP_Project $sub_project A sub project of a GlotPress project.
-        * @param int        $size        Size of icon.
-        * @return string HTML markup of an icon.
-        */
-       private function get_project_icon( $project, $sub_project, $size = 128 ) {
-               // The Waiting tab will have $sub_project's which are not sub-projects of $project
-               if ( $sub_project->parent_project_id && $sub_project->parent_project_id !== $project->id ) {
-                       $project = GP::$project->get( $sub_project->parent_project_id );
-                       // In the case of Plugins, we may need to go up another level yet
-                       if ( $project->parent_project_id ) {
-                               $sub_project = $project;
-                               $project = GP::$project->get( $sub_project->parent_project_id );
-                       }
-               }
-
-               switch( $project->slug ) {
-                       case 'wp':
-                               return '<div class="wordpress-icon"><span class="dashicons dashicons-wordpress-alt"></span></div>';
-                       case 'meta':
-                               switch( $sub_project->slug ) {
-                                       case 'forums':
-                                               return '<div class="default-icon"><span class="dashicons dashicons-format-chat"></span></div>';
-                                       case 'rosetta':
-                                               return '<div class="default-icon"><span class="dashicons dashicons-admin-site"></span></div>';
-                                       case 'plugins':
-                                               return '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
-                                       case 'themes':
-                                               return '<div class="default-icon"><span class="dashicons dashicons-admin-appearance"></span></div>';
-                                       case 'wordcamp':
-                                               return '<div class="default-icon"><span class="dashicons dashicons-tickets"></span></div>';
-                                       case 'browsehappy':
-                                               return '<div class="icon"><img src="https://translate.wordpress.org/translate/gp-templates-new/images/browsehappy.png" width="' . $size . '" height="' . $size . '"></div>';
-                                       default:
-                                               return '<div class="default-icon"><span class="dashicons dashicons-networking"></span></div>';
-                               }
-                       case 'wp-themes':
-                               $screenshot = gp_get_meta( 'wp-themes', $sub_project->id, 'screenshot' );
-                               if ( $screenshot ) {
-                                       return '<div class="theme icon"><img src="https://i0.wp.com/' . $screenshot . '?w=' . ( $size * 2 ) . '&amp;strip=all" width="' . $size . '" height="' . $size . '"></div>';
-                               } else {
-                                       return '<div class="default-icon"><span class="dashicons dashicons-admin-appearance"></span></div>';
-                               }
-                       case 'bbpress':
-                       case 'buddypress':
-                               if ( function_exists( 'wporg_get_plugin_icon' ) ) {
-                                       $screenshot = wporg_get_plugin_icon( $project->slug, $size );
-                                       if ( $screenshot ) {
-                                               return $screenshot;
-                                       }
-                               }
-                               return '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
-                       case 'wp-plugins':
-                               if ( function_exists( 'wporg_get_plugin_icon' ) ) {
-                                       $screenshot = wporg_get_plugin_icon( $sub_project->slug, $size );
-                                       if ( $screenshot ) {
-                                               return $screenshot;
-                                       }
-                               }
-                               return '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
-                       case 'glotpress':
-                               return '<div class="icon"><img src="https://translate.wordpress.org/translate/gp-templates-new/images/glotpress.png" width="' . $size . '" height="' . $size . '"></div>';
-                       default:
-                               return '<div class="default-icon"><span class="dashicons dashicons-translation"></span></div>';
-               }
-       }
-
-       /**
-        * Retrieves non-default slugs of translation sets for a list of
-        * project IDs.
-        *
-        * @param string $locale     Slug of a GlotPress locale.
-        * @param array $project_ids List of project IDs.
-        * @return array List of non-default slugs.
-        */
-       private function get_locale_variants( $locale, $project_ids ) {
-               global $wpdb;
-
-               $project_ids = implode( ',', $project_ids );
-               $slugs = $wpdb->get_col( $wpdb->prepare( "
-                       SELECT DISTINCT slug
-                       FROM {$wpdb->gp_translation_sets}
-                       WHERE
-                               project_id IN( $project_ids )
-                               AND locale = %s
-               ", $locale ) );
-
-               return $slugs;
-       }
-
-       /**
-        * Retrieves contributors of a project.
-        *
-        * @param GP_Project $project     A GlotPress project.
-        * @param string     $locale_slug Slug of the locale.
-        * @param string     $set_slug    Slug of the translation set.
-        * @return array Contributors.
-        */
-       private function get_locale_contributors( $project, $locale_slug, $set_slug ) {
-               global $wpdb;
-
-               $locale_contributors = array(
-                       'editors'      => array(),
-                       'contributors' => array(),
-               );
-
-               // Get the translation editors of the project.
-               $editors = $wpdb->get_col( $wpdb->prepare( "
-                       SELECT
-                               `user_id`
-                       FROM {$wpdb->wporg_translation_editors}
-                       WHERE
-                               `project_id` = %d
-                               AND `locale` = %s
-               ", $project->id, $locale_slug ) );
-
-               // Get the names of the translation editors.
-               foreach ( $editors as $editor_id ) {
-                       $user = get_user_by( 'id', $editor_id );
-                       if ( ! $user ) {
-                               continue;
-                       }
-
-                       $locale_contributors['editors'][ $editor_id ] = (object) array(
-                               'nicename'     => $user->user_nicename,
-                               'display_name' => $this->_encode( $user->display_name ),
-                               'email'        => $user->user_email,
-                       );
-               }
-               unset( $editors );
-
-               // Get the contributors of the project.
-               $contributors = array();
-
-               // In case the project has a translation set, like /wp-themes/twentysixteen.
-               $translation_set = GP::$translation_set->by_project_id_slug_and_locale( $project->id, $set_slug, $locale_slug );
-               if ( $translation_set ) {
-                       $contributors = array_merge(
-                               $contributors,
-                               $this->get_locale_contributors_by_translation_set( $translation_set )
-                       );
-               }
-
-               // Check if the project has sub-projects, like /wp-plugins/wordpress-importer.
-               $sub_projects = $wpdb->get_col( $wpdb->prepare( "
-                       SELECT id
-                       FROM {$wpdb->gp_projects}
-                       WHERE
-                               parent_project_id = %d
-                               AND active = 1
-               ", $project->id ) );
-
-               foreach ( $sub_projects as $sub_project ) {
-                       $translation_set = GP::$translation_set->by_project_id_slug_and_locale( $sub_project, $set_slug, $locale_slug );
-                       if ( ! $translation_set ) {
-                               continue;
-                       }
-
-                       $contributors = array_merge(
-                               $contributors,
-                               $this->get_locale_contributors_by_translation_set( $translation_set )
-                       );
-               }
-
-               // Get the names of the contributors.
-               foreach ( $contributors as $contributor ) {
-                       if ( isset( $locale_contributors['editors'][ $contributor->user_id ] ) ) {
-                               continue;
-                       }
-
-
-                       if ( isset( $locale_contributors['contributors'][ $contributor->user_id ] ) ) {
-                               // Update last updated and counts per status.
-                               $locale_contributors['contributors'][ $contributor->user_id ]->last_update = max(
-                                       $locale_contributors['contributors'][ $contributor->user_id ]->last_update,
-                                       $contributor->last_update
-                               );
-
-                               $locale_contributors['contributors'][ $contributor->user_id ]->total_count   += $contributor->total_count;
-                               $locale_contributors['contributors'][ $contributor->user_id ]->current_count += $contributor->current_count;
-                               $locale_contributors['contributors'][ $contributor->user_id ]->waiting_count += $contributor->waiting_count;
-                               $locale_contributors['contributors'][ $contributor->user_id ]->fuzzy_count   += $contributor->fuzzy_count;
-                               continue;
-                       }
-
-                       $user = get_user_by( 'id', $contributor->user_id );
-                       if ( ! $user ) {
-                               continue;
-                       }
-
-                       $locale_contributors['contributors'][ $contributor->user_id ] = (object) array(
-                               'nicename'      => $user->user_nicename,
-                               'display_name'  => $this->_encode( $user->display_name ),
-                               'email'         => $user->user_email,
-                               'last_update'   => $contributor->last_update,
-                               'total_count'   => $contributor->total_count,
-                               'current_count' => $contributor->current_count,
-                               'waiting_count' => $contributor->waiting_count,
-                               'fuzzy_count'   => $contributor->fuzzy_count,
-                       );
-               }
-               unset( $contributors );
-
-               uasort( $locale_contributors['contributors'], array( $this, '_sort_contributors_by_total_count_callback' ) );
-
-               return $locale_contributors;
-       }
-
-       /**
-        * Retrieves contributors of a translation set.
-        *
-        * @param GP_Translation_Set $translation_set A translation set.
-        * @return array List of user IDs.
-        */
-       private function get_locale_contributors_by_translation_set( $translation_set ) {
-               global $wpdb;
-
-               $contributors = $wpdb->get_results( $wpdb->prepare( "
-                       SELECT
-                               `user_id`,
-                               MAX( `date_added` ) AS `last_update`,
-                               COUNT( * ) as `total_count`,
-                               COUNT( CASE WHEN `status` = 'current' THEN `status` END ) AS `current_count`,
-                               COUNT( CASE WHEN `status` = 'waiting' THEN `status` END ) AS `waiting_count`,
-                               COUNT( CASE WHEN `status` = 'fuzzy' THEN `status` END ) AS `fuzzy_count`
-                       FROM `{$wpdb->gp_translations}`
-                       WHERE
-                               `translation_set_id` = %d
-                               AND `user_id` IS NOT NULL AND `user_id` != 0
-                               AND `status` IN( 'current', 'waiting', 'fuzzy' )
-                               AND `date_modified` > %s
-                       GROUP BY `user_id`
-               ", $translation_set->id, date( 'Y-m-d', time() - YEAR_IN_SECONDS ) ) );
-
-               return $contributors;
-       }
-
-       /**
-        * Calculates the status of a project.
-        *
-        * @param GP_Project $project           The GlotPress project.
-        * @param string     $locale            Slug of GlotPress locale.
-        * @param string     $set_slug          Slug of the translation set.
-        * @param object     $status            The status object.
-        * @param bool       $calc_sub_projects Whether sub projects should be calculated too.
-        *                                      Default true.
-        * @return object The status of a project.
-        */
-       private function get_project_status( $project, $locale, $set_slug, $status = null, $calc_sub_projects = true ) {
-               if ( null === $status ) {
-                       $status = new stdClass;
-                       $status->sub_projects_count = 0;
-                       $status->waiting_count      = 0;
-                       $status->current_count      = 0;
-                       $status->fuzzy_count        = 0;
-                       $status->all_count          = 0;
-                       $status->percent_complete   = 0;
-               }
-
-               $set = GP::$translation_set->by_project_id_slug_and_locale(
-                       $project->id,
-                       $set_slug,
-                       $locale
-               );
-
-               if ( $set ) {
-                       $status->sub_projects_count += 1;
-                       $status->waiting_count      += (int) $set->waiting_count();
-                       $status->current_count      += (int) $set->current_count();
-                       $status->fuzzy_count        += (int) $set->fuzzy_count();
-                       $status->all_count          += (int) $set->all_count();
-
-                       if ( $status->all_count ) {
-                               /*
-                                * > 50% round down, so that a project with all strings except 1 translated shows 99%, instead of 100%.
-                                * < 50% round up, so that a project with just a few strings shows 1%, instead of 0%.
-                                */
-                               $percent_complete = ( $status->current_count / $status->all_count * 100 );
-                               $status->percent_complete = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
-                       }
-               }
-
-               if ( $calc_sub_projects ) {
-                       $sub_projects = $this->get_active_sub_projects( $project, true );
-                       if ( $sub_projects ) {
-                               foreach ( $sub_projects as $sub_project ) {
-                                       $this->get_project_status( $sub_project, $locale, $set_slug, $status, false );
-                               }
-                       }
-               }
-
-               return $status;
-       }
-
-       /**
-        * Retrieves active sub projects.
-        *
-        * @param  GP_Project $project           The parent project
-        * @param  bool       $with_sub_projects Whether sub projects should be fetched too.
-        *                                       Default false.
-        * @return array List of sub projects.
-        */
-       private function get_active_sub_projects( $project, $with_sub_projects = false ) {
-               global $wpdb;
-
-               $_projects = $project->many( "
-                       SELECT *
-                       FROM {$wpdb->gp_projects}
-                       WHERE
-                               parent_project_id = %d
-                               AND active = 1
-                       ORDER BY id ASC
-               ", $project->id );
-
-               $projects = array();
-               foreach ( $_projects as $project ) {
-                       $projects[ $project->id ] = $project;
-
-                       if ( $with_sub_projects ) {
-                               // e.g. wp/dev/admin/network
-                               $sub_projects = $project->many( "
-                                       SELECT *
-                                       FROM {$wpdb->gp_projects}
-                                       WHERE
-                                               parent_project_id = %d
-                                               AND active = 1
-                                       ORDER BY id ASC
-                               ", $project->id );
-
-                               foreach ( $sub_projects as $sub_project ) {
-                                       $projects[ $sub_project->id ] = $sub_project;
-                               }
-                               unset( $sub_projects);
-                       }
-               }
-               unset( $_projects );
-
-               return $projects;
-       }
-
-       /**
-        * Retrieves active sub projects with paging.
-        *
-        * This method is horribly inefficient when there exists many sub-projects, as it can't use SQL.
-        *
-        * @param GP_Project $project           The parent project
-        * @param array $args {
-        *      @type int    $per_page Number of items per page. Default 20
-        *      @type int    $page     The page of results to view. Default 1.
-        *      @type string $orderby  The field to order by, id or name. Default id.
-        *      @type string $order    The sorting order, ASC or DESC. Default ASC.
-        *      @type string $search   The search string
-        *      @type string $set_slug The translation set to view.
-        *      @type string $locale   The locale of the translation set to view.
-        * }
-        * @return array List of sub projects.
-        */
-       private function get_paged_active_sub_projects( $project, $args = array() ) {
-               global $wpdb;
-
-               $defaults = array(
-                       'per_page' => 20,
-                       'page'     => 1,
-                       'search'   => false,
-                       'set_slug' => '',
-                       'locale'   => '',
-                       'filter'   => false,
-               );
-               $r = wp_parse_args( $args, $defaults );
-               extract( $r, EXTR_SKIP );
-
-               $limit_sql = '';
-               if ( $per_page ) {
-                       $limit_sql = $wpdb->prepare( 'LIMIT %d, %d', ( $page - 1 ) * $per_page, $per_page );
-               }
-
-               $parent_project_sql = $wpdb->prepare( 'AND tp.parent_project_id = %d', $project->id );
-
-               $search_sql = '';
-               if ( $search ) {
-                       $esc_search = '%%' . like_escape( $search ) . '%%';
-                       $search_sql = $wpdb->prepare( 'AND ( tp.name LIKE %s OR tp.slug LIKE %s )', $esc_search, $esc_search );
-               }
-
-               // Special Waiting Project Tab
-               // This removes the parent_project_id restriction and replaces it with all-translation-editer-projects
-               if ( 'waiting' == $project->slug && is_user_logged_in() && function_exists( 'wporg_gp_rosetta_roles' ) ) {
-
-                       if ( ! $filter ) {
-                               $filter = 'strings-waiting-and-fuzzy';
-                       }
-
-                       $user_id = get_current_user_id();
-
-                       // Global Admin or Locale-specific admin
-                       $can_approve_for_all = wporg_gp_rosetta_roles()->is_global_administrator( $user_id );
-
-                       // Check to see if they have any special approval permissions
-                       $allowed_projects = array();
-                       if ( ! $can_approve_for_all && wporg_gp_rosetta_roles()->is_approver_for_locale( $user_id, $locale ) ) {
-                               $allowed_projects = wporg_gp_rosetta_roles()->get_project_id_access_list( $user_id, $locale, true );
-
-                               // Check to see if they can approve for all projects in this locale.
-                               if ( in_array( 'all', $allowed_projects ) ) {
-                                       $can_approve_for_all = true;
-                                       $allowed_projects = array();
-                               }
-                       }
-
-                       $parent_project_sql = '';
-                       if ( $can_approve_for_all ) {
-                               // The current user can approve for all projects, so just grab all with any waiting strings.
-                               $parent_project_sql = 'AND ( stats.waiting > 0 OR stats.fuzzy > 0 )';
-
-                       } elseif ( $allowed_projects ) {
-                               // The current user can approve for a small set of projects.
-                               // We only need to check against tp.id and not tp_sub.id in this case as we've overriding the parent_project_id check
-                               $ids = implode( ', ', array_map( 'intval', $allowed_projects ) );
-                               $parent_project_sql = "AND tp.id IN( $ids ) AND stats.waiting > 0";
-
-                       } else {
-                               // The current user can't approve for any locale projects, or is logged out.
-                               $parent_project_sql = 'AND 0=1';
-
-                       }
-
-                       // Limit to only showing base-level projects
-                       $parent_project_sql .= " AND tp.parent_project_id IN( (SELECT id FROM {$wpdb->gp_projects} WHERE parent_project_id IS NULL AND active = 1) )";
-
-               }
-
-               $filter_order_by = $filter_where = '';
-               $sort_order = 'DESC';
-               $filter_name = $filter;
-               if ( $filter && '-asc' == substr( $filter, -4 ) ) {
-                       $sort_order = 'ASC';
-                       $filter_name = substr( $filter, 0, -4 );
-               }
-               switch ( $filter_name ) {
-                       default:
-                       case 'special':
-                               // Float favorites to the start, but only if they have untranslated strings
-                               $user_fav_projects = array_map( 'esc_sql', $this->get_user_favorites( $project->slug ) );
-
-                               // Float Favorites to the start, float fully translated to the bottom, order the rest by name
-                               if ( $user_fav_projects ) {
-                                       $filter_order_by = 'FIELD( tp.path, "' . implode( '", "', $user_fav_projects ) . '" ) > 0 AND stats.untranslated > 0 DESC, stats.untranslated > 0 DESC, stats.untranslated DESC, tp.name ASC';
-                               } else {
-                                       $filter_order_by = 'stats.untranslated > 0 DESC, stats.untranslated DESC, tp.name ASC';
-                               }
-                               break;
-
-                       case 'favorites':
-                               // Only list favorites
-                               $user_fav_projects = array_map( 'esc_sql', $this->get_user_favorites( $project->slug ) );
-
-                               if ( $user_fav_projects ) {
-                                       $filter_where = 'AND tp.path IN( "' . implode( '", "', $user_fav_projects ) . '" )';
-                               } else {
-                                       $filter_where = 'AND 0=1';
-                               }
-                               $filter_order_by = 'stats.untranslated > 0 DESC, tp.name ASC';
-
-                               break;
-
-                       case 'strings-remaining':
-                               $filter_where = 'AND stats.untranslated > 0';
-                               $filter_order_by = "stats.untranslated $sort_order, tp.name ASC";
-                               break;
-
-                       case 'strings-waiting-and-fuzzy':
-                               $filter_where = 'AND (stats.waiting > 0 OR stats.fuzzy > 0 )';
-                               $filter_order_by = "tp.path LIKE 'wp/%%' AND (stats.fuzzy + stats.waiting) > 0 DESC, (stats.fuzzy + stats.waiting) $sort_order, tp.name ASC";
-                               break;
-
-                       case 'percent-completed':
-                               $filter_where = 'AND stats.untranslated > 0';
-                               $filter_order_by = "( stats.current / stats.all ) $sort_order, tp.name ASC";
-                               break;
-               }
-
-               /*
-                * Find all child projects with translation sets that match the current locale/slug.
-                *
-                * 1. We need to fetch all sub-projects of the current project (so, if we're at wp-plugins, we want akismet, debug bar, importers, etc)
-                * 2. Next, we fetch the sub-projects of those sub-projects, that gets us things like Development, Readme, etc.
-                * 3. Next, we fetch the translation sets of both the sub-projects(1), and any sub-sub-projects(2).
-                * Once we have the sets in 3, we can then check to see if there exists any translation sets for the current (locale, slug) (ie. en-au/default)
-                * If not, we can simply filter them out, so that paging only has items returned that actually exist.
-                */
-               $_projects = $project->many( "
-                       SELECT SQL_CALC_FOUND_ROWS tp.*
-                       FROM {$wpdb->gp_projects} tp
-                               LEFT JOIN {$wpdb->project_translation_status} stats ON stats.project_id = tp.id AND stats.locale = %s AND stats.locale_slug = %s
-                       WHERE
-                               tp.active = 1
-                               $parent_project_sql
-                               $search_sql
-                               $filter_where
-                       GROUP BY tp.id
-                       ORDER BY $filter_order_by
-                       $limit_sql
-               ", $locale, $set_slug );
-
-               $results = (int) $project->found_rows();
-               $pages = (int) ceil( $results / $per_page );
-
-               $projects = array();
-               foreach ( $_projects as $project ) {
-                       $projects[ $project->id ] = $project;
-               }
-
-               return array(
-                       'pages' => compact( 'pages', 'page', 'per_page', 'results' ),
-                       'projects' => $projects,
-                       'filter' => $filter,
-               );
-       }
-
-       /**
-        * Retrieves a list of projects which the current user has favorited.
-        *
-        * @return array List of favorited items, eg [ 'wp-themes/twentyten', 'wp-themes/twentyeleven' ]
-        */
-       function get_user_favorites( $project_slug = false ) {
-               global $wpdb;
-
-               if ( ! is_user_logged_in() ) {
-                       return array();
-               }
-
-               $user_id = get_current_user_id();
-
-               switch ( $project_slug ) {
-                       default:
-                               // Fall through to include both Themes and Plugins
-                       case 'wp-themes':
-                               // Theme favorites are stored as theme slugs, these map 1:1 to GlotPress projects
-                               $theme_favorites = array_map( function( $slug ) {
-                                       return "wp-themes/$slug";
-                               }, (array) get_user_meta( $user_id, 'theme_favorites', true ) );
-
-                               if ( 'wp-themes' === $project_slug ) {
-                                       return $theme_favorites;
-                               }
-
-                       case 'wp-plugins':
-                               // Plugin favorites are stored as topic ID's
-                               $plugin_fav_ids = array_keys( (array) get_user_meta( $user_id, PLUGINS_TABLE_PREFIX . 'plugin_favorite', true ) );
-                               $plugin_fav_slugs = array();
-                               if ( $plugin_fav_ids ) {
-                                       $plugin_fav_ids = implode( ',', array_map( 'intval', $plugin_fav_ids ) );
-                                       $plugin_fav_slugs = $wpdb->get_col( "SELECT topic_slug FROM " . PLUGINS_TABLE_PREFIX . "topics WHERE topic_id IN( $plugin_fav_ids )" );
-                               }
-
-                               $plugin_favorites = array_map( function( $slug ) {
-                                       return "wp-plugins/$slug";
-                               }, $plugin_fav_slugs );
-
-                               if ( 'wp-plugins' === $project_slug ) {
-                                       return $plugin_favorites;
-                               }
-               }
-
-               // Return all favorites, for uses in things like the Waiting tab
-               return array_merge( $theme_favorites, $plugin_favorites );
-       }
-
-       /**
-        * Retrieves active top level projects.
-        *
-        * @return array List of top level projects.
-        */
-       public function get_active_top_level_projects() {
-               global $wpdb;
-
-               return GP::$project->many( "
-                       SELECT *
-                       FROM {$wpdb->gp_projects}
-                       WHERE
-                               parent_project_id IS NULL
-                               AND active = 1
-                       ORDER BY name ASC
-               " );
-       }
-
-       private function _sort_contributors_by_total_count_callback( $a, $b ) {
-               return $a->total_count < $b->total_count;
-       }
-
-       private function _sort_reverse_name_callback( $a, $b ) {
-               // The Waiting project should always be first.
-               if ( $a->slug == 'waiting' ) {
-                       return -1;
-               }
-               return - strcasecmp( $a->name, $b->name );
-       }
-
-       private function _sort_name_callback( $a, $b ) {
-               return strcasecmp( $a->name, $b->name );
-       }
-
-       private function _encode( $raw ) {
-               $raw = mb_convert_encoding( $raw, 'UTF-8', 'ASCII, JIS, UTF-8, Windows-1252, ISO-8859-1' );
-               return ent2ncr( htmlspecialchars_decode( htmlentities( $raw, ENT_NOQUOTES, 'UTF-8' ), ENT_NOQUOTES ) );
-       }
-}
</del></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesredirectorphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/redirector.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/redirector.php    2016-04-22 07:25:24 UTC (rev 2999)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/redirector.php  2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,20 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Redirector Route Class.
- *
- * Provides redirection routes.
- */
-class WPorg_GP_Route_Redirector extends GP_Route {
-
-       public function redirect_languages( $path = '' ) {
-               if ( empty( $path ) ) {
-                       $this->redirect( '/' );
-               } else {
-                       $this->redirect( "/locale/$path" );
-               }
-       }
-
-       public function redirect_index() {
-               $this->redirect( '/' );
-       }
-}
</del></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincroutesstatsoverviewphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/stats-overview.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/stats-overview.php        2016-04-22 07:25:24 UTC (rev 2999)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/stats-overview.php      2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,130 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Stats Route Class.
- *
- * Provides the route for translate.wordpress.org/stats.
- */
-class WPorg_GP_Route_Stats extends GP_Route {
-
-       public function get_stats_overview() {
-               global $wpdb;
-
-               $projects = array(
-                       'meta/rosetta' => false,
-                       'meta/browsehappy' => false,
-                       'meta/themes' => false,
-                       'meta/plugins' => false,
-                       'meta/forums' => false,
-                       'apps/android' => false,
-                       'apps/ios' => false,
-                       'waiting' => false,
-               );
-
-               // I'm sure there's somewhere to fetch these from statically defined
-               $wp_project = GP::$project->by_path('wp');
-               foreach ( GP::$project->find_many( array( 'parent_project_id' => $wp_project->id, 'active' => 1 ), 'name ASC' ) as $wp_sub_project ) {
-                       // Prefix the WordPress projects...
-                       $wp_sub_project->name = $wp_project->name . ' ' . $wp_sub_project->name;
-                       $projects = array_merge( array( $wp_sub_project->path => $wp_sub_project ), $projects );
-               }
-
-               // Load the projects for each display item
-               array_walk( $projects, function( &$project, $project_slug ) {
-                       if ( ! $project ) {
-                               $project = GP::$project->by_path( $project_slug );
-                       }
-               } );
-
-               $all_project_paths_sql = '"' . implode( '", "', array_keys( $projects ) ) . '"';
-               $sql = "SELECT
-                               path, locale, locale_slug,
-                               (100 * stats.current/stats.all) as percent_complete,
-                               stats.waiting+stats.fuzzy as waiting_strings
-                       FROM {$wpdb->project_translation_status} stats
-                               LEFT JOIN {$wpdb->gp_projects} p ON stats.project_id = p.id
-                       WHERE
-                               p.path IN ( $all_project_paths_sql )
-                               AND p.active = 1";
-
-               $rows = $wpdb->get_results( $sql );
-
-               // Split out into $[Locale][Project] = %
-               $translation_locale_statuses = array();
-               foreach ( $rows as $set ) {
-                       $locale_key = $set->locale;
-                       if ( 'default' != $set->locale_slug ) {
-                               $locale_key = $set->locale . '/' . $set->locale_slug;
-                       }
-
-                       /*
-                        * > 50% round down, so that a project with all strings except 1 translated shows 99%, instead of 100%.
-                        * < 50% round up, so that a project with just a few strings shows 1%, instead of 0%.
-                        */
-                       $percent_complete = (float) $set->percent_complete;
-                       $percent_complete = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
-                       $translation_locale_statuses[ $locale_key ][ $set->path ] = $percent_complete;
-
-                       if ( ! isset( $translation_locale_statuses[ $locale_key ]['waiting'] ) ) {
-                               $translation_locale_statuses[ $locale_key ]['waiting'] = 0;
-                       }
-                       $translation_locale_statuses[ $locale_key ]['waiting'] += (int) $set->waiting_strings;
-               }
-               unset( $rows, $locale_key, $set );
-
-               // Append the Plugins/Themes waiting strings
-               $parent_project_ids = implode(',', array(
-                       GP::$project->by_path( 'wp-plugins' )->id,
-                       GP::$project->by_path( 'wp-themes' )->id
-               ) );
-               $sql = "SELECT
-                               locale, locale_slug,
-                               SUM( stats.waiting ) + SUM( stats.fuzzy ) as waiting_strings
-                       FROM {$wpdb->project_translation_status} stats
-                               LEFT JOIN {$wpdb->gp_projects} p ON stats.project_id = p.id
-                       WHERE
-                               p.parent_project_id IN ( $parent_project_ids )
-                               AND p.active = 1
-                       GROUP BY locale, locale_slug";
-
-               $rows = $wpdb->get_results( $sql );
-               foreach ( $rows as $set ) {
-                       $locale_key = $set->locale;
-                       if ( 'default' != $set->locale_slug ) {
-                               $locale_key = $set->locale . '/' . $set->locale_slug;
-                       }
-
-                       $translation_locale_statuses[ $locale_key ]['waiting'] += (int) $set->waiting_strings;
-               }
-
-               // Calculate a list of [Locale] = % subtotals
-               $translation_locale_complete = array();
-               foreach ( $translation_locale_statuses as $locale => $sets ) {
-                       unset( $sets['waiting'] );
-                       $sets_count = count( $sets );
-                       if ( $sets_count ) {
-                               $translation_locale_complete[ $locale ] = round( array_sum( $sets ) / $sets_count, 3 );
-                       } else {
-                               $translation_locale_complete[ $locale ] = 0;
-                       }
-               }
-               unset( $locale, $sets );
-
-               // Sort by translation completeness, least number of waiting strings, and locale slug.
-               uksort( $translation_locale_complete, function ( $a, $b ) use ( $translation_locale_complete, $translation_locale_statuses ) {
-                       if ( $translation_locale_complete[ $a ] < $translation_locale_complete[ $b ] ) {
-                               return 1;
-                       } elseif ( $translation_locale_complete[ $a ] == $translation_locale_complete[ $b ] ) {
-                               if ( $translation_locale_statuses[ $a ]['waiting'] != $translation_locale_statuses[ $b ]['waiting'] ) {
-                                       return strnatcmp( $translation_locale_statuses[ $a ]['waiting'], $translation_locale_statuses[ $b ]['waiting'] );
-                               } else {
-                                       return strnatcmp( $a, $b );
-                               }
-                       } else {
-                               return -1;
-                       }
-               } );
-
-               $this->tmpl( 'stats-overview', get_defined_vars() );
-       }
-
-}
</del></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincrouteswpdirectoryphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/wp-directory.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/wp-directory.php  2016-04-22 07:25:24 UTC (rev 2999)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/wp-directory.php        2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,238 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-class WPorg_GP_Route_WP_Directory extends GP_Route {
-
-       /**
-        * Prints stats about contributors of a specific project.
-        *
-        * @param GP_Project $project The project.
-        * @return array|false False if project not found, otherwise array with contributors.
-        */
-       public function get_contributors( $project ) {
-               global $wpdb;
-
-               $contributors_by_locale = array();
-               $default_value = array(
-                       'count' => 0,
-                       'editors' => array(),
-                       'contributors' => array(),
-               );
-
-               $translation_editors = $wpdb->get_results( $wpdb->prepare( "
-                       SELECT
-                               `user_id`, `locale`
-                       FROM {$wpdb->wporg_translation_editors}
-                       WHERE `project_id` = %d
-               ", $project->id ), OBJECT );
-
-               foreach ( $translation_editors as $translation_editor ) {
-                       if ( ! isset( $contributors_by_locale[ $translation_editor->locale ] ) ) {
-                               $contributors_by_locale[ $translation_editor->locale ] = $default_value;
-                       }
-
-                       $user = get_user_by( 'id', $translation_editor->user_id );
-                       if ( ! $user ) {
-                               continue;
-                       }
-
-                       $contributors_by_locale[ $translation_editor->locale ]['editors'][ $translation_editor->user_id ] = (object) array(
-                               'nicename'     => $user->user_nicename,
-                               'display_name' => $this->_encode( $user->display_name ),
-                       );
-
-                       $contributors_by_locale[ $translation_editor->locale ]['count']++;
-               }
-
-               unset( $translation_editors );
-
-               foreach( $this->get_translation_contributors_by_locale( $project->id ) as $row ) {
-                       if ( ! isset( $contributors_by_locale[ $row->locale ] ) ) {
-                               $contributors_by_locale[ $row->locale ] = $default_value;
-                       }
-
-                       if ( isset( $contributors_by_locale[ $row->locale ]['editors'][ $row->user_id ] ) ) {
-                               continue;
-                       }
-
-                       if ( isset( $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] ) ) {
-                               continue;
-                       }
-
-                       $user = get_user_by( 'id', $row->user_id );
-                       if ( ! $user ) {
-                               continue;
-                       }
-
-                       $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] = (object) array(
-                               'nicename'     => $user->user_nicename,
-                               'display_name' => $this->_encode( $user->display_name ),
-                       );
-
-                       $contributors_by_locale[ $row->locale ]['count']++;
-               }
-
-               $sub_projects = $wpdb->get_col( $wpdb->prepare( "
-                       SELECT id
-                       FROM {$wpdb->gp_projects}
-                       WHERE parent_project_id = %d
-               ", $project->id ) );
-
-               foreach ( $sub_projects as $sub_project ) {
-                       foreach( $this->get_translation_contributors_by_locale( $sub_project ) as $row ) {
-                               if ( ! isset( $contributors_by_locale[ $row->locale ] ) ) {
-                                       $contributors_by_locale[ $row->locale ] = $default_value;
-                               }
-
-                               if ( isset( $contributors_by_locale[ $row->locale ]['editors'][ $row->user_id ] ) ) {
-                                       continue;
-                               }
-
-                               if ( isset( $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] ) ) {
-                                       continue;
-                               }
-
-                               $user = get_user_by( 'id', $row->user_id );
-                               if ( ! $user ) {
-                                       continue;
-                               }
-
-                               $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] = (object) array(
-                                       'nicename'     => $user->user_nicename,
-                                       'display_name' => $this->_encode( $user->display_name ),
-                               );
-
-                               $contributors_by_locale[ $row->locale ]['count']++;
-                       }
-               }
-
-               return $contributors_by_locale;
-       }
-
-       /**
-        * Generates the chart data for contributors activity.
-        *
-        * @param GP_Project $project_id The project.
-        * @return array The data to build a chart via Chartist.js.
-        */
-       protected function get_contributors_chart_data( $project ) {
-               global $wpdb;
-
-               $sub_projects = $wpdb->get_col( $wpdb->prepare( "
-                       SELECT id
-                       FROM {$wpdb->gp_projects}
-                       WHERE parent_project_id = %d
-               ", $project->id ) );
-
-               $project_ids = array_merge( array( $project->id ), $sub_projects );
-               $translation_set_ids = $wpdb->get_col( "
-                       SELECT `id` FROM {$wpdb->gp_translation_sets} WHERE `project_id` IN (" . implode( ',', $project_ids ) . ")
-               " );
-
-               if ( ! $translation_set_ids ) {
-                       return array();
-               }
-
-               $date_begin = new DateTime( '-6 day' );
-               $date_end = new DateTime( 'NOW' );
-               $date_interval = new DateInterval( 'P1D' );
-               $date_range = new DatePeriod( $date_begin, $date_interval, $date_end );
-
-               $days = array();
-               foreach( $date_range as $date ) {
-                       $days[] = $date->format( 'Y-m-d' );
-               }
-               $days[] = $date_end->format( 'Y-m-d' );
-
-               $counts = $wpdb->get_results( "
-                       SELECT
-                               DATE(date_modified) AS `day`, COUNT(*) AS `count`, `status`
-                       FROM {$wpdb->gp_translations}
-                       WHERE
-                               `translation_set_id` IN (" . implode( ',', $translation_set_ids ) . ")
-                               AND date_modified >= ( CURDATE() - INTERVAL 7 DAY )
-                       GROUP BY `status`, `day`
-                       ORDER BY `day` DESC
-               " );
-
-               $status = array( 'current', 'waiting', 'rejected' );
-               $data = [];
-               foreach ( $days as $day ) {
-                       $data[ $day ] = array_fill_keys( $status, 0 );
-                       foreach ( $counts as $count ) {
-                               if ( $count->day !== $day || ! in_array( $count->status, $status ) ) {
-                                       continue;
-                               }
-
-                               $data[ $day ][ $count->status ] = (int) $count->count;
-                       }
-               }
-
-               $labels = array_keys( $data );
-               array_pop( $labels );
-               $labels[] = ''; // Don't show a label for today
-
-               $series = array();
-               $series_data = array_values( $data );
-               foreach ( $status as $stati ) {
-                       $series[] = (object) array(
-                               'name' => $stati,
-                               'data' => wp_list_pluck( $series_data, $stati ),
-                       );
-               }
-
-               $chart_data = compact( 'labels', 'series' );
-
-               return $chart_data;
-       }
-
-       /**
-        * Prints stats about language packs of a specific project.
-        *
-        * @param string $type Type of the language pack, plugin or theme.
-        * @param string $slug Slug of a project.
-        */
-       public function get_language_packs( $type, $slug ) {
-               $http_context = stream_context_create( array(
-                       'http' => array(
-                               'user_agent' => 'WordPress.org Translate',
-                       ),
-               ) );
-               if ( 'plugin' === $type ) {
-                       $type = 'plugins';
-               } else {
-                       $type = 'themes';
-               }
-               $json = file_get_contents( "https://api.wordpress.org/translations/$type/1.0/?slug={$slug}", null, $http_context );
-               $language_packs = $json && '{' == $json[0] ? json_decode( $json ) : null;
-
-               return $language_packs;
-       }
-
-       /**
-        * Retrieves translators of a specific project.
-        *
-        * @param int $project_id Project ID.
-        * @return object Translators of the project.
-        */
-       private function get_translation_contributors_by_locale( $project_id ) {
-               global $wpdb;
-
-               $sql = $wpdb->prepare( "
-                       SELECT ts.`locale`, ts.`slug` AS `locale_slug`, t.`user_id`
-                       FROM `{$wpdb->gp_translations}` t, `{$wpdb->gp_translation_sets}` ts
-                       WHERE t.`translation_set_id` = ts.`id`
-                           AND t.`user_id` IS NOT NULL AND t.`user_id` != 0
-                           AND t.`date_modified` > %s
-                           AND ts.`project_id` = %d
-                           AND t.`status` <> 'rejected'
-                       GROUP BY ts.`locale`, ts.`slug`, t.`user_id`
-               ", date( 'Y-m-d', time() - YEAR_IN_SECONDS ), $project_id );
-
-               return $wpdb->get_results( $sql );
-       }
-
-       private function _encode( $raw ) {
-               $raw = mb_convert_encoding( $raw, 'UTF-8', 'ASCII, JIS, UTF-8, Windows-1252, ISO-8859-1' );
-               return ent2ncr( htmlspecialchars_decode( htmlentities( $raw, ENT_NOQUOTES, 'UTF-8' ), ENT_NOQUOTES ) );
-       }
-}
</del></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincrouteswppluginsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/wp-plugins.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/wp-plugins.php    2016-04-22 07:25:24 UTC (rev 2999)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/wp-plugins.php  2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,181 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-class WPorg_GP_Route_WP_Plugins extends WPorg_GP_Route_WP_Directory {
-
-       /**
-        * Prints stats about sub-project of a specific project.
-        *
-        * @param string $project_slug Slug of a project.
-        */
-       public function get_plugin_projects( $project_slug ) {
-               global $wpdb;
-
-               $project_path = 'wp-plugins/' . $project_slug;
-               $project = GP::$project->by_path( $project_path );
-               if ( ! $project ) {
-                       return $this->die_with_404();
-               }
-
-               $rows = $wpdb->get_results( "
-                       SELECT
-                               path, locale, locale_slug,
-                               (100 * stats.current/stats.all) as percent_complete,
-                               stats.waiting+stats.fuzzy as waiting_strings,
-                               stats.untranslated as untranslated
-                       FROM {$wpdb->project_translation_status} stats
-                               LEFT JOIN {$wpdb->gp_projects} p ON stats.project_id = p.id
-                       WHERE
-                               p.parent_project_id = '{$project->id}'
-               " );
-
-               // Split out into $[Locale][Project] = %
-               $translation_locale_statuses = array();
-               $sub_projects = array();
-               foreach ( $rows as $set ) {
-
-                       // Find unique locale key.
-                       $locale_key = $set->locale;
-                       if ( 'default' != $set->locale_slug ) {
-                               $locale_key = $set->locale . '/' . $set->locale_slug;
-                       }
-                       $sub_project = str_replace( "$project_path/", '', $set->path );
-                       $sub_projects[ $sub_project ] = true;
-
-                       /*
-                        * > 50% round down, so that a project with all strings except 1 translated shows 99%, instead of 100%.
-                        * < 50% round up, so that a project with just a few strings shows 1%, instead of 0%.
-                        */
-                       $percent_complete = (float) $set->percent_complete;
-                       $translation_locale_statuses[ $locale_key ][ $sub_project ] = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
-
-                       // Increment the amount of waiting and untranslated strings.
-                       if ( ! isset( $translation_locale_statuses[ $locale_key ]['waiting'] ) ) {
-                               $translation_locale_statuses[ $locale_key ]['waiting'] = 0;
-                       }
-                       if ( ! isset( $translation_locale_statuses[ $locale_key ]['untranslated'] ) ) {
-                               $translation_locale_statuses[ $locale_key ]['untranslated'] = 0;
-                       }
-                       $translation_locale_statuses[ $locale_key ]['waiting'] += (int) $set->waiting_strings;
-                       $translation_locale_statuses[ $locale_key ]['untranslated'] += (int) $set->untranslated;
-
-
-                       ksort( $translation_locale_statuses[ $locale_key ], SORT_NATURAL );
-               }
-
-               // Check if the plugin has at least one code project. These won't be created if a plugin
-               // has no text domain defined.
-               $sub_projects = array_keys( $sub_projects );
-               $has_error = ( ! in_array( 'dev', $sub_projects ) && ! in_array( 'stable', $sub_projects ) );
-
-               unset( $project_path, $locale_key, $rows, $set, $sub_project, $sub_projects );
-
-               // Calculate a list of [Locale] = % subtotals
-               $translation_locale_complete = array();
-               foreach ( $translation_locale_statuses as $locale => $sets ) {
-                       unset( $sets['waiting'], $sets['untranslated'] );
-                       $translation_locale_complete[ $locale ] = round( array_sum( $sets ) / count( $sets ), 3 );
-               }
-               unset( $locale, $sets );
-
-
-               // Sort by translation completeness, least number of waiting strings, and locale slug.
-               uksort( $translation_locale_complete, function ( $a, $b ) use ( $translation_locale_complete, $translation_locale_statuses ) {
-                       if ( $translation_locale_complete[ $a ] < $translation_locale_complete[ $b ] ) {
-                               return 1;
-                       } elseif ( $translation_locale_complete[ $a ] == $translation_locale_complete[ $b ] ) {
-                               if ( $translation_locale_statuses[ $a ]['waiting'] != $translation_locale_statuses[ $b ]['waiting'] ) {
-                                       return strnatcmp( $translation_locale_statuses[ $a ]['waiting'], $translation_locale_statuses[ $b ]['waiting'] );
-                               } else {
-                                       return strnatcmp( $a, $b );
-                               }
-                       } else {
-                               return -1;
-                       }
-               } );
-
-               $project->icon = $this->get_plugin_icon( $project, 64 );
-
-               $this->tmpl( 'projects-wp-plugins', get_defined_vars() );
-       }
-
-       /**
-        * Prints stats about contributors of a specific project.
-        *
-        * @param string $project_slug Slug of a project.
-        */
-       public function get_plugin_contributors( $project_slug ) {
-               global $wpdb;
-
-               $project_path = 'wp-plugins/' . $project_slug;
-               $project = GP::$project->by_path( $project_path );
-               if ( ! $project ) {
-                       return $this->die_with_404();
-               }
-
-               $project->icon = $this->get_plugin_icon( $project, 64 );
-
-               $contributors_by_locale = gp_get_meta( 'wp-plugins', $project->id, 'contributors-by-locale' );
-               if ( ! $contributors_by_locale || $contributors_by_locale['last_updated'] + HOUR_IN_SECONDS < time() ) {
-                       $contributors_by_locale = $this->get_contributors( $project );
-                       $contributors_by_locale['last_updated'] = time();
-                       gp_update_meta( $project->id, 'contributors-by-locale', $contributors_by_locale, 'wp-plugins' );
-               }
-
-               $chart_data = gp_get_meta( 'wp-plugins', $project->id, 'contributors-chart-data' );
-               if ( ! $chart_data || $chart_data['last_updated'] + DAY_IN_SECONDS < time() ) {
-                       $chart_data = $this->get_contributors_chart_data( $project );
-                       $chart_data['last_updated'] = time();
-                       gp_update_meta( $project->id, 'contributors-chart-data', $chart_data, 'wp-plugins' );
-               }
-
-               unset( $contributors_by_locale['last_updated'], $chart_data['last_updated'] );
-
-               $this->tmpl( 'projects-wp-plugins-contributors', get_defined_vars() );
-       }
-
-       /**
-        * Prints stats about language packs of a specific project.
-        *
-        * @param string $project_slug Slug of a project.
-        */
-       public function get_plugin_language_packs( $project_slug ) {
-               $project_path = 'wp-plugins/' . $project_slug;
-               $project = GP::$project->by_path( $project_path );
-               if ( ! $project ) {
-                       return $this->die_with_404();
-               }
-
-               $project->icon = $this->get_plugin_icon( $project, 64 );
-
-               $http_context = stream_context_create( array(
-                       'http' => array(
-                               'user_agent' => 'WordPress.org Translate',
-                       ),
-               ) );
-               $json = file_get_contents( "https://api.wordpress.org/translations/plugins/1.0/?slug={$project_slug}", null, $http_context );
-               $language_packs = $json && '{' == $json[0] ? json_decode( $json ) : null;
-
-               $this->tmpl( 'projects-wp-plugins-language-packs', get_defined_vars() );
-       }
-
-       /**
-        * Retrieves the icon of a plugin.
-        *
-        * @param GP_Project $project The plugin project.
-        * @param int        $size    Optional. The size of the icon. Default 64.
-        * @return string HTML markup for the icon.
-        */
-       private function get_plugin_icon( $project, $size = 64 ) {
-               $default = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
-
-               if ( function_exists( 'wporg_get_plugin_icon' ) ) {
-                       $icon = wporg_get_plugin_icon( $project->slug, $size );
-               }
-
-               if ( $icon ) {
-                       return $icon;
-               }
-
-               return $default;
-       }
-}
</del></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesincrouteswpthemesphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/wp-themes.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/wp-themes.php     2016-04-22 07:25:24 UTC (rev 2999)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/wp-themes.php   2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,170 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-class WPorg_GP_Route_WP_Themes extends WPorg_GP_Route_WP_Directory {
-
-       /**
-        * Prints stats about sub-project of a specific project.
-        *
-        * @param string $project_slug Slug of a project.
-        */
-       public function get_theme_projects( $project_slug ) {
-               global $wpdb;
-
-               $project_path = 'wp-themes/' . $project_slug;
-               $project = GP::$project->by_path( $project_path );
-               if ( ! $project ) {
-                       return $this->die_with_404();
-               }
-
-               $rows = $wpdb->get_results( "
-                       SELECT
-                               path, locale, locale_slug,
-                               (100 * stats.current/stats.all) as percent_complete,
-                               stats.waiting+stats.fuzzy as waiting_strings,
-                               stats.untranslated as untranslated
-                       FROM {$wpdb->project_translation_status} stats
-                               LEFT JOIN {$wpdb->gp_projects} p ON stats.project_id = p.id
-                       WHERE
-                               p.id = '{$project->id}'
-               " );
-
-               // Split out into $[Locale][Project] = %
-               $translation_locale_statuses = array();
-               $sub_projects = array();
-               foreach ( $rows as $set ) {
-
-                       // Find unique locale key.
-                       $locale_key = $set->locale;
-                       if ( 'default' != $set->locale_slug ) {
-                               $locale_key = $set->locale . '/' . $set->locale_slug;
-                       }
-
-                       /*
-                        * > 50% round down, so that a project with all strings except 1 translated shows 99%, instead of 100%.
-                        * < 50% round up, so that a project with just a few strings shows 1%, instead of 0%.
-                        */
-                       $percent_complete = (float) $set->percent_complete;
-                       $translation_locale_statuses[ $locale_key ]['stable'] = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
-
-                       // Increment the amount of waiting and untranslated strings.
-                       if ( ! isset( $translation_locale_statuses[ $locale_key ]['waiting'] ) ) {
-                               $translation_locale_statuses[ $locale_key ]['waiting'] = 0;
-                       }
-                       if ( ! isset( $translation_locale_statuses[ $locale_key ]['untranslated'] ) ) {
-                               $translation_locale_statuses[ $locale_key ]['untranslated'] = 0;
-                       }
-                       $translation_locale_statuses[ $locale_key ]['waiting'] += (int) $set->waiting_strings;
-                       $translation_locale_statuses[ $locale_key ]['untranslated'] += (int) $set->untranslated;
-
-
-                       ksort( $translation_locale_statuses[ $locale_key ], SORT_NATURAL );
-               }
-
-               unset( $project_path, $locale_key, $rows, $set, $sub_project, $sub_projects );
-
-               // Calculate a list of [Locale] = % subtotals
-               $translation_locale_complete = array();
-               foreach ( $translation_locale_statuses as $locale => $sets ) {
-                       unset( $sets['waiting'], $sets['untranslated'] );
-                       $translation_locale_complete[ $locale ] = round( array_sum( $sets ) / count( $sets ), 3 );
-               }
-               unset( $locale, $sets );
-
-
-               // Sort by translation completeness, least number of waiting strings, and locale slug.
-               uksort( $translation_locale_complete, function ( $a, $b ) use ( $translation_locale_complete, $translation_locale_statuses ) {
-                       if ( $translation_locale_complete[ $a ] < $translation_locale_complete[ $b ] ) {
-                               return 1;
-                       } elseif ( $translation_locale_complete[ $a ] == $translation_locale_complete[ $b ] ) {
-                               if ( $translation_locale_statuses[ $a ]['waiting'] != $translation_locale_statuses[ $b ]['waiting'] ) {
-                                       return strnatcmp( $translation_locale_statuses[ $a ]['waiting'], $translation_locale_statuses[ $b ]['waiting'] );
-                               } else {
-                                       return strnatcmp( $a, $b );
-                               }
-                       } else {
-                               return -1;
-                       }
-               } );
-
-               $project->icon = $this->get_theme_icon( $project, 64 );
-
-               $this->tmpl( 'projects-wp-themes', get_defined_vars() );
-       }
-
-       /**
-        * Prints stats about contributors of a specific project.
-        *
-        * @param string $project_slug Slug of a project.
-        */
-       public function get_theme_contributors( $project_slug ) {
-               global $wpdb;
-
-               $project_path = 'wp-themes/' . $project_slug;
-               $project = GP::$project->by_path( $project_path );
-               if ( ! $project ) {
-                       return $this->die_with_404();
-               }
-
-               $project->icon = $this->get_theme_icon( $project, 64 );
-
-               $contributors_by_locale = gp_get_meta( 'wp-themes', $project->id, 'contributors-by-locale' );
-               if ( ! $contributors_by_locale || $contributors_by_locale['last_updated'] + HOUR_IN_SECONDS < time() ) {
-                       $contributors_by_locale = $this->get_contributors( $project );
-                       $contributors_by_locale['last_updated'] = time();
-                       gp_update_meta( $project->id, 'contributors-by-locale', $contributors_by_locale, 'wp-themes' );
-               }
-
-               $chart_data = gp_get_meta( 'wp-themes', $project->id, 'contributors-chart-data' );
-               if ( ! $chart_data || $chart_data['last_updated'] + DAY_IN_SECONDS < time() ) {
-                       $chart_data = $this->get_contributors_chart_data( $project );
-                       $chart_data['last_updated'] = time();
-                       gp_update_meta( $project->id, 'contributors-chart-data', $chart_data, 'wp-themes' );
-               }
-
-               unset( $contributors_by_locale['last_updated'], $chart_data['last_updated'] );
-
-               $this->tmpl( 'projects-wp-themes-contributors', get_defined_vars() );
-       }
-
-       /**
-        * Prints stats about language packs of a specific project.
-        *
-        * @param string $project_slug Slug of a project.
-        */
-       public function get_theme_language_packs( $project_slug ) {
-               $project_path = 'wp-themes/' . $project_slug;
-               $project = GP::$project->by_path( $project_path );
-               if ( ! $project ) {
-                       return $this->die_with_404();
-               }
-
-               $project->icon = $this->get_theme_icon( $project, 64 );
-
-               $language_packs = $this->get_language_packs( 'theme', $project_slug );
-
-               $this->tmpl( 'projects-wp-themes-language-packs', get_defined_vars() );
-       }
-
-       /**
-        * Retrieves the icon of a theme.
-        *
-        * @param GP_Project $project The theme project.
-        * @param int        $size    Optional. The size of the icon. Default 64.
-        * @return string HTML markup for the icon.
-        */
-       private function get_theme_icon( $project, $size = 64 ) {
-               $default = '<div class="default-icon"><span class="dashicons dashicons-admin-themes"></span></div>';
-
-               $screenshot = gp_get_meta( 'wp-themes', $project->id, 'screenshot' );
-               if ( $screenshot ) {
-                       return sprintf(
-                               '<div class="icon"><img src="%s" alt="" width="%d" height="%d"></div>',
-                               esc_url( 'https://i0.wp.com/' . $screenshot . '?w=' . $size * 2 . '&strip=all' ),
-                               $size,
-                               $size
-                       );
-               }
-
-               return $default;
-       }
-}
</del></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggproutesvendorwordpressdotorgclassautoloaderphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/vendor/wordpressdotorg/class-autoloader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/vendor/wordpressdotorg/class-autoloader.php                              (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/vendor/wordpressdotorg/class-autoloader.php        2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,93 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+namespace WordPressdotorg\Autoload;
+
+/**
+ * An Autoloader which respects WordPress's filename standards.
+ *
+ * @package WordPressdotorg\Autoload
+ */
+class Autoloader {
+
+       /**
+        * Namespace separator.
+        */
+       const NS_SEPARATOR = '\\';
+
+       /**
+        * The prefix to compare classes against.
+        *
+        * @var string
+        * @access protected
+        */
+       protected $prefix;
+
+       /**
+        * Length of the prefix string.
+        *
+        * @var int
+        * @access protected
+        */
+       protected $prefix_length;
+
+       /**
+        * Path to the file to be loaded.
+        *
+        * @var string
+        * @access protected
+        */
+       protected $path;
+
+       /**
+        * Constructor.
+        *
+        * @param string $prefix Prefix all classes have in common.
+        * @param string $path   Path to the files to be loaded.
+        */
+       public function __construct( $prefix, $path ) {
+               $this->prefix        = $prefix;
+               $this->prefix_length = strlen( $prefix );
+               $this->path          = trailingslashit( $path );
+       }
+
+       /**
+        * Loads a class if it starts with `$this->prefix`.
+        *
+        * @param string $class The class to be loaded.
+        */
+       public function load( $class ) {
+               if ( strpos( $class, $this->prefix . self::NS_SEPARATOR ) !== 0 ) {
+                       return;
+               }
+
+               // Strip prefix from the start (ala PSR-4)
+               $class = substr( $class, $this->prefix_length + 1 );
+               $class = strtolower( $class );
+               $file  = '';
+
+               if ( false !== ( $last_ns_pos = strripos( $class, self::NS_SEPARATOR ) ) ) {
+                       $namespace = substr( $class, 0, $last_ns_pos );
+                       $namespace = str_replace( '_', '-', $namespace );
+                       $class     = substr( $class, $last_ns_pos + 1 );
+                       $file      = str_replace( self::NS_SEPARATOR, DIRECTORY_SEPARATOR, $namespace ) . DIRECTORY_SEPARATOR;
+               }
+
+               $file .= 'class-' . str_replace( '_', '-', $class ) . '.php';
+
+               $path = $this->path . $file;
+
+               if ( file_exists( $path ) ) {
+                       require $path;
+               }
+       }
+}
+
+/**
+ * Registers Autoloader's autoload function.
+ *
+ * @param string $prefix
+ * @param string $path
+ */
+function register_class_path( $prefix, $path ) {
+       $loader = new Autoloader( $prefix, $path );
+       spl_autoload_register( array( $loader, 'load' ) );
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggprouteswporggproutesphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/wporg-gp-routes.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/wporg-gp-routes.php      2016-04-22 17:34:04 UTC (rev 3001)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/wporg-gp-routes.php        2016-04-25 15:38:34 UTC (rev 3002)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2,111 +2,25 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Plugin name: GlotPress: Custom Routes
</span><span class="cx" style="display: block; padding: 0 10px">  * Description: Provides custom routes like <code>/locale</code> or <code>/stats</code> for translate.wordpress.org.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Version:     1.0
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Version:     2.0
</ins><span class="cx" style="display: block; padding: 0 10px">  * Author:      WordPress.org
</span><span class="cx" style="display: block; padding: 0 10px">  * Author URI:  http://wordpress.org/
</span><span class="cx" style="display: block; padding: 0 10px">  * License:     GPLv2 or later
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-require_once __DIR__ . '/routes/redirector.php';
-require_once __DIR__ . '/routes/index.php';
-require_once __DIR__ . '/routes/locale.php';
-require_once __DIR__ . '/routes/stats-overview.php';
-require_once __DIR__ . '/routes/wp-directory.php';
-require_once __DIR__ . '/routes/wp-plugins.php';
-require_once __DIR__ . '/routes/wp-themes.php';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+namespace WordPressdotorg\GlotPress\Routes;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-class WPorg_GP_Routes {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use WordPressdotorg\Autoload;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public function __construct() {
-               add_action( 'template_redirect', array( $this, 'register_routes' ), 5 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Store the root plugin file for usage with functions which use the plugin basename.
+define( __NAMESPACE__ . '\PLUGIN_FILE', __FILE__ );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( defined( 'WP_CLI' ) && WP_CLI ) {
-                       $this->register_cli_commands();
-               }
-       }
-
-       /**
-        * Registers custom routes and removes default routes.
-        *
-        * Removes:
-        *  - API: /languages/$locale
-        *  - /languages/$locale
-        *  - /languages/$locale
-        *  - /languages/$locale/$path
-        *  - /profile/$path
-        *  - /projects/wp-plugins/?
-        *  - /projects/wp-themes/?
-        *
-        * Adds:
-        *  - /
-        *  - /locale/$locale
-        *  - /locale/$locale/$path
-        *  - /locale/$locale/$path/$path
-        *  - /locale/$locale/$path/$path/$path
-        *  - /stats/?
-        *  - /projects/wp-plugins/$project
-        *  - /projects/wp-plugins/$project/contributors
-        *  - /projects/wp-plugins/$project/language-packs
-        *  - /projects/wp-themes/$project
-        *  - /projects/wp-themes/$project/contributors
-        *  - /projects/wp-themes/$project/language-packs
-        */
-       public function register_routes() {
-               $request_uri = GP::$router->request_uri();
-               $path = '(.+?)';
-               $locale = '(' . implode( '|', array_map( function( $locale ) { return $locale->slug; }, GP_Locales::locales() ) ) . ')';
-
-               if ( gp_startswith( $request_uri, '/' . GP::$router->api_prefix . '/' ) ) { // API requests.
-                       // Delete default routes.
-                       GP::$router->remove( "/languages/$locale" );
-               } else {
-                       // Delete default routes.
-                       GP::$router->remove( "/languages/$locale" );
-                       GP::$router->remove( "/languages/$locale/$path" );
-                       GP::$router->remove( "/profile" );
-                       GP::$router->remove( "/profile/$path" );
-
-                       // Redirect routes.
-                       GP::$router->prepend( '/languages', array( 'WPorg_GP_Route_Redirector', 'redirect_languages' ) );
-                       GP::$router->prepend( "/languages/$path", array( 'WPorg_GP_Route_Redirector', 'redirect_languages' ) );
-                       GP::$router->prepend( '/projects/wp-plugins/?', array( 'WPorg_GP_Route_Redirector', 'redirect_index' ) );
-                       GP::$router->prepend( '/projects/wp-themes/?', array( 'WPorg_GP_Route_Redirector', 'redirect_index' ) );
-
-                       // Register custom routes.
-                       GP::$router->prepend( '/', array( 'WPorg_GP_Route_Index', 'get_locales' ) );
-                       GP::$router->prepend( "/locale/$locale", array( 'WPorg_GP_Route_Locale', 'get_locale_projects' ) );
-                       GP::$router->prepend( "/locale/$locale/$path", array( 'WPorg_GP_Route_Locale', 'get_locale_projects' ) );
-                       GP::$router->prepend( "/locale/$locale/$path/$path", array( 'WPorg_GP_Route_Locale', 'get_locale_projects' ) );
-                       GP::$router->prepend( "/locale/$locale/$path/$path/$path", array( 'WPorg_GP_Route_Locale', 'get_locale_project' ) );
-                       GP::$router->prepend( '/stats/?', array( 'WPorg_GP_Route_Stats', 'get_stats_overview' ) );
-                       $project = '([^/]*)/?';
-                       GP::$router->prepend( "/projects/wp-plugins/$project", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_projects' ) );
-                       GP::$router->prepend( "/projects/wp-plugins/$project/contributors", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_contributors' ) );
-                       GP::$router->prepend( "/projects/wp-plugins/$project/language-packs", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_language_packs' ) );
-                       GP::$router->prepend( "/projects/wp-themes/$project", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_projects' ) );
-                       GP::$router->prepend( "/projects/wp-themes/$project/contributors", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_contributors' ) );
-                       GP::$router->prepend( "/projects/wp-themes/$project/language-packs", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_language_packs' ) );
-               }
-       }
-
-       /**
-        * Registers CLI commands if WP-CLI is loaded.
-        */
-       function register_cli_commands() {
-               require_once __DIR__ . '/cli/update-caches.php';
-
-               WP_CLI::add_command( 'wporg-translate update-cache', 'WPorg_GP_CLI_Update_Caches' );
-       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+if ( ! class_exists( '\WordPressdotorg\Autoload\Autoloader', false ) ) {
+       include __DIR__ . '/vendor/wordpressdotorg/class-autoloader.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function wporg_gp_routes() {
-       global $wporg_gp_routes;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Register an Autoloader for all files.
+Autoload\register_class_path( __NAMESPACE__, __DIR__ . '/inc' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( ! isset( $wporg_gp_routes ) ) {
-               $wporg_gp_routes = new WPorg_GP_Routes();
-       }
-
-       return $wporg_gp_routes;
-}
-add_action( 'plugins_loaded', 'wporg_gp_routes' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Instantiate the Plugin.
+Plugin::get_instance();
</ins></span></pre>
</div>
</div>

</body>
</html>