Making WordPress.org

Changeset 2439


Ignore:
Timestamp:
01/31/2016 10:58:15 PM (9 years ago)
Author:
ocean90
Message:

Translate: Add child pages to plugin projects to show contributors and language packs, first pass.

See #1388.

Location:
sites/trunk
Files:
6 added
5 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/translate.wordpress.org/public_html/gp-templates-new/helper-functions.php

    r2279 r2439  
    22wp_register_style( 'wporg-translate', 'https://wordpress.org/translate/gp-templates-new/style.css', array( 'base' ), '20151123' );
    33gp_enqueue_style( 'wporg-translate' );
     4
     5wp_register_style( 'chartist', 'https://wordpress.org/translate/gp-templates-new/css/chartist.min.css', array(), '0.9.5' );
     6wp_register_script( 'chartist', 'https://wordpress.org/translate/gp-templates-new/js/chartist.min.js', array(), '0.9.5' );
     7
    48
    59/**
  • sites/trunk/translate.wordpress.org/public_html/gp-templates-new/projects-wp-plugins.php

    r2285 r2439  
    3030            <ul class="project-meta">
    3131                <li class="project-name"><?php echo $project->name; ?> <?php echo $edit_link; ?></li>
     32            </ul>
     33        </div>
     34
     35        <div class="project-box-footer">
     36            <ul class="projects-dropdown">
     37                <li><span>Projects</span>
     38                    <ul>
     39                        <li><a href="<?php echo gp_url_join( gp_url_project( $project ), 'contributors' ); ?>">Contributors</a></li>
     40                        <li><a href="<?php echo gp_url_join( gp_url_project( $project ), 'language-packs' ); ?>">Language Packs</a></li>
     41                    </ul>
     42                </li>
    3243            </ul>
    3344        </div>
     
    128139        }
    129140    });
     141
     142    $( '.projects-dropdown > li' ).on( 'click', function() {
     143        $( this ).parent( '.projects-dropdown' ).toggleClass( 'open' );
     144    });
    130145});
    131146</script>
  • sites/trunk/translate.wordpress.org/public_html/gp-templates-new/style.css

    r2118 r2439  
    734734}
    735735
     736ul.projects-dropdown {
     737    border-top: 1px solid #ccc;
     738}
     739
     740.project-status-progress + .project-box-footer > ul.projects-dropdown {
     741    border-top: 0;
     742}
     743
    736744ul.projects-dropdown,
    737745ul.projects-dropdown ul {
     
    764772    box-shadow: 1px 1px 1px 0 rgba(0,0,0,0.04);
    765773    display: none;
     774    z-index: 10;
    766775}
    767776
     
    10511060    border-top: none;
    10521061}
     1062
     1063.project-sub-page h3 {
     1064    margin: 30px 0 10px;
     1065    font-size: 1.2em;
     1066}
     1067
     1068.project-sub-page p {
     1069    margin-bottom: 1em;
     1070}
     1071
     1072ul.contributors-list,
     1073ul.language-packs-list {
     1074    margin: 30px 0;
     1075    padding: 0;
     1076    list-style: none;
     1077}
     1078
     1079.contributors-list li,
     1080.language-packs-list li {
     1081    margin: 10px 0 10px 30px;
     1082}
     1083
     1084.contributors-list li strong,
     1085.language-packs-list li strong {
     1086    display: block;
     1087    margin-left: -30px;
     1088}
     1089
     1090.ct-chart-contributors {
     1091    position: relative;
     1092}
     1093
     1094.ct-chart-contributors .ct-series-a .ct-area { /* current */
     1095    fill: #c1e1b9;
     1096    fill-opacity: .5;
     1097}
     1098
     1099.ct-chart-contributors .ct-series-b .ct-area { /* waiting */
     1100    fill: #ffe399;
     1101    fill-opacity: .5;
     1102}
     1103
     1104.ct-chart-contributors .ct-series-c .ct-area { /* rejected */
     1105    fill: #f1adad;
     1106    fill-opacity: .5;
     1107}
     1108
     1109ul.ct-legend {
     1110    position: absolute;
     1111    right: 0;
     1112    top: -15px;
     1113    padding: 0;
     1114    list-style: none;
     1115    z-index: 5;
     1116}
     1117
     1118.ct-legend li {
     1119    position: relative;
     1120    display: inline-block;
     1121    padding-left: 23px;
     1122    margin-right: 3px;
     1123    cursor: pointer;
     1124}
     1125
     1126.ct-legend li:before {
     1127    content: '';
     1128    position: absolute;
     1129    top: 2px;
     1130    left: 0;
     1131    width: 12px;
     1132    height: 12px;
     1133    border: 3px solid transparent;
     1134    border-radius: 2px;
     1135}
     1136
     1137.ct-legend li.inactive:before {
     1138    background: transparent;
     1139}
     1140
     1141.ct-legend .ct-series-0:before { /* current */
     1142    background-color: #c1e1b9;
     1143    border-color: #c1e1b9;
     1144}
     1145
     1146.ct-legend .ct-series-1:before { /* waiting */
     1147    background-color: #ffe399;
     1148    border-color: #ffe399;
     1149}
     1150
     1151.ct-legend .ct-series-2:before { /* rejected */
     1152    background-color: #f1adad;
     1153    border-color: #f1adad;
     1154}
     1155
     1156@media (min-width: 600px) {
     1157    .contributors-list {
     1158        -webkit-column-count: 2;
     1159        -webkit-column-gap: 3%;
     1160        -moz-column-count: 2;
     1161        -moz-column-gap: 3%;
     1162        column-count: 2;
     1163        column-gap: 3%;
     1164    }
     1165}
     1166
     1167.contributors-list-box {
     1168    display: inline-block;
     1169    width: 100%;
     1170    margin-bottom: 20px;
     1171    padding-bottom: 10px;
     1172    border: 1px solid #ccc;
     1173    background: #fff;
     1174    -webkit-box-sizing: border-box;
     1175    box-sizing: border-box;
     1176    -webkit-column-break-inside: avoid;
     1177    page-break-inside: avoid;
     1178    break-inside: avoid;
     1179}
     1180
     1181.contributors-list-box h4 {
     1182    position: relative;
     1183    display: block;
     1184    padding: 5px 8px;
     1185    margin-bottom: 10px;
     1186    background: #f9f9f9;
     1187    border-bottom: 1px solid #ccc;
     1188    font-size: 14px;
     1189}
     1190
     1191.contributors-list-box .contributors-count {
     1192    font-size: 11px;
     1193    color: #82878c;
     1194    padding-left: 3px;
     1195}
     1196
     1197.contributors-list-box .locale-code {
     1198    position: absolute;
     1199    top: 1px;
     1200    right: 5px;
     1201    font-size: 10px;
     1202    color: #b4b9be;
     1203}
     1204
     1205.contributors-list-box p {
     1206    margin-bottom: .5em;
     1207    padding: 0 8px;
     1208}
     1209
     1210.contributors-list-box p:last-child {
     1211    margin-bottom: 0;
     1212}
     1213
     1214.contributors-list-search {
     1215    margin: 15px 0 10px;
     1216    text-align: right;
     1217}
     1218
     1219.contributors-list-search input {
     1220    margin: 0;
     1221    padding: 2px 4px;
     1222    width: 280px;
     1223    font-size: 13px;
     1224    font-weight: 300;
     1225    border: 1px solid #ddd;
     1226    -webkit-box-shadow: inset 0 1px 2px rgba( 0, 0, 0, 0.07 );
     1227    box-shadow: inset 0 1px 2px rgba( 0, 0, 0, 0.07 );
     1228    background-color: #fff;
     1229    color: #333;
     1230    outline: none;
     1231    -webkit-border-radius: 0;
     1232    border-radius: 0;
     1233    -webkit-appearance: textfield;
     1234}
     1235
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/routes/wp-plugins.php

    r2267 r2439  
    33class WPorg_GP_Route_WP_Plugins extends GP_Route {
    44
     5    /**
     6     * Prints stats about sub-project of a specific project.
     7     *
     8     * @param string $project_slug Slug of a project.
     9     */
    510    public function get_plugin_projects( $project_slug ) {
    611        global $wpdb;
     
    97102        $this->tmpl( 'projects-wp-plugins', get_defined_vars() );
    98103    }
     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        }
     124
     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']++;
     155        }
     156
     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 );
     194
     195        $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( 'current' => 0, 'waiting' => 0, 'rejected' => 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
     267        $labels = array_keys( $data );
     268        array_pop( $labels );
     269        $labels[] = ''; // Don't show a label for today
     270
     271        $series = array();
     272        $series_data = array_values( $data );
     273        foreach ( $status as $stati ) {
     274            $series[] = (object) array(
     275                'name' => $stati,
     276                'data' => wp_list_pluck( $series_data, $stati ),
     277            );
     278        }
     279
     280        $last_updated = time();
     281        $chart_data = compact( 'labels', 'series', 'last_updated' );
     282
     283        gp_update_meta( $project_id, 'contributors-chart-data', $chart_data, 'wp-plugins' );
     284
     285        return $chart_data;
     286    }
     287
     288    /**
     289     * Prints stats about language packs of a specific project.
     290     *
     291     * @param string $project_slug Slug of a project.
     292     */
     293    public function get_plugin_language_packs( $project_slug ) {
     294        $project_path = 'wp-plugins/' . $project_slug;
     295        $project = GP::$project->by_path( $project_path );
     296        if ( ! $project ) {
     297            return $this->die_with_404();
     298        }
     299
     300        if ( function_exists( 'wporg_get_plugin_icon' ) ) {
     301            $project->icon = wporg_get_plugin_icon( $project->slug, 64 );
     302        } else {
     303            $project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
     304        }
     305
     306        $http_context = stream_context_create( array(
     307            'http' => array(
     308                'user_agent' => 'WordPress.org Translate',
     309            ),
     310        ) );
     311        $json = file_get_contents( "https://api.wordpress.org/translations/plugins/1.0/?slug={$project_slug}", null, $http_context );
     312        $language_packs = $json && '{' == $json[0] ? json_decode( $json ) : null;
     313
     314
     315        $this->tmpl( 'projects-wp-plugins-language-packs', get_defined_vars() );
     316    }
     317
     318    /**
     319     * Retrieves translators of a specific project.
     320     *
     321     * @param int $project_id Project ID.
     322     * @return object Translators of the project.
     323     */
     324    private function get_translation_contributors_by_locale( $project_id ) {
     325        global $wpdb;
     326
     327        $sql = $wpdb->prepare( "
     328            SELECT ts.`locale`, ts.`slug` AS `locale_slug`, t.`user_id`
     329            FROM `{$wpdb->gp_translations}` t, `{$wpdb->gp_translation_sets}` ts
     330            WHERE t.`translation_set_id` = ts.`id`
     331                AND t.`user_id` IS NOT NULL AND t.`user_id` != 0
     332                AND t.`date_modified` > %s
     333                AND ts.`project_id` = %d
     334                AND t.`status` <> 'rejected'
     335            GROUP BY ts.`locale`, ts.`slug`, t.`user_id`
     336        ", date( 'Y-m-d', time() - YEAR_IN_SECONDS ), $project_id );
     337
     338        return $wpdb->get_results( $sql );
     339    }
     340
     341    private function _encode( $raw ) {
     342        $raw = mb_convert_encoding( $raw, 'UTF-8', 'ASCII, JIS, UTF-8, Windows-1252, ISO-8859-1' );
     343        return ent2ncr( htmlspecialchars_decode( htmlentities( $raw, ENT_NOQUOTES, 'UTF-8' ), ENT_NOQUOTES ) );
     344    }
    99345}
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/wporg-gp-routes.php

    r2278 r2439  
    4343     *  - /stats/?
    4444     *  - /projects/wp-plugins/$project
     45     *  - /projects/wp-plugins/$project/contributors
     46     *  - /projects/wp-plugins/$project/language-packs
    4547     */
    4648    public function register_routes() {
     
    7173            $project = '([^/]*)/?';
    7274            GP::$router->prepend( "/projects/wp-plugins/$project", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_projects' ) );
     75            GP::$router->prepend( "/projects/wp-plugins/$project/contributors", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_contributors' ) );
     76            GP::$router->prepend( "/projects/wp-plugins/$project/language-packs", array( 'WPorg_GP_Route_WP_Plugins', 'get_plugin_language_packs' ) );
    7377        }
    7478    }
Note: See TracChangeset for help on using the changeset viewer.