WordPress.org

Making WordPress.org

Changeset 3002


Ignore:
Timestamp:
04/25/2016 03:38:34 PM (5 years ago)
Author:
ocean90
Message:

Translate: Refactor the Custom Routes plugin to use an autoloader.

See #1682.

Location:
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes
Files:
4 added
8 deleted
1 edited
9 copied
2 moved

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/class-plugin.php

    r2999 r3002  
    11<?php
    2 /**
    3  * Plugin name: GlotPress: Custom Routes
    4  * Description: Provides custom routes like <code>/locale</code> or <code>/stats</code> for translate.wordpress.org.
    5  * Version:     1.0
    6  * Author:      WordPress.org
    7  * Author URI:  http://wordpress.org/
    8  * License:     GPLv2 or later
    9  */
    102
    11 require_once __DIR__ . '/routes/redirector.php';
    12 require_once __DIR__ . '/routes/index.php';
    13 require_once __DIR__ . '/routes/locale.php';
    14 require_once __DIR__ . '/routes/stats-overview.php';
    15 require_once __DIR__ . '/routes/wp-directory.php';
    16 require_once __DIR__ . '/routes/wp-plugins.php';
    17 require_once __DIR__ . '/routes/wp-themes.php';
     3namespace WordPressdotorg\GlotPress\Routes;
    184
    19 class WPorg_GP_Routes {
     5use GP;
     6use GP_Locales;
     7use WP_CLI;
    208
    21     public function __construct() {
     9class Plugin {
     10
     11    /**
     12     * @var Plugin The singleton instance.
     13     */
     14    private static $instance;
     15
     16    /**
     17     *
     18     * @var Sync\Translation_Sync
     19     */
     20    public $translation_sync = null;
     21
     22    /**
     23     * Returns always the same instance of this plugin.
     24     *
     25     * @return Plugin
     26     */
     27    public static function get_instance() {
     28        if ( ! ( self::$instance instanceof Plugin ) ) {
     29            self::$instance = new Plugin();
     30        }
     31        return self::$instance;
     32    }
     33
     34    /**
     35     * Instantiates a new Plugin object.
     36     */
     37    private function __construct() {
     38        add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) );
     39    }
     40
     41    /**
     42     * Initializes the plugin.
     43     */
     44    public function plugins_loaded() {
     45        if ( file_exists( WPORGPATH . 'extend/plugins-plugins/_plugin-icons.php' ) ) {
     46            include_once WPORGPATH . 'extend/plugins-plugins/_plugin-icons.php';
     47        }
     48
    2249        add_action( 'template_redirect', array( $this, 'register_routes' ), 5 );
    2350
     
    6592            GP::$router->remove( "/languages/$locale" );
    6693            GP::$router->remove( "/languages/$locale/$path" );
    67             GP::$router->remove( "/profile" );
     94            GP::$router->remove( '/profile' );
    6895            GP::$router->remove( "/profile/$path" );
    6996
    7097            // Redirect routes.
    71             GP::$router->prepend( '/languages', array( 'WPorg_GP_Route_Redirector', 'redirect_languages' ) );
    72             GP::$router->prepend( "/languages/$path", array( 'WPorg_GP_Route_Redirector', 'redirect_languages' ) );
    73             GP::$router->prepend( '/projects/wp-plugins/?', array( 'WPorg_GP_Route_Redirector', 'redirect_index' ) );
    74             GP::$router->prepend( '/projects/wp-themes/?', array( 'WPorg_GP_Route_Redirector', 'redirect_index' ) );
     98            GP::$router->prepend( '/languages', array( __NAMESPACE__ . '\Routes\Redirector', 'redirect_languages' ) );
     99            GP::$router->prepend( "/languages/$path", array( __NAMESPACE__ . '\Routes\Redirector', 'redirect_languages' ) );
     100            GP::$router->prepend( '/projects/wp-plugins/?', array( __NAMESPACE__ . '\Routes\Redirector', 'redirect_index' ) );
     101            GP::$router->prepend( '/projects/wp-themes/?', array( __NAMESPACE__ . '\Routes\Redirector', 'redirect_index' ) );
    75102
    76103            // Register custom routes.
    77             GP::$router->prepend( '/', array( 'WPorg_GP_Route_Index', 'get_locales' ) );
    78             GP::$router->prepend( "/locale/$locale", array( 'WPorg_GP_Route_Locale', 'get_locale_projects' ) );
    79             GP::$router->prepend( "/locale/$locale/$path", array( 'WPorg_GP_Route_Locale', 'get_locale_projects' ) );
    80             GP::$router->prepend( "/locale/$locale/$path/$path", array( 'WPorg_GP_Route_Locale', 'get_locale_projects' ) );
    81             GP::$router->prepend( "/locale/$locale/$path/$path/$path", array( 'WPorg_GP_Route_Locale', 'get_locale_project' ) );
    82             GP::$router->prepend( '/stats/?', array( 'WPorg_GP_Route_Stats', 'get_stats_overview' ) );
     104            GP::$router->prepend( '/', array( __NAMESPACE__ . '\Routes\Index', 'get_locales' ) );
     105            GP::$router->prepend( "/locale/$locale", array( __NAMESPACE__ . '\Routes\Locale', 'get_locale_projects' ) );
     106            GP::$router->prepend( "/locale/$locale/$path", array( __NAMESPACE__ . '\Routes\Locale', 'get_locale_projects' ) );
     107            GP::$router->prepend( "/locale/$locale/$path/$path", array( __NAMESPACE__ . '\Routes\Locale', 'get_locale_projects' ) );
     108            GP::$router->prepend( "/locale/$locale/$path/$path/$path", array( __NAMESPACE__ . '\Routes\Locale', 'get_locale_project' ) );
     109            GP::$router->prepend( '/stats/?', array( __NAMESPACE__ . '\Routes\Stats', 'get_stats_overview' ) );
    83110            $project = '([^/]*)/?';
    84             GP::$router->prepend( "/projects/wp-plugins/$project", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_projects' ) );
    85             GP::$router->prepend( "/projects/wp-plugins/$project/contributors", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_contributors' ) );
    86             GP::$router->prepend( "/projects/wp-plugins/$project/language-packs", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_language_packs' ) );
    87             GP::$router->prepend( "/projects/wp-themes/$project", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_projects' ) );
    88             GP::$router->prepend( "/projects/wp-themes/$project/contributors", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_contributors' ) );
    89             GP::$router->prepend( "/projects/wp-themes/$project/language-packs", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_language_packs' ) );
     111            GP::$router->prepend( "/projects/wp-plugins/$project", array( __NAMESPACE__ . '\Routes\WP_Plugins', 'get_plugin_projects' ) );
     112            GP::$router->prepend( "/projects/wp-plugins/$project/contributors", array( __NAMESPACE__ . '\Routes\WP_Plugins', 'get_plugin_contributors' ) );
     113            GP::$router->prepend( "/projects/wp-plugins/$project/language-packs", array( __NAMESPACE__ . '\Routes\WP_Plugins', 'get_plugin_language_packs' ) );
     114            GP::$router->prepend( "/projects/wp-themes/$project", array( __NAMESPACE__ . '\Routes\WP_Themes', 'get_theme_projects' ) );
     115            GP::$router->prepend( "/projects/wp-themes/$project/contributors", array( __NAMESPACE__ . '\Routes\WP_Themes', 'get_theme_contributors' ) );
     116            GP::$router->prepend( "/projects/wp-themes/$project/language-packs", array( __NAMESPACE__ . '\Routes\WP_Themes', 'get_theme_language_packs' ) );
    90117        }
    91118    }
     
    95122     */
    96123    function register_cli_commands() {
    97         require_once __DIR__ . '/cli/update-caches.php';
    98 
    99         WP_CLI::add_command( 'wporg-translate update-cache', 'WPorg_GP_CLI_Update_Caches' );
     124        WP_CLI::add_command( 'wporg-translate update-cache', __NAMESPACE__ . '\CLI\Update_Caches' );
    100125    }
    101126}
    102 
    103 function wporg_gp_routes() {
    104     global $wporg_gp_routes;
    105 
    106     if ( ! isset( $wporg_gp_routes ) ) {
    107         $wporg_gp_routes = new WPorg_GP_Routes();
    108     }
    109 
    110     return $wporg_gp_routes;
    111 }
    112 add_action( 'plugins_loaded', 'wporg_gp_routes' );
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/cli/class-update-caches.php

    r2999 r3002  
    11<?php
    22
    3 class WPorg_GP_CLI_Update_Caches extends WP_CLI_Command {
     3namespace WordPressdotorg\GlotPress\Routes\CLI;
     4
     5use GP;
     6use WP_CLI;
     7use WP_CLI_Command;
     8
     9class Update_Caches extends WP_CLI_Command {
    410
    511    private $cache_group = 'wporg-translate';
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-index.php

    r2999 r3002  
    11<?php
     2
     3namespace WordPressdotorg\GlotPress\Routes\Routes;
     4
     5use GP_Locales;
     6use GP_Route;
     7
    28/**
    39 * Index Route Class.
     
    511 * Provides the route for translate.wordpress.org/.
    612 */
    7 class WPorg_GP_Route_Index extends GP_Route {
     13class Index extends GP_Route {
    814
    915    private $cache_group = 'wporg-translate';
     
    2430            $locales[] = GP_Locales::by_slug( $locale );
    2531        }
    26         usort( $locales, array( $this, '_sort_english_name_callback') );
     32        usort( $locales, array( $this, '_sort_english_name_callback' ) );
    2733        unset( $existing_locales );
    2834
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-locale.php

    r2999 r3002  
    11<?php
    22
     3namespace WordPressdotorg\GlotPress\Routes\Routes;
     4
    35// wporg_get_plugin_icon()
    4 if ( file_exists( WPORGPATH . 'extend/plugins-plugins/_plugin-icons.php' ) ) {
    5     include_once WPORGPATH . 'extend/plugins-plugins/_plugin-icons.php';
    6 }
     6use GP;
     7use GP_Locales;
     8use GP_Route;
     9use stdClass;
    710
    811/**
     
    1114 * Provides the route for translate.wordpress.org/locale/$locale.
    1215 */
    13 class WPorg_GP_Route_Locale extends GP_Route {
     16class Locale extends GP_Route {
    1417
    1518    /**
    1619     * Prints projects/translation sets of a top level project.
    1720     *
    18      * @param string $locale_slug      Slug of the locale.
    19      * @param string $set_slug         Slug of the translation set.
    20      * @param string $project_path    Path of a project.
     21     * @param string      $locale_slug  Slug of the locale.
     22     * @param string      $set_slug     Slug of the translation set.
     23     * @param bool|string $project_path Path of a project.
    2124     */
    2225    public function get_locale_projects( $locale_slug, $set_slug = 'default', $project_path = false ) {
     
    101104        $project_ids = array_merge(
    102105            $project_ids,
    103             $wpdb->get_col( "SELECT id FROM {$wpdb->gp_projects} WHERE parent_project_id IN(" . implode(', ', $project_ids  ) . ")" )
     106            $wpdb->get_col( "SELECT id FROM {$wpdb->gp_projects} WHERE parent_project_id IN( " . implode( ', ', $project_ids ) . ')' )
    104107        );
    105108
     
    192195        }
    193196
    194         switch( $project->slug ) {
     197        switch ( $project->slug ) {
    195198            case 'wp':
    196199                return '<div class="wordpress-icon"><span class="dashicons dashicons-wordpress-alt"></span></div>';
    197200            case 'meta':
    198                 switch( $sub_project->slug ) {
     201                switch ( $sub_project->slug ) {
    199202                    case 'forums':
    200203                        return '<div class="default-icon"><span class="dashicons dashicons-format-chat"></span></div>';
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-redirector.php

    r2999 r3002  
    11<?php
     2
     3namespace WordPressdotorg\GlotPress\Routes\Routes;
     4
     5use GP_Route;
     6
    27/**
    38 * Redirector Route Class.
     
    510 * Provides redirection routes.
    611 */
    7 class WPorg_GP_Route_Redirector extends GP_Route {
     12class Redirector extends GP_Route {
    813
    914    public function redirect_languages( $path = '' ) {
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-stats.php

    r2999 r3002  
    11<?php
     2
     3namespace WordPressdotorg\GlotPress\Routes\Routes;
     4
     5use GP;
     6use GP_Route;
     7
    28/**
    39 * Stats Route Class.
     
    511 * Provides the route for translate.wordpress.org/stats.
    612 */
    7 class WPorg_GP_Route_Stats extends GP_Route {
     13class Stats extends GP_Route {
    814
    915    public function get_stats_overview() {
     
    2228
    2329        // I'm sure there's somewhere to fetch these from statically defined
    24         $wp_project = GP::$project->by_path('wp');
     30        $wp_project = GP::$project->by_path( 'wp' );
    2531        foreach ( GP::$project->find_many( array( 'parent_project_id' => $wp_project->id, 'active' => 1 ), 'name ASC' ) as $wp_sub_project ) {
    2632            // Prefix the WordPress projects...
     
    7581        $parent_project_ids = implode(',', array(
    7682            GP::$project->by_path( 'wp-plugins' )->id,
    77             GP::$project->by_path( 'wp-themes' )->id
     83            GP::$project->by_path( 'wp-themes' )->id,
    7884        ) );
    7985        $sql = "SELECT
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-directory.php

    r2999 r3002  
    11<?php
    22
    3 class WPorg_GP_Route_WP_Directory extends GP_Route {
     3namespace WordPressdotorg\GlotPress\Routes\Routes;
     4
     5use DateInterval;
     6use DatePeriod;
     7use DateTime;
     8use GP_Route;
     9
     10class WP_Directory extends GP_Route {
    411
    512    /**
     
    4653        unset( $translation_editors );
    4754
    48         foreach( $this->get_translation_contributors_by_locale( $project->id ) as $row ) {
     55        foreach ( $this->get_translation_contributors_by_locale( $project->id ) as $row ) {
    4956            if ( ! isset( $contributors_by_locale[ $row->locale ] ) ) {
    5057                $contributors_by_locale[ $row->locale ] = $default_value;
     
    7986
    8087        foreach ( $sub_projects as $sub_project ) {
    81             foreach( $this->get_translation_contributors_by_locale( $sub_project ) as $row ) {
     88            foreach ( $this->get_translation_contributors_by_locale( $sub_project ) as $row ) {
    8289                if ( ! isset( $contributors_by_locale[ $row->locale ] ) ) {
    8390                    $contributors_by_locale[ $row->locale ] = $default_value;
     
    112119     * Generates the chart data for contributors activity.
    113120     *
    114      * @param GP_Project $project_id The project.
     121     * @param \GP_Project $project The project.
    115122     * @return array The data to build a chart via Chartist.js.
    116123     */
     
    126133        $project_ids = array_merge( array( $project->id ), $sub_projects );
    127134        $translation_set_ids = $wpdb->get_col( "
    128             SELECT `id` FROM {$wpdb->gp_translation_sets} WHERE `project_id` IN (" . implode( ',', $project_ids ) . ")
     135            SELECT `id` FROM {$wpdb->gp_translation_sets} WHERE `project_id` IN ( " . implode( ',', $project_ids ) . ")
    129136        " );
    130137
     
    139146
    140147        $days = array();
    141         foreach( $date_range as $date ) {
     148        foreach ( $date_range as $date ) {
    142149            $days[] = $date->format( 'Y-m-d' );
    143150        }
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-plugins.php

    r2999 r3002  
    11<?php
    22
    3 class WPorg_GP_Route_WP_Plugins extends WPorg_GP_Route_WP_Directory {
     3namespace WordPressdotorg\GlotPress\Routes\Routes;
     4
     5use GP;
     6
     7class WP_Plugins extends WP_Directory {
    48
    59    /**
     
    105109     */
    106110    public function get_plugin_contributors( $project_slug ) {
    107         global $wpdb;
    108 
    109111        $project_path = 'wp-plugins/' . $project_slug;
    110112        $project = GP::$project->by_path( $project_path );
     
    169171        $default = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
    170172
     173        $icon = '';
    171174        if ( function_exists( 'wporg_get_plugin_icon' ) ) {
    172175            $icon = wporg_get_plugin_icon( $project->slug, $size );
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-wp-themes.php

    r2999 r3002  
    11<?php
    22
    3 class WPorg_GP_Route_WP_Themes extends WPorg_GP_Route_WP_Directory {
     3namespace WordPressdotorg\GlotPress\Routes\Routes;
     4
     5use GP;
     6
     7class WP_Themes extends WP_Directory {
    48
    59    /**
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/wporg-gp-routes.php

    r2932 r3002  
    33 * Plugin name: GlotPress: Custom Routes
    44 * Description: Provides custom routes like <code>/locale</code> or <code>/stats</code> for translate.wordpress.org.
    5  * Version:     1.0
     5 * Version:     2.0
    66 * Author:      WordPress.org
    77 * Author URI:  http://wordpress.org/
     
    99 */
    1010
    11 require_once __DIR__ . '/routes/redirector.php';
    12 require_once __DIR__ . '/routes/index.php';
    13 require_once __DIR__ . '/routes/locale.php';
    14 require_once __DIR__ . '/routes/stats-overview.php';
    15 require_once __DIR__ . '/routes/wp-directory.php';
    16 require_once __DIR__ . '/routes/wp-plugins.php';
    17 require_once __DIR__ . '/routes/wp-themes.php';
     11namespace WordPressdotorg\GlotPress\Routes;
    1812
    19 class WPorg_GP_Routes {
     13use WordPressdotorg\Autoload;
    2014
    21     public function __construct() {
    22         add_action( 'template_redirect', array( $this, 'register_routes' ), 5 );
     15// Store the root plugin file for usage with functions which use the plugin basename.
     16define( __NAMESPACE__ . '\PLUGIN_FILE', __FILE__ );
    2317
    24         if ( defined( 'WP_CLI' ) && WP_CLI ) {
    25             $this->register_cli_commands();
    26         }
    27     }
    28 
    29     /**
    30      * Registers custom routes and removes default routes.
    31      *
    32      * Removes:
    33      *  - API: /languages/$locale
    34      *  - /languages/$locale
    35      *  - /languages/$locale
    36      *  - /languages/$locale/$path
    37      *  - /profile/$path
    38      *  - /projects/wp-plugins/?
    39      *  - /projects/wp-themes/?
    40      *
    41      * Adds:
    42      *  - /
    43      *  - /locale/$locale
    44      *  - /locale/$locale/$path
    45      *  - /locale/$locale/$path/$path
    46      *  - /locale/$locale/$path/$path/$path
    47      *  - /stats/?
    48      *  - /projects/wp-plugins/$project
    49      *  - /projects/wp-plugins/$project/contributors
    50      *  - /projects/wp-plugins/$project/language-packs
    51      *  - /projects/wp-themes/$project
    52      *  - /projects/wp-themes/$project/contributors
    53      *  - /projects/wp-themes/$project/language-packs
    54      */
    55     public function register_routes() {
    56         $request_uri = GP::$router->request_uri();
    57         $path = '(.+?)';
    58         $locale = '(' . implode( '|', array_map( function( $locale ) { return $locale->slug; }, GP_Locales::locales() ) ) . ')';
    59 
    60         if ( gp_startswith( $request_uri, '/' . GP::$router->api_prefix . '/' ) ) { // API requests.
    61             // Delete default routes.
    62             GP::$router->remove( "/languages/$locale" );
    63         } else {
    64             // Delete default routes.
    65             GP::$router->remove( "/languages/$locale" );
    66             GP::$router->remove( "/languages/$locale/$path" );
    67             GP::$router->remove( "/profile" );
    68             GP::$router->remove( "/profile/$path" );
    69 
    70             // Redirect routes.
    71             GP::$router->prepend( '/languages', array( 'WPorg_GP_Route_Redirector', 'redirect_languages' ) );
    72             GP::$router->prepend( "/languages/$path", array( 'WPorg_GP_Route_Redirector', 'redirect_languages' ) );
    73             GP::$router->prepend( '/projects/wp-plugins/?', array( 'WPorg_GP_Route_Redirector', 'redirect_index' ) );
    74             GP::$router->prepend( '/projects/wp-themes/?', array( 'WPorg_GP_Route_Redirector', 'redirect_index' ) );
    75 
    76             // Register custom routes.
    77             GP::$router->prepend( '/', array( 'WPorg_GP_Route_Index', 'get_locales' ) );
    78             GP::$router->prepend( "/locale/$locale", array( 'WPorg_GP_Route_Locale', 'get_locale_projects' ) );
    79             GP::$router->prepend( "/locale/$locale/$path", array( 'WPorg_GP_Route_Locale', 'get_locale_projects' ) );
    80             GP::$router->prepend( "/locale/$locale/$path/$path", array( 'WPorg_GP_Route_Locale', 'get_locale_projects' ) );
    81             GP::$router->prepend( "/locale/$locale/$path/$path/$path", array( 'WPorg_GP_Route_Locale', 'get_locale_project' ) );
    82             GP::$router->prepend( '/stats/?', array( 'WPorg_GP_Route_Stats', 'get_stats_overview' ) );
    83             $project = '([^/]*)/?';
    84             GP::$router->prepend( "/projects/wp-plugins/$project", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_projects' ) );
    85             GP::$router->prepend( "/projects/wp-plugins/$project/contributors", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_contributors' ) );
    86             GP::$router->prepend( "/projects/wp-plugins/$project/language-packs", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_language_packs' ) );
    87             GP::$router->prepend( "/projects/wp-themes/$project", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_projects' ) );
    88             GP::$router->prepend( "/projects/wp-themes/$project/contributors", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_contributors' ) );
    89             GP::$router->prepend( "/projects/wp-themes/$project/language-packs", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_language_packs' ) );
    90         }
    91     }
    92 
    93     /**
    94      * Registers CLI commands if WP-CLI is loaded.
    95      */
    96     function register_cli_commands() {
    97         require_once __DIR__ . '/cli/update-caches.php';
    98 
    99         WP_CLI::add_command( 'wporg-translate update-cache', 'WPorg_GP_CLI_Update_Caches' );
    100     }
     18if ( ! class_exists( '\WordPressdotorg\Autoload\Autoloader', false ) ) {
     19    include __DIR__ . '/vendor/wordpressdotorg/class-autoloader.php';
    10120}
    10221
    103 function wporg_gp_routes() {
    104     global $wporg_gp_routes;
     22// Register an Autoloader for all files.
     23Autoload\register_class_path( __NAMESPACE__, __DIR__ . '/inc' );
    10524
    106     if ( ! isset( $wporg_gp_routes ) ) {
    107         $wporg_gp_routes = new WPorg_GP_Routes();
    108     }
    109 
    110     return $wporg_gp_routes;
    111 }
    112 add_action( 'plugins_loaded', 'wporg_gp_routes' );
     25// Instantiate the Plugin.
     26Plugin::get_instance();
Note: See TracChangeset for help on using the changeset viewer.