Making WordPress.org

Changeset 2864


Ignore:
Timestamp:
03/31/2016 08:25:15 PM (9 years ago)
Author:
ocean90
Message:

Translate: Display stats, contributors and language packs on theme project pages.

See #1388.

Location:
sites/trunk
Files:
5 edited
5 copied

Legend:

Unmodified
Added
Removed
  • sites/trunk/translate.wordpress.org/public_html/gp-templates-new/projects-wp-plugins.php

    r2765 r2864  
    5656        <thead>
    5757            <tr>
    58                 <?php foreach ( $table_headings as $heading ) : ?>
    59                 <th class="title"><?php echo $heading; ?></th>
     58                <?php foreach ( $table_headings as $key => $heading ) : ?>
     59                <th class="col-<?php echo $key; ?>"><?php echo $heading; ?></th>
    6060                <?php endforeach; ?>
    6161            </tr>
  • sites/trunk/translate.wordpress.org/public_html/gp-templates-new/projects-wp-themes-contributors.php

    r2858 r2864  
    4949
    5050    <h3>Teams</h3>
    51     <p>For each locale a plugin can have translation editors and contributors. If a locale has no editor yet then you should probably <a href="https://make.wordpress.org/polyglots/handbook/rosetta/theme-plugin-directories/#translating-themes-plugins">make a request</a>.</p>
     51    <p>For each locale a theme can have translation editors and contributors. If a locale has no editor yet then you should probably <a href="https://make.wordpress.org/polyglots/handbook/rosetta/theme-plugin-directories/#translating-themes-plugins">make a request</a>.</p>
    5252    <?php
    5353    if ( $contributors_by_locale ) {
  • sites/trunk/translate.wordpress.org/public_html/gp-templates-new/projects-wp-themes-language-packs.php

    r2858 r2864  
    4343    <h3>Language Packs</h3>
    4444
    45     <p>Language packs are installed automatically if they are available. Once a locale has reached the threshold for a package build it will be listed here. It also means that you don&#8217;t have to include this language in your plugin anymore.</p>
     45    <p>Language packs are installed automatically if they are available. Once a locale has reached the threshold for a package build it will be listed here. It also means that you don&#8217;t have to include this language in your theme anymore.</p>
    4646
    4747    <?php
  • sites/trunk/translate.wordpress.org/public_html/gp-templates-new/projects-wp-themes.php

    r2858 r2864  
    33$table_headings = array(
    44    'locale'        => __( 'Locale' ),
    5     'dev'           => __( 'Development' ),
    6     'dev-readme'    => __( 'Development Readme' ),
    75    'stable'        => __( 'Stable' ),
    8     'stable-readme' => __( 'Stable Readme' ),
    96    'waiting'       => __( 'Waiting' ),
    107);
     
    4643</div>
    4744
    48 <?php if ( $has_error ) : ?>
    49 <div class="wporg-notice wporg-notice-warning">
    50     <p>This plugin is not <a href="https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/">properly prepared for localization</a>. If you would like to translate this plugin, <a href="<?php echo esc_url( 'https://wordpress.org/support/plugin/' . $project->slug ); ?>">please contact the author</a>.</p>
    51 </div>
    52 <?php endif; ?>
    53 
    5445<div class="stats-table">
    5546    <table id="stats-table" class="table">
    5647        <thead>
    5748            <tr>
    58                 <?php foreach ( $table_headings as $heading ) : ?>
    59                 <th class="title"><?php echo $heading; ?></th>
     49                <?php foreach ( $table_headings as $key => $heading ) : ?>
     50                <th class="col-<?php echo $key; ?>"><?php echo $heading; ?></th>
    6051                <?php endforeach; ?>
    6152            </tr>
     
    8879                    <?php
    8980                        if ( $translation_locale_statuses[ $locale_slug ] ) :
    90                             foreach ( array( 'dev', 'dev-readme', 'stable', 'stable-readme', 'waiting' ) as $subproject_slug ) :
     81                            foreach ( array( 'stable', 'waiting' ) as $subproject_slug ) :
    9182                                if ( isset( $translation_locale_statuses[ $locale_slug ][ $subproject_slug ] ) ) :
    9283                                    $percent = $translation_locale_statuses[ $locale_slug ][ $subproject_slug ];
     
    10596                                    else :
    10697                                        $percent_class = (int) ( $percent / 10 ) * 10;
    107                                         $link_url  = gp_url_project( $project->path, gp_url_join( $subproject_slug, $gp_locale->slug, $set_slug ) );
     98                                        $link_url  = gp_url_project( $project->path, gp_url_join( $gp_locale->slug, $set_slug ) );
    10899                                        $link_text = "$percent%";
    109100
  • sites/trunk/translate.wordpress.org/public_html/gp-templates-new/stats-overview.php

    r2740 r2864  
    1414        <thead>
    1515            <tr>
    16                 <th class="title"><?php _e( 'Locale' ); ?></th>
     16                <th class="col-locale-code"><?php _e( 'Locale' ); ?></th>
    1717                <?php foreach ( $projects as $slug => $project ) : ?>
    1818                    <th><?php
  • sites/trunk/translate.wordpress.org/public_html/gp-templates-new/style.css

    r2667 r2864  
    949949    text-align: center;
    950950    border-collapse: collapse;
     951    table-layout: fixed;
    951952}
    952953
     
    962963    text-align: center;
    963964    vertical-align: bottom;
    964     width: 79px;
    965965    padding: 10px 3px;
    966966    font-size: .8em;
     
    974974}
    975975
    976 .stats-table table th.title {
    977     width: 140px!important;
     976.stats-table table th {
     977    word-break: break-word;
     978}
     979
     980.stats-table table th.col-locale-code {
     981    width: 110px;
     982}
     983
     984.stats-table table th.col-locale {
     985    width: 180px;
    978986}
    979987
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/wp-directory.php

    r2858 r2864  
    11<?php
    22
    3 class WPorg_GP_Route_WP_Plugins extends GP_Route {
    4 
    5     /**
    6      * Prints stats about sub-project of a specific project.
    7      *
    8      * @param string $project_slug Slug of a project.
    9      */
    10     public function get_plugin_projects( $project_slug ) {
     3class WPorg_GP_Route_WP_Directory extends GP_Route {
     4
     5    /**
     6     * Prints stats about contributors of a specific project.
     7     *
     8     * @param GP_Project $project The project.
     9     * @return array|false False if project not found, otherwise array with contributors.
     10     */
     11    public function get_contributors( $project ) {
    1112        global $wpdb;
    12 
    13         $project_path = 'wp-plugins/' . $project_slug;
    14         $project = GP::$project->by_path( $project_path );
    15         if ( ! $project ) {
    16             return $this->die_with_404();
    17         }
    18 
    19         $rows = $wpdb->get_results( "
    20             SELECT
    21                 path, locale, locale_slug,
    22                 (100 * stats.current/stats.all) as percent_complete,
    23                 stats.waiting+stats.fuzzy as waiting_strings,
    24                 stats.untranslated as untranslated
    25             FROM {$wpdb->project_translation_status} stats
    26                 LEFT JOIN {$wpdb->gp_projects} p ON stats.project_id = p.id
    27             WHERE
    28                 p.parent_project_id = '{$project->id}'
    29         " );
    30 
    31         // Split out into $[Locale][Project] = %
    32         $translation_locale_statuses = array();
    33         $sub_projects = array();
    34         foreach ( $rows as $set ) {
    35 
    36             // Find unique locale key.
    37             $locale_key = $set->locale;
    38             if ( 'default' != $set->locale_slug ) {
    39                 $locale_key = $set->locale . '/' . $set->locale_slug;
    40             }
    41             $sub_project = str_replace( "$project_path/", '', $set->path );
    42             $sub_projects[ $sub_project ] = true;
    43 
    44             /*
    45              * > 50% round down, so that a project with all strings except 1 translated shows 99%, instead of 100%.
    46              * < 50% round up, so that a project with just a few strings shows 1%, instead of 0%.
    47              */
    48             $percent_complete = (float) $set->percent_complete;
    49             $translation_locale_statuses[ $locale_key ][ $sub_project ] = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
    50 
    51             // Increment the amount of waiting and untranslated strings.
    52             if ( ! isset( $translation_locale_statuses[ $locale_key ]['waiting'] ) ) {
    53                 $translation_locale_statuses[ $locale_key ]['waiting'] = 0;
    54             }
    55             if ( ! isset( $translation_locale_statuses[ $locale_key ]['untranslated'] ) ) {
    56                 $translation_locale_statuses[ $locale_key ]['untranslated'] = 0;
    57             }
    58             $translation_locale_statuses[ $locale_key ]['waiting'] += (int) $set->waiting_strings;
    59             $translation_locale_statuses[ $locale_key ]['untranslated'] += (int) $set->untranslated;
    60 
    61 
    62             ksort( $translation_locale_statuses[ $locale_key ], SORT_NATURAL );
    63         }
    64 
    65         // Check if the plugin has at least one code project. These won't be created if a plugin
    66         // has no text domain defined.
    67         $sub_projects = array_keys( $sub_projects );
    68         $has_error = ( ! in_array( 'dev', $sub_projects ) && ! in_array( 'stable', $sub_projects ) );
    69 
    70         unset( $project_path, $locale_key, $rows, $set, $sub_project, $sub_projects );
    71 
    72         // Calculate a list of [Locale] = % subtotals
    73         $translation_locale_complete = array();
    74         foreach ( $translation_locale_statuses as $locale => $sets ) {
    75             unset( $sets['waiting'], $sets['untranslated'] );
    76             $translation_locale_complete[ $locale ] = round( array_sum( $sets ) / count( $sets ), 3 );
    77         }
    78         unset( $locale, $sets );
    79 
    80 
    81         // Sort by translation completeness, least number of waiting strings, and locale slug.
    82         uksort( $translation_locale_complete, function ( $a, $b ) use ( $translation_locale_complete, $translation_locale_statuses ) {
    83             if ( $translation_locale_complete[ $a ] < $translation_locale_complete[ $b ] ) {
    84                 return 1;
    85             } elseif ( $translation_locale_complete[ $a ] == $translation_locale_complete[ $b ] ) {
    86                 if ( $translation_locale_statuses[ $a ]['waiting'] != $translation_locale_statuses[ $b ]['waiting'] ) {
    87                     return strnatcmp( $translation_locale_statuses[ $a ]['waiting'], $translation_locale_statuses[ $b ]['waiting'] );
    88                 } else {
    89                     return strnatcmp( $a, $b );
    90                 }
    91             } else {
    92                 return -1;
    93             }
    94         } );
    95 
    96         if ( function_exists( 'wporg_get_plugin_icon' ) ) {
    97             $project->icon = wporg_get_plugin_icon( $project->slug, 64 );
    98         } else {
    99             $project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
    100         }
    101 
    102         $this->tmpl( 'projects-wp-plugins', get_defined_vars() );
    103     }
    104 
    105     /**
    106      * Prints stats about contributors of a specific project.
    107      *
    108      * @param string $project_slug Slug of a project.
    109      */
    110     public function get_plugin_contributors( $project_slug ) {
    111         global $wpdb;
    112 
    113         $project_path = 'wp-plugins/' . $project_slug;
    114         $project = GP::$project->by_path( $project_path );
    115         if ( ! $project ) {
    116             return $this->die_with_404();
    117         }
    118 
    119         if ( function_exists( 'wporg_get_plugin_icon' ) ) {
    120             $project->icon = wporg_get_plugin_icon( $project->slug, 64 );
    121         } else {
    122             $project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
    123         }
    12413
    12514        $contributors_by_locale = array();
     
    15746        unset( $translation_editors );
    15847
     48        foreach( $this->get_translation_contributors_by_locale( $project->id ) as $row ) {
     49            if ( ! isset( $contributors_by_locale[ $row->locale ] ) ) {
     50                $contributors_by_locale[ $row->locale ] = $default_value;
     51            }
     52
     53            if ( isset( $contributors_by_locale[ $row->locale ]['editors'][ $row->user_id ] ) ) {
     54                continue;
     55            }
     56
     57            if ( isset( $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] ) ) {
     58                continue;
     59            }
     60
     61            $user = get_user_by( 'id', $row->user_id );
     62            if ( ! $user ) {
     63                continue;
     64            }
     65
     66            $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] = (object) array(
     67                'nicename'     => $user->user_nicename,
     68                'display_name' => $this->_encode( $user->display_name ),
     69            );
     70
     71            $contributors_by_locale[ $row->locale ]['count']++;
     72        }
     73
    15974        $sub_projects = $wpdb->get_col( $wpdb->prepare( "
    16075            SELECT id
     
    191106        }
    192107
    193         $chart_data = $this->get_plugin_contributors_chart_data( $project->id, $sub_projects );
    194 
    195         $this->tmpl( 'projects-wp-plugins-contributors', get_defined_vars() );
     108        return $contributors_by_locale;
    196109    }
    197110
     
    199112     * Generates the chart data for contributors activity.
    200113     *
    201      * @param  int    $project_id   The ID of a project. Used to store data as meta.
    202      * @param  array $sub_projects Optional. IDs of sub-projects.
     114     * @param GP_Project $project_id The project.
    203115     * @return array The data to build a chart via Chartist.js.
    204116     */
    205     private function get_plugin_contributors_chart_data( $project_id, $sub_projects = null ) {
    206         $chart_data = gp_get_meta( 'wp-plugins', $project_id, 'contributors-chart-data' );
    207         if ( $chart_data ) {
    208             if ( $chart_data['last_updated'] + DAY_IN_SECONDS > time() ) {
    209                 return $chart_data;
    210             }
    211         }
    212 
     117    protected function get_contributors_chart_data( $project ) {
    213118        global $wpdb;
    214119
    215         if ( ! $sub_projects ) {
    216             $sub_projects = $wpdb->get_col( $wpdb->prepare( "
    217                 SELECT id
    218                 FROM {$wpdb->gp_projects}
    219                 WHERE parent_project_id = %d
    220             ", $$project_id ) );
    221         }
    222 
     120        $sub_projects = $wpdb->get_col( $wpdb->prepare( "
     121            SELECT id
     122            FROM {$wpdb->gp_projects}
     123            WHERE parent_project_id = %d
     124        ", $project->id ) );
     125
     126        $project_ids = array_merge( array( $project->id ), $sub_projects );
    223127        $translation_set_ids = $wpdb->get_col( "
    224             SELECT `id` FROM {$wpdb->gp_translation_sets} WHERE `project_id` IN (" . implode( ',', $sub_projects ) . ")
     128            SELECT `id` FROM {$wpdb->gp_translation_sets} WHERE `project_id` IN (" . implode( ',', $project_ids ) . ")
    225129        " );
    226130
     
    277181        }
    278182
    279         $last_updated = time();
    280         $chart_data = compact( 'labels', 'series', 'last_updated' );
    281 
    282         gp_update_meta( $project_id, 'contributors-chart-data', $chart_data, 'wp-plugins' );
     183        $chart_data = compact( 'labels', 'series' );
    283184
    284185        return $chart_data;
     
    288189     * Prints stats about language packs of a specific project.
    289190     *
    290      * @param string $project_slug Slug of a project.
    291      */
    292     public function get_plugin_language_packs( $project_slug ) {
    293         $project_path = 'wp-plugins/' . $project_slug;
    294         $project = GP::$project->by_path( $project_path );
    295         if ( ! $project ) {
    296             return $this->die_with_404();
    297         }
    298 
    299         if ( function_exists( 'wporg_get_plugin_icon' ) ) {
    300             $project->icon = wporg_get_plugin_icon( $project->slug, 64 );
    301         } else {
    302             $project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
    303         }
    304 
     191     * @param string $type Type of the language pack, plugin or theme.
     192     * @param string $slug Slug of a project.
     193     */
     194    public function get_language_packs( $type, $slug ) {
    305195        $http_context = stream_context_create( array(
    306196            'http' => array(
     
    308198            ),
    309199        ) );
    310         $json = file_get_contents( "https://api.wordpress.org/translations/plugins/1.0/?slug={$project_slug}", null, $http_context );
     200        if ( 'plugin' === $type ) {
     201            $type = 'plugins';
     202        } else {
     203            $type = 'themes';
     204        }
     205        $json = file_get_contents( "https://api.wordpress.org/translations/$type/1.0/?slug={$slug}", null, $http_context );
    311206        $language_packs = $json && '{' == $json[0] ? json_decode( $json ) : null;
    312207
    313         $this->tmpl( 'projects-wp-plugins-language-packs', get_defined_vars() );
     208        return $language_packs;
    314209    }
    315210
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/wp-plugins.php

    r2663 r2864  
    11<?php
    22
    3 class WPorg_GP_Route_WP_Plugins extends GP_Route {
     3class WPorg_GP_Route_WP_Plugins extends WPorg_GP_Route_WP_Directory {
    44
    55    /**
     
    9494        } );
    9595
    96         if ( function_exists( 'wporg_get_plugin_icon' ) ) {
    97             $project->icon = wporg_get_plugin_icon( $project->slug, 64 );
    98         } else {
    99             $project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
    100         }
     96        $project->icon = $this->get_plugin_icon( $project, 64 );
    10197
    10298        $this->tmpl( 'projects-wp-plugins', get_defined_vars() );
     
    117113        }
    118114
    119         if ( function_exists( 'wporg_get_plugin_icon' ) ) {
    120             $project->icon = wporg_get_plugin_icon( $project->slug, 64 );
    121         } else {
    122             $project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
     115        $project->icon = $this->get_plugin_icon( $project, 64 );
     116
     117        $contributors_by_locale = gp_get_meta( 'wp-plugins', $project->id, 'contributors-by-locale' );
     118        if ( ! $contributors_by_locale || $contributors_by_locale['last_updated'] + HOUR_IN_SECONDS < time() ) {
     119            $contributors_by_locale = $this->get_contributors( $project );
     120            $contributors_by_locale['last_updated'] = time();
     121            gp_update_meta( $project->id, 'contributors-by-locale', $contributors_by_locale, 'wp-plugins' );
    123122        }
    124123
    125         $contributors_by_locale = array();
    126         $default_value = array(
    127             'count' => 0,
    128             'editors' => array(),
    129             'contributors' => array(),
    130         );
    131 
    132         $translation_editors = $wpdb->get_results( $wpdb->prepare( "
    133             SELECT
    134                 `user_id`, `locale`
    135             FROM {$wpdb->wporg_translation_editors}
    136             WHERE `project_id` = %d
    137         ", $project->id ), OBJECT );
    138 
    139         foreach ( $translation_editors as $translation_editor ) {
    140             if ( ! isset( $contributors_by_locale[ $translation_editor->locale ] ) ) {
    141                 $contributors_by_locale[ $translation_editor->locale ] = $default_value;
    142             }
    143 
    144             $user = get_user_by( 'id', $translation_editor->user_id );
    145             if ( ! $user ) {
    146                 continue;
    147             }
    148 
    149             $contributors_by_locale[ $translation_editor->locale ]['editors'][ $translation_editor->user_id ] = (object) array(
    150                 'nicename'     => $user->user_nicename,
    151                 'display_name' => $this->_encode( $user->display_name ),
    152             );
    153 
    154             $contributors_by_locale[ $translation_editor->locale ]['count']++;
     124        $chart_data = gp_get_meta( 'wp-plugins', $project->id, 'contributors-chart-data' );
     125        if ( ! $chart_data || $chart_data['last_updated'] + DAY_IN_SECONDS < time() ) {
     126            $chart_data = $this->get_contributors_chart_data( $project );
     127            $chart_data['last_updated'] = time();
     128            gp_update_meta( $project->id, 'contributors-chart-data', $chart_data, 'wp-plugins' );
    155129        }
    156130
    157         unset( $translation_editors );
    158 
    159         $sub_projects = $wpdb->get_col( $wpdb->prepare( "
    160             SELECT id
    161             FROM {$wpdb->gp_projects}
    162             WHERE parent_project_id = %d
    163         ", $project->id ) );
    164 
    165         foreach ( $sub_projects as $sub_project ) {
    166             foreach( $this->get_translation_contributors_by_locale( $sub_project ) as $row ) {
    167                 if ( ! isset( $contributors_by_locale[ $row->locale ] ) ) {
    168                     $contributors_by_locale[ $row->locale ] = $default_value;
    169                 }
    170 
    171                 if ( isset( $contributors_by_locale[ $row->locale ]['editors'][ $row->user_id ] ) ) {
    172                     continue;
    173                 }
    174 
    175                 if ( isset( $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] ) ) {
    176                     continue;
    177                 }
    178 
    179                 $user = get_user_by( 'id', $row->user_id );
    180                 if ( ! $user ) {
    181                     continue;
    182                 }
    183 
    184                 $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] = (object) array(
    185                     'nicename'     => $user->user_nicename,
    186                     'display_name' => $this->_encode( $user->display_name ),
    187                 );
    188 
    189                 $contributors_by_locale[ $row->locale ]['count']++;
    190             }
    191         }
    192 
    193         $chart_data = $this->get_plugin_contributors_chart_data( $project->id, $sub_projects );
     131        unset( $contributors_by_locale['last_updated'], $chart_data['last_updated'] );
    194132
    195133        $this->tmpl( 'projects-wp-plugins-contributors', get_defined_vars() );
    196     }
    197 
    198     /**
    199      * Generates the chart data for contributors activity.
    200      *
    201      * @param  int    $project_id   The ID of a project. Used to store data as meta.
    202      * @param  array $sub_projects Optional. IDs of sub-projects.
    203      * @return array The data to build a chart via Chartist.js.
    204      */
    205     private function get_plugin_contributors_chart_data( $project_id, $sub_projects = null ) {
    206         $chart_data = gp_get_meta( 'wp-plugins', $project_id, 'contributors-chart-data' );
    207         if ( $chart_data ) {
    208             if ( $chart_data['last_updated'] + DAY_IN_SECONDS > time() ) {
    209                 return $chart_data;
    210             }
    211         }
    212 
    213         global $wpdb;
    214 
    215         if ( ! $sub_projects ) {
    216             $sub_projects = $wpdb->get_col( $wpdb->prepare( "
    217                 SELECT id
    218                 FROM {$wpdb->gp_projects}
    219                 WHERE parent_project_id = %d
    220             ", $$project_id ) );
    221         }
    222 
    223         $translation_set_ids = $wpdb->get_col( "
    224             SELECT `id` FROM {$wpdb->gp_translation_sets} WHERE `project_id` IN (" . implode( ',', $sub_projects ) . ")
    225         " );
    226 
    227         if ( ! $translation_set_ids ) {
    228             return array();
    229         }
    230 
    231         $date_begin = new DateTime( '-6 day' );
    232         $date_end = new DateTime( 'NOW' );
    233         $date_interval = new DateInterval( 'P1D' );
    234         $date_range = new DatePeriod( $date_begin, $date_interval, $date_end );
    235 
    236         $days = array();
    237         foreach( $date_range as $date ) {
    238             $days[] = $date->format( 'Y-m-d' );
    239         }
    240         $days[] = $date_end->format( 'Y-m-d' );
    241 
    242         $counts = $wpdb->get_results( "
    243             SELECT
    244                 DATE(date_modified) AS `day`, COUNT(*) AS `count`, `status`
    245             FROM {$wpdb->gp_translations}
    246             WHERE
    247                 `translation_set_id` IN (" . implode( ',', $translation_set_ids ) . ")
    248                 AND date_modified >= ( CURDATE() - INTERVAL 7 DAY )
    249             GROUP BY `status`, `day`
    250             ORDER BY `day` DESC
    251         " );
    252 
    253         $status = array( 'current', 'waiting', 'rejected' );
    254         $data = [];
    255         foreach ( $days as $day ) {
    256             $data[ $day ] = array_fill_keys( $status, 0 );
    257             foreach ( $counts as $count ) {
    258                 if ( $count->day !== $day || ! in_array( $count->status, $status ) ) {
    259                     continue;
    260                 }
    261 
    262                 $data[ $day ][ $count->status ] = (int) $count->count;
    263             }
    264         }
    265 
    266         $labels = array_keys( $data );
    267         array_pop( $labels );
    268         $labels[] = ''; // Don't show a label for today
    269 
    270         $series = array();
    271         $series_data = array_values( $data );
    272         foreach ( $status as $stati ) {
    273             $series[] = (object) array(
    274                 'name' => $stati,
    275                 'data' => wp_list_pluck( $series_data, $stati ),
    276             );
    277         }
    278 
    279         $last_updated = time();
    280         $chart_data = compact( 'labels', 'series', 'last_updated' );
    281 
    282         gp_update_meta( $project_id, 'contributors-chart-data', $chart_data, 'wp-plugins' );
    283 
    284         return $chart_data;
    285134    }
    286135
     
    297146        }
    298147
    299         if ( function_exists( 'wporg_get_plugin_icon' ) ) {
    300             $project->icon = wporg_get_plugin_icon( $project->slug, 64 );
    301         } else {
    302             $project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
    303         }
     148        $project->icon = $this->get_plugin_icon( $project, 64 );
    304149
    305150        $http_context = stream_context_create( array(
     
    315160
    316161    /**
    317      * Retrieves translators of a specific project.
     162     * Retrieves the icon of a plugin.
    318163     *
    319      * @param int $project_id Project ID.
    320      * @return object Translators of the project.
     164     * @param GP_Project $project The plugin project.
     165     * @param int        $size    Optional. The size of the icon. Default 64.
     166     * @return string HTML markup for the icon.
    321167     */
    322     private function get_translation_contributors_by_locale( $project_id ) {
    323         global $wpdb;
     168    private function get_plugin_icon( $project, $size = 64 ) {
     169        $default = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
    324170
    325         $sql = $wpdb->prepare( "
    326             SELECT ts.`locale`, ts.`slug` AS `locale_slug`, t.`user_id`
    327             FROM `{$wpdb->gp_translations}` t, `{$wpdb->gp_translation_sets}` ts
    328             WHERE t.`translation_set_id` = ts.`id`
    329                 AND t.`user_id` IS NOT NULL AND t.`user_id` != 0
    330                 AND t.`date_modified` > %s
    331                 AND ts.`project_id` = %d
    332                 AND t.`status` <> 'rejected'
    333             GROUP BY ts.`locale`, ts.`slug`, t.`user_id`
    334         ", date( 'Y-m-d', time() - YEAR_IN_SECONDS ), $project_id );
     171        if ( function_exists( 'wporg_get_plugin_icon' ) ) {
     172            $icon = wporg_get_plugin_icon( $project->slug, $size );
     173        }
    335174
    336         return $wpdb->get_results( $sql );
    337     }
     175        if ( $icon ) {
     176            return $icon;
     177        }
    338178
    339     private function _encode( $raw ) {
    340         $raw = mb_convert_encoding( $raw, 'UTF-8', 'ASCII, JIS, UTF-8, Windows-1252, ISO-8859-1' );
    341         return ent2ncr( htmlspecialchars_decode( htmlentities( $raw, ENT_NOQUOTES, 'UTF-8' ), ENT_NOQUOTES ) );
     179        return $default;
    342180    }
    343181}
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/wp-themes.php

    r2858 r2864  
    11<?php
    22
    3 class WPorg_GP_Route_WP_Plugins extends GP_Route {
     3class WPorg_GP_Route_WP_Themes extends WPorg_GP_Route_WP_Directory {
    44
    55    /**
     
    88     * @param string $project_slug Slug of a project.
    99     */
    10     public function get_plugin_projects( $project_slug ) {
     10    public function get_theme_projects( $project_slug ) {
    1111        global $wpdb;
    1212
    13         $project_path = 'wp-plugins/' . $project_slug;
     13        $project_path = 'wp-themes/' . $project_slug;
    1414        $project = GP::$project->by_path( $project_path );
    1515        if ( ! $project ) {
     
    2626                LEFT JOIN {$wpdb->gp_projects} p ON stats.project_id = p.id
    2727            WHERE
    28                 p.parent_project_id = '{$project->id}'
     28                p.id = '{$project->id}'
    2929        " );
    3030
     
    3939                $locale_key = $set->locale . '/' . $set->locale_slug;
    4040            }
    41             $sub_project = str_replace( "$project_path/", '', $set->path );
    42             $sub_projects[ $sub_project ] = true;
    4341
    4442            /*
     
    4745             */
    4846            $percent_complete = (float) $set->percent_complete;
    49             $translation_locale_statuses[ $locale_key ][ $sub_project ] = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
     47            $translation_locale_statuses[ $locale_key ]['stable'] = ( $percent_complete > 50 ) ? floor( $percent_complete ) : ceil( $percent_complete );
    5048
    5149            // Increment the amount of waiting and untranslated strings.
     
    6260            ksort( $translation_locale_statuses[ $locale_key ], SORT_NATURAL );
    6361        }
    64 
    65         // Check if the plugin has at least one code project. These won't be created if a plugin
    66         // has no text domain defined.
    67         $sub_projects = array_keys( $sub_projects );
    68         $has_error = ( ! in_array( 'dev', $sub_projects ) && ! in_array( 'stable', $sub_projects ) );
    6962
    7063        unset( $project_path, $locale_key, $rows, $set, $sub_project, $sub_projects );
     
    9487        } );
    9588
    96         if ( function_exists( 'wporg_get_plugin_icon' ) ) {
    97             $project->icon = wporg_get_plugin_icon( $project->slug, 64 );
    98         } else {
    99             $project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
    100         }
     89        $project->icon = $this->get_theme_icon( $project, 64 );
    10190
    102         $this->tmpl( 'projects-wp-plugins', get_defined_vars() );
     91        $this->tmpl( 'projects-wp-themes', get_defined_vars() );
    10392    }
    10493
     
    10897     * @param string $project_slug Slug of a project.
    10998     */
    110     public function get_plugin_contributors( $project_slug ) {
     99    public function get_theme_contributors( $project_slug ) {
    111100        global $wpdb;
    112101
    113         $project_path = 'wp-plugins/' . $project_slug;
     102        $project_path = 'wp-themes/' . $project_slug;
    114103        $project = GP::$project->by_path( $project_path );
    115104        if ( ! $project ) {
     
    117106        }
    118107
    119         if ( function_exists( 'wporg_get_plugin_icon' ) ) {
    120             $project->icon = wporg_get_plugin_icon( $project->slug, 64 );
    121         } else {
    122             $project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
     108        $project->icon = $this->get_theme_icon( $project, 64 );
     109
     110        $contributors_by_locale = gp_get_meta( 'wp-themes', $project->id, 'contributors-by-locale' );
     111        if ( ! $contributors_by_locale || $contributors_by_locale['last_updated'] + HOUR_IN_SECONDS < time() ) {
     112            $contributors_by_locale = $this->get_contributors( $project );
     113            $contributors_by_locale['last_updated'] = time();
     114            gp_update_meta( $project->id, 'contributors-by-locale', $contributors_by_locale, 'wp-themes' );
    123115        }
    124116
    125         $contributors_by_locale = array();
    126         $default_value = array(
    127             'count' => 0,
    128             'editors' => array(),
    129             'contributors' => array(),
    130         );
    131 
    132         $translation_editors = $wpdb->get_results( $wpdb->prepare( "
    133             SELECT
    134                 `user_id`, `locale`
    135             FROM {$wpdb->wporg_translation_editors}
    136             WHERE `project_id` = %d
    137         ", $project->id ), OBJECT );
    138 
    139         foreach ( $translation_editors as $translation_editor ) {
    140             if ( ! isset( $contributors_by_locale[ $translation_editor->locale ] ) ) {
    141                 $contributors_by_locale[ $translation_editor->locale ] = $default_value;
    142             }
    143 
    144             $user = get_user_by( 'id', $translation_editor->user_id );
    145             if ( ! $user ) {
    146                 continue;
    147             }
    148 
    149             $contributors_by_locale[ $translation_editor->locale ]['editors'][ $translation_editor->user_id ] = (object) array(
    150                 'nicename'     => $user->user_nicename,
    151                 'display_name' => $this->_encode( $user->display_name ),
    152             );
    153 
    154             $contributors_by_locale[ $translation_editor->locale ]['count']++;
     117        $chart_data = gp_get_meta( 'wp-themes', $project->id, 'contributors-chart-data' );
     118        if ( ! $chart_data || $chart_data['last_updated'] + DAY_IN_SECONDS < time() ) {
     119            $chart_data = $this->get_contributors_chart_data( $project );
     120            $chart_data['last_updated'] = time();
     121            gp_update_meta( $project->id, 'contributors-chart-data', $chart_data, 'wp-themes' );
    155122        }
    156123
    157         unset( $translation_editors );
    158 
    159         $sub_projects = $wpdb->get_col( $wpdb->prepare( "
    160             SELECT id
    161             FROM {$wpdb->gp_projects}
    162             WHERE parent_project_id = %d
    163         ", $project->id ) );
    164 
    165         foreach ( $sub_projects as $sub_project ) {
    166             foreach( $this->get_translation_contributors_by_locale( $sub_project ) as $row ) {
    167                 if ( ! isset( $contributors_by_locale[ $row->locale ] ) ) {
    168                     $contributors_by_locale[ $row->locale ] = $default_value;
    169                 }
    170 
    171                 if ( isset( $contributors_by_locale[ $row->locale ]['editors'][ $row->user_id ] ) ) {
    172                     continue;
    173                 }
    174 
    175                 if ( isset( $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] ) ) {
    176                     continue;
    177                 }
    178 
    179                 $user = get_user_by( 'id', $row->user_id );
    180                 if ( ! $user ) {
    181                     continue;
    182                 }
    183 
    184                 $contributors_by_locale[ $row->locale ]['contributors'][ $row->user_id ] = (object) array(
    185                     'nicename'     => $user->user_nicename,
    186                     'display_name' => $this->_encode( $user->display_name ),
    187                 );
    188 
    189                 $contributors_by_locale[ $row->locale ]['count']++;
    190             }
    191         }
    192 
    193         $chart_data = $this->get_plugin_contributors_chart_data( $project->id, $sub_projects );
     124        unset( $contributors_by_locale['last_updated'], $chart_data['last_updated'] );
    194125
    195126        $this->tmpl( 'projects-wp-plugins-contributors', get_defined_vars() );
    196     }
    197 
    198     /**
    199      * Generates the chart data for contributors activity.
    200      *
    201      * @param  int    $project_id   The ID of a project. Used to store data as meta.
    202      * @param  array $sub_projects Optional. IDs of sub-projects.
    203      * @return array The data to build a chart via Chartist.js.
    204      */
    205     private function get_plugin_contributors_chart_data( $project_id, $sub_projects = null ) {
    206         $chart_data = gp_get_meta( 'wp-plugins', $project_id, 'contributors-chart-data' );
    207         if ( $chart_data ) {
    208             if ( $chart_data['last_updated'] + DAY_IN_SECONDS > time() ) {
    209                 return $chart_data;
    210             }
    211         }
    212 
    213         global $wpdb;
    214 
    215         if ( ! $sub_projects ) {
    216             $sub_projects = $wpdb->get_col( $wpdb->prepare( "
    217                 SELECT id
    218                 FROM {$wpdb->gp_projects}
    219                 WHERE parent_project_id = %d
    220             ", $$project_id ) );
    221         }
    222 
    223         $translation_set_ids = $wpdb->get_col( "
    224             SELECT `id` FROM {$wpdb->gp_translation_sets} WHERE `project_id` IN (" . implode( ',', $sub_projects ) . ")
    225         " );
    226 
    227         if ( ! $translation_set_ids ) {
    228             return array();
    229         }
    230 
    231         $date_begin = new DateTime( '-6 day' );
    232         $date_end = new DateTime( 'NOW' );
    233         $date_interval = new DateInterval( 'P1D' );
    234         $date_range = new DatePeriod( $date_begin, $date_interval, $date_end );
    235 
    236         $days = array();
    237         foreach( $date_range as $date ) {
    238             $days[] = $date->format( 'Y-m-d' );
    239         }
    240         $days[] = $date_end->format( 'Y-m-d' );
    241 
    242         $counts = $wpdb->get_results( "
    243             SELECT
    244                 DATE(date_modified) AS `day`, COUNT(*) AS `count`, `status`
    245             FROM {$wpdb->gp_translations}
    246             WHERE
    247                 `translation_set_id` IN (" . implode( ',', $translation_set_ids ) . ")
    248                 AND date_modified >= ( CURDATE() - INTERVAL 7 DAY )
    249             GROUP BY `status`, `day`
    250             ORDER BY `day` DESC
    251         " );
    252 
    253         $status = array( 'current', 'waiting', 'rejected' );
    254         $data = [];
    255         foreach ( $days as $day ) {
    256             $data[ $day ] = array_fill_keys( $status, 0 );
    257             foreach ( $counts as $count ) {
    258                 if ( $count->day !== $day || ! in_array( $count->status, $status ) ) {
    259                     continue;
    260                 }
    261 
    262                 $data[ $day ][ $count->status ] = (int) $count->count;
    263             }
    264         }
    265 
    266         $labels = array_keys( $data );
    267         array_pop( $labels );
    268         $labels[] = ''; // Don't show a label for today
    269 
    270         $series = array();
    271         $series_data = array_values( $data );
    272         foreach ( $status as $stati ) {
    273             $series[] = (object) array(
    274                 'name' => $stati,
    275                 'data' => wp_list_pluck( $series_data, $stati ),
    276             );
    277         }
    278 
    279         $last_updated = time();
    280         $chart_data = compact( 'labels', 'series', 'last_updated' );
    281 
    282         gp_update_meta( $project_id, 'contributors-chart-data', $chart_data, 'wp-plugins' );
    283 
    284         return $chart_data;
    285127    }
    286128
     
    290132     * @param string $project_slug Slug of a project.
    291133     */
    292     public function get_plugin_language_packs( $project_slug ) {
    293         $project_path = 'wp-plugins/' . $project_slug;
     134    public function get_theme_language_packs( $project_slug ) {
     135        $project_path = 'wp-themes/' . $project_slug;
    294136        $project = GP::$project->by_path( $project_path );
    295137        if ( ! $project ) {
     
    297139        }
    298140
    299         if ( function_exists( 'wporg_get_plugin_icon' ) ) {
    300             $project->icon = wporg_get_plugin_icon( $project->slug, 64 );
    301         } else {
    302             $project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
    303         }
     141        $project->icon = $this->get_theme_icon( $project, 64 );
    304142
    305         $http_context = stream_context_create( array(
    306             'http' => array(
    307                 'user_agent' => 'WordPress.org Translate',
    308             ),
    309         ) );
    310         $json = file_get_contents( "https://api.wordpress.org/translations/plugins/1.0/?slug={$project_slug}", null, $http_context );
    311         $language_packs = $json && '{' == $json[0] ? json_decode( $json ) : null;
     143        $language_packs = $this->get_language_packs( 'theme', $project_slug );
    312144
    313145        $this->tmpl( 'projects-wp-plugins-language-packs', get_defined_vars() );
     
    315147
    316148    /**
    317      * Retrieves translators of a specific project.
     149     * Retrieves the icon of a theme.
    318150     *
    319      * @param int $project_id Project ID.
    320      * @return object Translators of the project.
     151     * @param GP_Project $project The theme project.
     152     * @param int        $size    Optional. The size of the icon. Default 64.
     153     * @return string HTML markup for the icon.
    321154     */
    322     private function get_translation_contributors_by_locale( $project_id ) {
    323         global $wpdb;
     155    private function get_theme_icon( $project, $size = 64 ) {
     156        $default = '<div class="default-icon"><span class="dashicons dashicons-admin-themes"></span></div>';
    324157
    325         $sql = $wpdb->prepare( "
    326             SELECT ts.`locale`, ts.`slug` AS `locale_slug`, t.`user_id`
    327             FROM `{$wpdb->gp_translations}` t, `{$wpdb->gp_translation_sets}` ts
    328             WHERE t.`translation_set_id` = ts.`id`
    329                 AND t.`user_id` IS NOT NULL AND t.`user_id` != 0
    330                 AND t.`date_modified` > %s
    331                 AND ts.`project_id` = %d
    332                 AND t.`status` <> 'rejected'
    333             GROUP BY ts.`locale`, ts.`slug`, t.`user_id`
    334         ", date( 'Y-m-d', time() - YEAR_IN_SECONDS ), $project_id );
     158        $screenshot = gp_get_meta( 'wp-themes', $project->id, 'screenshot' );
     159        if ( $screenshot ) {
     160            return sprintf(
     161                '<div class="icon"><img src="%s" alt="" width="%d" height="%d"></div>',
     162                esc_url( 'https://i0.wp.com/' . $screenshot . '?w=' . $size * 2 . '&strip=all' ),
     163                $size,
     164                $size
     165            );
     166        }
    335167
    336         return $wpdb->get_results( $sql );
    337     }
    338 
    339     private function _encode( $raw ) {
    340         $raw = mb_convert_encoding( $raw, 'UTF-8', 'ASCII, JIS, UTF-8, Windows-1252, ISO-8859-1' );
    341         return ent2ncr( htmlspecialchars_decode( htmlentities( $raw, ENT_NOQUOTES, 'UTF-8' ), ENT_NOQUOTES ) );
     168        return $default;
    342169    }
    343170}
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/wporg-gp-routes.php

    r2531 r2864  
    1313require_once __DIR__ . '/routes/locale.php';
    1414require_once __DIR__ . '/routes/stats-overview.php';
     15require_once __DIR__ . '/routes/wp-directory.php';
    1516require_once __DIR__ . '/routes/wp-plugins.php';
     17require_once __DIR__ . '/routes/wp-themes.php';
    1618
    1719class WPorg_GP_Routes {
     
    3436     *  - /languages/$locale/$path
    3537     *  - /profile/$path
     38     *  - /projects/wp-plugins/?
     39     *  - /projects/wp-themes/?
    3640     *
    3741     * Adds:
     
    4549     *  - /projects/wp-plugins/$project/contributors
    4650     *  - /projects/wp-plugins/$project/language-packs
     51     *  - /projects/wp-themes/$project
     52     *  - /projects/wp-themes/$project/contributors
     53     *  - /projects/wp-themes/$project/language-packs
    4754     */
    4855    public function register_routes() {
     
    7784            GP::$router->prepend( "/projects/wp-plugins/$project/contributors", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_contributors' ) );
    7885            GP::$router->prepend( "/projects/wp-plugins/$project/language-packs", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_language_packs' ) );
     86            GP::$router->prepend( "/projects/wp-themes/$project", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_projects' ) );
     87            GP::$router->prepend( "/projects/wp-themes/$project/contributors", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_contributors' ) );
     88            GP::$router->prepend( "/projects/wp-themes/$project/language-packs", array( 'WPorg_GP_Route_WP_Themes', 'get_theme_language_packs' ) );
    7989        }
    8090    }
Note: See TracChangeset for help on using the changeset viewer.