| | 1 | <?php |
| | 2 | /** |
| | 3 | * Handles automatic download of translations |
| | 4 | * |
| | 5 | * @package BuddyPress |
| | 6 | * @subpackage CoreAdministration |
| | 7 | * @since BuddyPress (1.8) |
| | 8 | */ |
| | 9 | |
| | 10 | // Exit if accessed directly |
| | 11 | if ( ! defined( 'ABSPATH' ) ) exit; |
| | 12 | |
| | 13 | /** |
| | 14 | * Creates an instance of the BP_Translate class that downloads translations for BuddyPress automatically |
| | 15 | * |
| | 16 | * @since BuddyPress (1.8) |
| | 17 | */ |
| | 18 | function bp_automatic_translations() { |
| | 19 | if ( ! current_user_can( 'update_plugins' ) ) |
| | 20 | return; |
| | 21 | |
| | 22 | if ( ! is_admin() || ( is_multisite() && ! is_network_admin() ) ) |
| | 23 | return; |
| | 24 | |
| | 25 | // Store a reference to the BP_Translate object in the BP global for other plugins |
| | 26 | buddypress()->translate = BP_Translate::get_instance(); |
| | 27 | } |
| | 28 | add_action( 'bp_admin_init', 'bp_automatic_translations' ); |
| | 29 | |
| | 30 | /** |
| | 31 | * If we're in the WordPress dashboard, and a pending translation is available, bump the update count. |
| | 32 | * |
| | 33 | * This has to be hooked before admin_init due to wp_get_update_data() being invoked in wp-admin/menu.php before the admin_init action is called. |
| | 34 | * |
| | 35 | * @since BuddyPress (1.8) |
| | 36 | */ |
| | 37 | function bp_admin_maybe_bump_update_count() { |
| | 38 | if ( ! current_user_can( 'update_plugins' ) ) |
| | 39 | return; |
| | 40 | |
| | 41 | if ( ! is_admin() || ( is_multisite() && ! is_network_admin() ) ) |
| | 42 | return; |
| | 43 | |
| | 44 | add_filter( 'wp_get_update_data', array( 'BP_Translate', 'maybe_bump_update_count' ) ); |
| | 45 | } |
| | 46 | add_action( 'bp_init', 'bp_admin_maybe_bump_update_count' ); |
| | 47 | |
| | 48 | /** |
| | 49 | * Fetch translations from http://translate.wordpress.org/ and display an update prompt on the admin dashboard. |
| | 50 | * |
| | 51 | * @since BuddyPress (1.8) |
| | 52 | */ |
| | 53 | class BP_Translate { |
| | 54 | |
| | 55 | /** |
| | 56 | * Singleton instance of the BP_Translate class |
| | 57 | * |
| | 58 | * @since BuddyPress (1.8) |
| | 59 | * @var BP_Translate |
| | 60 | */ |
| | 61 | private static $instance; |
| | 62 | |
| | 63 | /** |
| | 64 | * Return the singleton instance of the BP_Translate class |
| | 65 | * |
| | 66 | * @return BP_Translate |
| | 67 | * @since BuddyPress (1.8) |
| | 68 | */ |
| | 69 | static public function get_instance() { |
| | 70 | if ( ! self::$instance ) |
| | 71 | self::$instance = new BP_Translate; |
| | 72 | |
| | 73 | return self::$instance; |
| | 74 | } |
| | 75 | |
| | 76 | /** |
| | 77 | * Constructor |
| | 78 | * |
| | 79 | * @since BuddyPress (1.8) |
| | 80 | */ |
| | 81 | public function __construct() { |
| | 82 | $this->register_actions(); |
| | 83 | $this->register_cron(); // Intentionally after actions |
| | 84 | } |
| | 85 | |
| | 86 | /** |
| | 87 | * Hook into actions necessary to automate the translation process and customise wp-admin |
| | 88 | * |
| | 89 | * @since BuddyPress (1.8) |
| | 90 | */ |
| | 91 | protected function register_actions() { |
| | 92 | add_action( 'bp_translate_update_check', array( __CLASS__, 'check_for_updated_translation' ) ); |
| | 93 | add_action( 'core_upgrade_preamble', array( __CLASS__, 'updates_screen' ) ); |
| | 94 | add_action( 'update-core-custom_do-update-buddypress-translation', array( __CLASS__, 'updates_screen_iframe' ) ); |
| | 95 | add_action( 'update-custom_update-buddypress-translation', array( __CLASS__, 'update_translation' ) ); |
| | 96 | } |
| | 97 | |
| | 98 | /** |
| | 99 | * Register cron task to check for language updates |
| | 100 | * |
| | 101 | * @since BuddyPress (1.8) |
| | 102 | */ |
| | 103 | protected function register_cron() { |
| | 104 | if ( ! wp_next_scheduled( 'bp_translate_update_check' ) ) |
| | 105 | wp_schedule_event( time(), 'daily', 'bp_translate_update_check' ); |
| | 106 | } |
| | 107 | |
| | 108 | /** |
| | 109 | * Find out if there's a newer translation available for this site on translate.wordpress.org |
| | 110 | * |
| | 111 | * @since BuddyPress (1.8) |
| | 112 | */ |
| | 113 | static public function check_for_updated_translation() { |
| | 114 | $locale = BP_Translate::get_locale(); |
| | 115 | if ( 'en_US' === $locale ) |
| | 116 | return; |
| | 117 | |
| | 118 | // No point checking if we know there's an updated translation |
| | 119 | if ( bp_is_translation_update_pending() ) |
| | 120 | return; |
| | 121 | |
| | 122 | $locale = BP_Translate::get_glotpress_locale(); |
| | 123 | if ( ! $locale ) |
| | 124 | return; |
| | 125 | |
| | 126 | $url = 'https://translate.wordpress.org/projects/buddypress/%1$s/%2$s/default/export-translations?format=mo'; |
| | 127 | $args = bp_get_translation_version() ? array( 'headers' => 'If-Modified-Since: ' . gmdate( 'D, d M Y H:i:s', bp_get_translation_version() ) . ' GMT' ) : array(); |
| | 128 | |
| | 129 | // Check version of translation on translate.wordpress.org |
| | 130 | $response = wp_remote_head( sprintf( $url, buddypress()->glotpress_version, $locale ), $args ); |
| | 131 | if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) === 304 || wp_remote_retrieve_response_code( $response ) !== 200 ) |
| | 132 | return; |
| | 133 | |
| | 134 | // An updated translation is available |
| | 135 | bp_update_option( '_bp_translation_pending', true ); |
| | 136 | } |
| | 137 | |
| | 138 | /** |
| | 139 | * Get the current locale |
| | 140 | * |
| | 141 | * @return string |
| | 142 | * @since BuddyPress (1.8) |
| | 143 | */ |
| | 144 | static public function get_locale() { |
| | 145 | return apply_filters( 'buddypress_locale', get_locale() ); |
| | 146 | } |
| | 147 | |
| | 148 | /** |
| | 149 | * Get the GlotPress locale code for the current locale |
| | 150 | * |
| | 151 | * @return string|bool Returns bool if an error occured, otherwise the GlotPress locale as a string |
| | 152 | * @since BuddyPress (1.8) |
| | 153 | */ |
| | 154 | static public function get_glotpress_locale() { |
| | 155 | static $glotpress_locale; |
| | 156 | if ( ! empty( $glotpress_locale ) ) |
| | 157 | return $glotpress_locale; |
| | 158 | |
| | 159 | // Get the list of available translations from translate.wordpress.org |
| | 160 | $translations = wp_remote_get( sprintf( 'https://translate.wordpress.org/api/projects/buddypress/%1$s', buddypress()->glotpress_version ) ); |
| | 161 | if ( is_wp_error( $translations ) || wp_remote_retrieve_response_code( $translations ) !== 200 ) |
| | 162 | return false; |
| | 163 | |
| | 164 | $translations = json_decode( wp_remote_retrieve_body( $translations ) ); |
| | 165 | if ( is_null( $translations ) ) |
| | 166 | return false; |
| | 167 | |
| | 168 | // Does the requested $locale have an available translation? |
| | 169 | $translations = array_shift( wp_list_filter( $translations->translation_sets, array( 'wp_locale' => BP_Translate::get_locale() ) ) ); |
| | 170 | if ( empty( $translations ) ) |
| | 171 | return false; |
| | 172 | |
| | 173 | $glotpress_locale = $translations->locale; |
| | 174 | return $glotpress_locale; |
| | 175 | } |
| | 176 | |
| | 177 | /** |
| | 178 | * If in the WordPress dashboard, maybe bump the "available updates" count if there's a pending translation. |
| | 179 | * |
| | 180 | * @param array $data Counts and UI strings for available updates |
| | 181 | * @return array |
| | 182 | * @since BuddyPress (1.8) |
| | 183 | */ |
| | 184 | static public function maybe_bump_update_count( $data ) { |
| | 185 | if ( current_user_can( 'update_plugins' ) && bp_is_translation_update_pending() ) |
| | 186 | $data['counts']['total']++; |
| | 187 | |
| | 188 | return $data; |
| | 189 | } |
| | 190 | |
| | 191 | /** |
| | 192 | * If we have a pending translation, display a message on the wp-admin/update-core.php screen. |
| | 193 | * |
| | 194 | * @since BuddyPress (1.8) |
| | 195 | */ |
| | 196 | static public function updates_screen() { |
| | 197 | |
| | 198 | if ( BP_Translate::get_locale() === 'en_US' || ! bp_is_translation_update_pending() ) |
| | 199 | return; |
| | 200 | ?> |
| | 201 | <h3><?php _e( 'BuddyPress Translation', 'buddypress' ); ?></h3> |
| | 202 | <p><?php _e( 'An updated version of the current BuddyPress translation is available. Click “Update Translation”.', 'buddypress' ); ?></p> |
| | 203 | |
| | 204 | <form method="post" action="<?php echo esc_url( 'update-core.php?action=do-update-buddypress-translation' ); ?>" name="update-buddypress-translation" class="upgrade"> |
| | 205 | <?php wp_nonce_field( 'update-buddypress-translation' ); ?> |
| | 206 | |
| | 207 | <p><input class="button" type="submit" value="<?php esc_attr_e( 'Update Translation', 'buddypress' ); ?>" name="upgrade" /></p> |
| | 208 | </form> |
| | 209 | <?php |
| | 210 | } |
| | 211 | |
| | 212 | /** |
| | 213 | * We're going to update the BuddyPress translation; output an iframe in which the magic will happen. |
| | 214 | * |
| | 215 | * This copies the implementation for the Plugin and Theme updates wherein the work is done in a separate |
| | 216 | * request that is iframed in. This allows WordPress to recover from any errors during the process. |
| | 217 | * |
| | 218 | * @since BuddyPress (1.8) |
| | 219 | */ |
| | 220 | static public function updates_screen_iframe() { |
| | 221 | |
| | 222 | if ( ! current_user_can( 'update_plugins' ) ) |
| | 223 | wp_die( __( 'You do not have sufficient permissions to update this site.', 'buddypress' ) ); |
| | 224 | |
| | 225 | check_admin_referer( 'update-buddypress-translation' ); |
| | 226 | |
| | 227 | // If no pending translation updates, redirect away. |
| | 228 | if ( ! bp_is_translation_update_pending() ) { |
| | 229 | wp_redirect( admin_url('update-core.php') ); |
| | 230 | exit; |
| | 231 | } |
| | 232 | |
| | 233 | require_once( ABSPATH . 'wp-admin/admin-header.php' ); |
| | 234 | $title = __( 'Update BuddyPress Translation', 'buddypress' ); |
| | 235 | $url = wp_nonce_url( 'update.php?action=update-buddypress-translation', 'update-buddypress-translation' ); |
| | 236 | |
| | 237 | echo '<div class="wrap">'; |
| | 238 | screen_icon( 'plugins' ); |
| | 239 | echo '<h2>' . esc_html( $title ) . '</h2>'; |
| | 240 | echo '<iframe src=' . esc_url( $url ) . ' style="width: 100%; height: 100%; min-height: 750px;" frameborder="0"></iframe>'; |
| | 241 | echo '</div>'; |
| | 242 | |
| | 243 | include( ABSPATH . 'wp-admin/admin-footer.php' ); |
| | 244 | } |
| | 245 | |
| | 246 | /** |
| | 247 | * Download the latest version of the current locale's translation from translate.wordpress.org |
| | 248 | * |
| | 249 | * @since BuddyPress (1.8) |
| | 250 | */ |
| | 251 | static public function update_translation() { |
| | 252 | |
| | 253 | // @todo Not sure if this does anything |
| | 254 | if ( ! defined( 'IFRAME_REQUEST' ) ) |
| | 255 | define( 'IFRAME_REQUEST', true ); |
| | 256 | |
| | 257 | if ( ! current_user_can( 'update_plugins' ) || BP_Translate::get_locale() === 'en_US' ) |
| | 258 | wp_die( __( 'You do not have sufficient permissions to update this site.', 'buddypress' ) ); |
| | 259 | |
| | 260 | check_admin_referer( 'update-buddypress-translation' ); |
| | 261 | iframe_header(); |
| | 262 | |
| | 263 | echo '<p>' . __( 'The update process is starting. This process may take a while on some hosts, so please be patient.', 'buddypress' ) . '</p>'; |
| | 264 | echo '<h4>' . __( 'Updating BuddyPress Translation', 'buddypress' ) . '</h4>'; |
| | 265 | |
| | 266 | // Download the .mo to a local temporary file |
| | 267 | $url = 'https://translate.wordpress.org/projects/buddypress/%1$s/%2$s/default/export-translations?format=mo'; |
| | 268 | $tmp = download_url( sprintf( $url, buddypress()->glotpress_version, BP_Translate::get_glotpress_locale() ) ); |
| | 269 | |
| | 270 | if ( is_wp_error( $tmp ) ) { |
| | 271 | $css_class = 'error'; |
| | 272 | $message = __( 'Error: failure updating translation.', 'buddypress' ); |
| | 273 | $message .= '</p><p><strong>' . $tmp->get_error_message() . '</strong>'; |
| | 274 | |
| | 275 | } else { |
| | 276 | $css_class = 'updated'; |
| | 277 | $message = __( 'Translation updated succesfully!', 'buddypress' ); |
| | 278 | $upload_dir = wp_upload_dir(); |
| | 279 | $new_file = sprintf( '%s/buddypress/buddypress-%s.mo', $upload_dir['basedir'], BP_Translate::get_locale() ); |
| | 280 | |
| | 281 | // Check the target folder exists |
| | 282 | @mkdir( $upload_dir['basedir'] . '/buddypress' ); |
| | 283 | |
| | 284 | // Move the file into place |
| | 285 | @copy( $tmp, $new_file ); |
| | 286 | @unlink( $tmp ); |
| | 287 | |
| | 288 | // Store the current timestamp for future checks, for the IF-MODIFIED-SINCE header |
| | 289 | bp_update_option( '_bp_translation_version', time() ); |
| | 290 | |
| | 291 | // Clear the pending translation flag |
| | 292 | bp_delete_option( '_bp_translation_pending' ); |
| | 293 | } |
| | 294 | ?> |
| | 295 | |
| | 296 | <div class="<?php echo esc_attr( $css_class ); ?>"> |
| | 297 | <p><?php echo $message; ?></p> |
| | 298 | </div> |
| | 299 | |
| | 300 | <p><a href="<?php echo self_admin_url( 'update-core.php' ); ?>" target="_parent"><?php _e( 'Return to WordPress Updates', 'buddypress' ); ?></a></p> |
| | 301 | |
| | 302 | <?php |
| | 303 | iframe_footer(); |
| | 304 | } |
| | 305 | } |