Ticket #340: 340.5.diff
File 340.5.diff, 18.3 KB (added by , 10 years ago) |
---|
-
sites/trunk/wordpress.org/public_html/wp-content/plugins/wp-i18n-teams/css/front-end.css
1 /* 2 * Table of all locales 3 */ 4 5 .i18n-label.latest { 6 color: #468847; 7 } 8 .i18n-label.minor-behind { 9 color: #F7D708; 10 } 11 .i18n-label.major-behind-one { 12 color: #E6A45A; 13 } 14 .i18n-label.major-behind-many { 15 color: #e38587; 16 } 17 18 .locale-version.latest { 19 background: #d2e7ca; 20 } 21 .locale-version.minor-behind { 22 background: #fef0c1; 23 } 24 .locale-version.major-behind-one { 25 background: #E6A45A; 26 } 27 .locale-version.major-behind-many { 28 background: #e38587; 29 } 30 31 32 /* 33 * Details page for an individual locale 34 */ 35 36 #locale-header { 37 padding: 0 20px 20px 20px; 38 background-color: #F5F5F5; 39 border: 1px solid #E3E3E3; 40 border-radius: 4px; 41 box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05) inset; 42 } 43 #locale-header ul, 44 #main ul#postlist .postcontent #locale-header ul { 45 overflow: auto; 46 margin-left: 0 !important; /* !important is necessary here to override P2's use of !important in .postcontent ul */ 47 } 48 #locale-header ul li { 49 list-style-type: none; 50 } 51 #locale-header ul#locale-details { 52 margin-bottom: 0 !important; /* !important is necessary here to override P2's use of !important in .postcontent ul */ 53 } 54 #locale-download { 55 margin-bottom: 0 !important; /* !important is necessary here to override P2's use of !important in .postcontent ul */ 56 } 57 #locale-header ul li.download-button, 58 #main ul#postlist .postcontent #locale-header ul li.download-button { 59 float: left; 60 height: 34px; 61 margin: 8px 12px 0 0; /* todo put list spacing on the button itself, so that the ul takes up no space when there are no li's inside it */ 62 padding: 6px 12px; 63 } 64 #locale-header ul li.download-button a { 65 font-size: 1.1em; 66 color: white; 67 } 68 #locale-details, 69 #main ul#locale-details { 70 font-size: 1.2em; 71 } 72 73 .validators, 74 #main ul#postlist .postcontent ul.validators { 75 overflow: auto; 76 margin: 0 !important; /* !important is necessary here to override P2's use of !important in .postcontent ul */ 77 padding: 0; 78 } 79 .validators li { 80 float: left; 81 width: 280px; /* todo use flexbox to prog enhance this so that they fill the space. AF is good one to test */ 82 padding-bottom: 15px; 83 list-style-type: none; 84 } 85 .validators .gravatar { 86 position: relative; 87 top: -6px; 88 float: left; 89 margin: 0 10px 10px 0; 90 padding: 2px; 91 } 92 .validators a { 93 display: block; 94 margin: 6px 0 2px; 95 font-size: 16px; 96 text-decoration: none; 97 } 98 99 .translators-info table { 100 width: 100%; 101 margin: .7em 0 1.5em; 102 border: 1px solid #ccc; 103 border-width: 1px 1px 1px 0; 104 border-spacing: 0; 105 } 106 .translators-info th { 107 background: #ccc; 108 border: 1px solid #ccc; 109 border-width: 1px 0 0 1px; 110 padding: 6px 15px; 111 } 112 .translators-info td { 113 border: 1px solid #ccc; 114 border-width: 1px 0 0 1px; 115 padding: 6px 15px; 116 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wp-i18n-teams/views/all-locales.php
1 <?php if ( $locale_data ) : ?> 2 3 <div class="translators-info"> 4 <p> 5 <?php printf( 6 __( '%s locales up-to-date. %s locales behind by one minor version. %s locales behind by one major version. %s locales behind by two or more major versions. %s locales do not yet have a package available.', 'wporg' ), 7 '<strong class="i18n-label latest">' . $locale_data['status_counts']['latest'] . '</strong>', 8 '<strong class="i18n-label minor-behind">' . $locale_data['status_counts']['minor-behind'] . '</strong>', 9 '<strong class="i18n-label major-behind-one">' . $locale_data['status_counts']['major-behind-one'] . '</strong>', 10 '<strong class="i18n-label major-behind-many">' . $locale_data['status_counts']['major-behind-many'] . '</strong>', 11 '<strong class="i18n-label no-site no-releases">' . ( $locale_data['status_counts']['no-site'] + $locale_data['status_counts']['no-releases'] ) . '</strong>' 12 ); ?> 13 </p> 14 15 <table> 16 <thead> 17 <tr> 18 <th><?php _e( 'Locale Name', 'wporg' ); ?></th> 19 <th><?php _e( 'Native Name', 'wporg' ); ?></th> 20 <th><?php _e( 'Locale Code', 'wporg' ); ?></th> 21 <th><?php _e( 'WordPress Locale', 'wporg' ); ?></th> 22 <th><?php _e( 'Version', 'wporg' ); ?></th> 23 <th><!-- intentionally blank --></th> 24 </tr> 25 </thead> 26 27 <tbody> 28 <?php foreach ( $locales as $locale ) : ?> 29 <tr class="locale-version <?php echo esc_attr( $locale_data[ $locale->wp_locale ]['status'] ); ?>"> 30 <td><?php echo esc_html( $locale->english_name ); ?></td> 31 <td><?php echo esc_html( $locale->native_name ); ?></td> 32 33 <td> 34 <?php if ( $locale_data[ $locale->wp_locale ]['rosetta_site_url'] ) : ?> 35 <a href="<?php echo esc_url( $locale_data[ $locale->wp_locale ]['rosetta_site_url'] ); ?>"> 36 <?php echo esc_html( $locale->slug ); ?> 37 </a> 38 <?php else : ?> 39 <?php echo esc_html( $locale->slug ); ?> 40 <?php endif; ?> 41 </td> 42 43 <td><?php echo esc_html( $locale->wp_locale ); ?></td> 44 45 <td> 46 <?php 47 if ( $locale_data[ $locale->wp_locale ]['rosetta_site_url'] ) { 48 if ( $locale_data[ $locale->wp_locale ]['latest_release'] ) { 49 echo esc_html( $locale_data[ $locale->wp_locale ]['latest_release'] ); 50 } else { 51 _e( 'None', 'wporg' ); 52 } 53 } else { 54 _e( 'No site', 'wporg' ); 55 } 56 ?> 57 </td> 58 59 <td> 60 <a href="<?php echo esc_url( add_query_arg( 'locale', $locale->wp_locale ) ); ?>"> 61 <?php _e( 'More', 'wporg' ); ?> 62 </a> 63 </td> 64 </tr> 65 <?php endforeach; ?> 66 </tbody> 67 </table> 68 </div> <!-- /.translators-info --> 69 70 <?php else : ?> 71 72 <p class="alert alert-info" role="alert"> 73 <?php _e( 'Localization data is not available.', 'wporg' ); ?> 74 </p> 75 76 <?php endif; ?> -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wp-i18n-teams/views/locale-details.php
1 <?php if ( $locale_data && is_a( $locale, 'GP_Locale' ) ) : ?> 2 <?php $locale_data = $locale_data[ $locale->wp_locale ]; ?> 3 4 <div id="locale-header"> 5 <h1> 6 <?php echo esc_html( $locale->native_name ); ?> 7 8 <?php if ( $locale->native_name != $locale->english_name ) : ?> 9 / <?php echo esc_html( $locale->english_name ); ?> 10 <?php endif; ?> 11 </h1> 12 13 <ul id="locale-details"> 14 <li> 15 <strong><?php _e( 'Locale site:', 'wporg' ); ?></strong> 16 <a href="<?php echo esc_url( $locale_data['rosetta_site_url'] ); ?>"><?php _e( 'View locale site', 'wporg' ); ?></a> 17 </li> 18 19 <li> 20 <strong><?php _e( 'Version:', 'wporg' ); ?></strong> 21 <?php echo $locale_data['latest_release'] ? esc_html( $locale_data['latest_release'] ) : __( 'None available', 'wporg' ); ?> 22 </li> 23 24 <li> 25 <strong><?php _e( 'WordPress Locale:', 'wporg' ); ?></strong> 26 <?php echo esc_html( $locale->wp_locale ); ?> 27 </li> 28 </ul> 29 30 <?php if ( $locale_data['localized_core_url'] ) : ?> 31 <ul id="locale-download"> 32 <li class="button download-button"> 33 <a href="<?php echo esc_url( $locale_data['localized_core_url'] ); ?>" role="button"> 34 <?php printf( __( 'Download WordPress in %s', 'wporg' ), $locale->native_name ); ?> 35 </a> 36 </li> 37 38 <?php if ( $locale_data['language_pack_url'] ) : ?> 39 <li class="button download-button"> 40 <a href="<?php echo esc_url( $locale_data['language_pack_url'] ); ?>" role="button"> 41 <?php // translators: %s is the latest version ?> 42 <?php printf( __( 'Download language pack (%s)', 'wporg' ), $locale_data['latest_release'] ); ?> 43 </a> 44 </li> 45 <?php endif; ?> 46 </ul> 47 <?php endif; ?> 48 </div> 49 50 51 <h2>Validators</h2> 52 53 <?php if ( empty( $locale_data['validators'] ) ) : ?> 54 <p><?php printf( __( '%s does not have any validators yet.', 'wporg' ), $locale->native_name ); ?></p> 55 <?php else : ?> 56 <ul class="validators"> 57 <?php foreach ( $locale_data['validators'] as $validator ) : ?> 58 <li> 59 <a href="https://profiles.wordpress.org/<?php echo esc_attr( $validator[2] ); ?>"> 60 <img src="https://secure.gravatar.com/avatar/<?php echo esc_attr( $validator[1] ); ?>?size=60" class="gravatar" alt="<?php echo esc_attr( $validator[0] ); ?>" /> 61 <?php echo esc_html( $validator[0] ); ?> 62 </a> 63 </li> 64 <?php endforeach; ?> 65 </ul> 66 <?php endif; ?> 67 68 69 <h2>Translators</h2> 70 71 <?php if ( empty( $locale_data['translators'] ) ) : ?> 72 <p><?php printf( __( '%s does not have any translators yet.', 'wporg' ), $locale->native_name ); ?></p> 73 <?php else : ?> 74 <ul> 75 <?php foreach ( $locale_data['translators'] as $username => $name ) : ?> 76 <li> 77 <a href="https://profiles.wordpress.org/<?php echo esc_attr( $username ); ?>"> 78 <?php echo esc_html( $name ); ?> 79 </a> 80 </li> 81 <?php endforeach; ?> 82 </ul> 83 <?php endif; ?> 84 85 <p class="alert alert-info" role="alert"> 86 <a href="https://translate.wordpress.org/languages/<?php echo esc_attr( $locale->slug ); ?>"> 87 <?php printf( __( 'Become a translator yourself, check if %s needs some help!', 'wporg' ), $locale->native_name ); ?> 88 </a> 89 </p> 90 91 <?php else : ?> 92 93 <p class="alert alert-info" role="alert"> 94 <?php _e( 'Localization data is not available.', 'wporg' ); ?> 95 </p> 96 97 <?php endif; ?> -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wp-i18n-teams/wp-i18n-teams.php
1 <?php 2 /* 3 Plugin Name: WP I18N Teams 4 Description: Provides shortcodes for displaying details about translation teams. 5 Version: 0.9 6 License: GPLv2 or later 7 Author: Marko Heijnen 8 Author URI: http://www.markoheijnen.com 9 Text Domain: wporg 10 */ 11 12 13 /* 14 * TODO 15 * 16 * inline TODOs 17 * cross browser testing 18 * fix cron bug 19 * final review + double check security 20 * 21 * ask nacin for a sanity check on cache_locale_data() and get_contributors() 22 * deploy new API_WPORG_PATH constant and wp4.css (including header cachebuster) changes before deploying this 23 */ 24 25 26 class WP_I18n_Teams { 27 const LOCALE_DATA_TRANSIENT_KEY = 'wit_locale_data'; 28 29 /** 30 * Constructor 31 */ 32 public function __construct() { 33 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) ); 34 add_filter( 'cron_schedules', array( $this, 'add_cron_intervals' ) ); 35 add_action( 'wit_refresh_cache', array( $this, 'cache_locale_data' ) ); // todo this runs when visiting wp-cron directly, but not when by visiting pages 36 37 add_shortcode( 'wp-locales', array( $this, 'wp_locales' ) ); 38 39 $this->activate(); // todo shouldn't need this once cron bug is fixed 40 } 41 42 /** 43 * Prepares site to use the plugin during activation 44 */ 45 public function activate() { 46 if ( false === wp_next_scheduled( 'wit_refresh_cache' ) ) { 47 wp_schedule_event( time(), 'wit_five_minutes', 'wit_refresh_cache' ); 48 } 49 } 50 51 /** 52 * Rolls back activation procedures when de-activating the plugin 53 */ 54 public function deactivate() { 55 wp_clear_scheduled_hook( 'wit_refresh_cache' ); 56 } 57 58 /** 59 * Enqueue JavaScript and CSS 60 */ 61 public function enqueue_assets() { 62 wp_enqueue_style( 'wp-i18n-teams', plugins_url( 'css/front-end.css', __FILE__ ), array(), 1 ); 63 } 64 65 /** 66 * Adds custom intervals to the cron schedule. 67 * 68 * @param array $schedules 69 * 70 * @return array 71 */ 72 public function add_cron_intervals( $schedules ) { 73 $schedules[ 'wit_five_minutes' ] = array( 74 'interval' => 5 * MINUTE_IN_SECONDS, 75 'display' => 'Every 5 minutes' 76 ); 77 78 return $schedules; 79 } 80 81 /** 82 * Render the [wp-locales] shortcode. 83 * 84 * @param array $attributes 85 * 86 * @return string 87 */ 88 public function wp_locales( $attributes ) { 89 //do_action( 'wit_refresh_cache' ); // todo 90 // delete_transient( self::LOCALE_DATA_TRANSIENT_KEY ); // todo 91 92 $locale_data = get_transient( self::LOCALE_DATA_TRANSIENT_KEY ); 93 94 ob_start(); 95 96 if ( empty( $_GET['locale'] ) ) { 97 $locales = self::get_locales(); 98 require( __DIR__ . '/views/all-locales.php' ); 99 } else { 100 require_once( WPORGPATH . 'translate/glotpress/locales/locales.php' ); 101 $locale = GP_Locales::by_field( 'wp_locale', $_GET['locale'] ); 102 require( __DIR__ . '/views/locale-details.php' ); 103 } 104 105 return ob_get_clean(); 106 } 107 108 /** 109 * Get GlotPress locales that have a wp_locale, sorted alphabetically. 110 * 111 * @return array 112 */ 113 protected static function get_locales() { 114 require_once( WPORGPATH . 'translate/glotpress/locales/locales.php' ); 115 116 $locales = GP_Locales::locales(); 117 $locales = array_filter( $locales, array( __CLASS__, 'filter_locale_for_wp' ) ); 118 usort( $locales, array( __CLASS__, 'sort_locales' ) ); 119 120 return $locales; 121 } 122 123 /** 124 * Remove locales that are missing a wp_locale. 125 * 126 * This is a callback for array_filter(). 127 * 128 * @param GP_Locale $element 129 * 130 * @return bool 131 */ 132 protected static function filter_locale_for_wp( $element ) { 133 if ( isset( $element->wp_locale ) ) { 134 return true; 135 } else { 136 return false; 137 } 138 } 139 140 /** 141 * Sort GlotPress locales alphabetically by the English name. 142 * 143 * @param GP_Locale $a 144 * @param GP_Locale $b 145 * 146 * @return int 147 */ 148 protected static function sort_locales( $a, $b ) { 149 return strcmp( $a->english_name, $b->english_name ); 150 } 151 152 /** 153 * Gather all the required data and cache it. 154 */ 155 public function cache_locale_data() { 156 error_log(__METHOD__); // todo 157 158 global $wpdb; 159 $locales = WP_I18n_Teams::get_locales(); 160 161 $locale_wporg_info = $wpdb->get_results( " 162 SELECT locale, subdomain, latest_release 163 FROM locales 164 ORDER BY locale 165 LIMIT 400" 166 ); 167 168 // Reindex locale wporg info so that locales can be accessed directly 169 foreach ( $locale_wporg_info as $index => $info ) { 170 unset( $locale_wporg_info[ $index ] ); 171 $locale_wporg_info[ $info->locale ] = $info; 172 } 173 174 foreach( $locales as $locale ) { 175 $contributors = self::get_contributors( $locale ); 176 $latest_release = empty( $locale_wporg_info[ $locale->wp_locale ]->latest_release ) ? false : $locale_wporg_info[ $locale->wp_locale ]->latest_release; 177 $latest_branch = explode( '.', $latest_release ); 178 $latest_branch = $latest_branch[0] .'.'. $latest_branch[1]; 179 $rosetta_site_url = empty( $locale_wporg_info[ $locale->wp_locale ]->subdomain ) ? '' : 'https://' . $locale_wporg_info[ $locale->wp_locale ]->subdomain . '.wordpress.org'; 180 $localized_core_url = $rosetta_site_url && $latest_release ? sprintf( '%s/wordpress-%s-%s.zip', $rosetta_site_url, $latest_release, $locale->wp_locale ) : ''; 181 // todo not sure localized url is always in this format? find where it's generated to verify. if have to derive it, try to get them all at once instead of 100+ calls 182 $language_pack_url = $latest_release ? sprintf( 'https://downloads.wordpress.org/translation/core/%s/%s.zip', $latest_branch, $locale->wp_locale ) : ''; 183 // todo verify that want to use $latest_branch here, vs $latest_release or CORE_LATEST_STABLE_BRANCH or CORE_LATEST_STABLE_RELEASE 184 $locale_status = self::get_locale_status( $rosetta_site_url, $latest_release ); 185 186 $locale_data[ $locale->wp_locale ] = array( 187 'status' => $locale_status, 188 'rosetta_site_url' => $rosetta_site_url, 189 'localized_core_url' => $localized_core_url, 190 'language_pack_url' => $language_pack_url, 191 'latest_release' => $latest_release, 192 'validators' => $contributors['validators'], 193 'translators' => $contributors['translators'], 194 ); 195 196 $locale_data['status_counts'][ $locale_status ]++; 197 } 198 199 error_log( count( $locale_data ) ); // todo 200 set_transient( self::LOCALE_DATA_TRANSIENT_KEY, $locale_data ); 201 } 202 203 /** 204 * Get the translators and validators for the given locale. 205 * 206 * @param GP_Locale $locale 207 * @return array 208 */ 209 public static function get_contributors( $locale ) { 210 require_once( API_WPORG_PATH . 'core/credits/wp-credits.php' ); 211 212 $credits = WP_Credits::factory( WP_CORE_LATEST_RELEASE, $locale ); 213 $results = $credits->get_results(); 214 215 $contributors = array( 216 'validators' => ! empty( $results['groups']['validators']['data'] ) ? $results['groups']['validators']['data'] : array(), 217 'translators' => ! empty( $results['groups']['translators']['data'] ) ? $results['groups']['translators']['data'] : array(), 218 ); 219 220 return $contributors; 221 } 222 223 /** 224 * Determine the status of the given locale 225 * 226 * @param string $rosetta_site_url 227 * @param string $latest_release 228 * 229 * @return string 230 */ 231 protected static function get_locale_status( $rosetta_site_url, $latest_release ) { 232 if ( $rosetta_site_url ) { 233 if ( $latest_release ) { 234 $one_lower = WP_CORE_LATEST_RELEASE - 0.1; 235 236 if ( $latest_release == WP_CORE_LATEST_RELEASE ) { 237 $status = 'latest'; 238 } else if ( substr( $latest_release, 0, 3 ) == substr( WP_CORE_LATEST_RELEASE, 0, 3 ) ) { 239 $status = 'minor-behind'; 240 } else if ( substr( $latest_release, 0, 3 ) == substr( $one_lower, 0, 3 ) ) { 241 $status = 'major-behind-one'; 242 } else { 243 $status = 'major-behind-many'; 244 } 245 } else { 246 $status = 'no-releases'; 247 } 248 } else { 249 $status = 'no-site'; 250 } 251 252 return $status; 253 } 254 } 255 256 $GLOBALS['wp_i18n_teams'] = new WP_I18n_Teams(); 257 register_activation_hook( __FILE__, array( $GLOBALS['wp_i18n_teams'], 'activate' ) ); 258 register_deactivation_hook( __FILE__, array( $GLOBALS['wp_i18n_teams'], 'deactivate' ) );