From 2c6bfc63098beecc45253885138b867a703a1a50 Mon Sep 17 00:00:00 2001 From: Yahnis Elsts Date: Tue, 19 Apr 2016 13:56:54 +0300 Subject: [PATCH 1/3] Initial support for translation updates / language packs. Needs polishing and testing. --- plugin-update-checker.php | 121 +++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 3 deletions(-) diff --git a/plugin-update-checker.php b/plugin-update-checker.php index 11ead791..a40fe908 100644 --- a/plugin-update-checker.php +++ b/plugin-update-checker.php @@ -100,9 +100,10 @@ protected function installHooks(){ //Override requests for plugin information add_filter('plugins_api', array($this, 'injectInfo'), 20, 3); - //Insert our update info into the update array maintained by WP + //Insert our update info into the update array maintained by WP. add_filter('site_transient_update_plugins', array($this,'injectUpdate')); //WP 3.0+ add_filter('transient_update_plugins', array($this,'injectUpdate')); //WP 2.8+ + add_filter('site_transient_update_plugins', array($this, 'injectTranslationUpdates')); add_filter('plugin_row_meta', array($this, 'addCheckForUpdatesLink'), 10, 2); add_action('admin_init', array($this, 'handleManualCheck')); @@ -111,6 +112,10 @@ protected function installHooks(){ //Clear the version number cache when something - anything - is upgraded or WP clears the update cache. add_filter('upgrader_post_install', array($this, 'clearCachedVersion')); add_action('delete_site_transient_update_plugins', array($this, 'clearCachedVersion')); + //Clear translation updates when WP clears the update cache. + //This needs to be done directly because the library doesn't actually remove obsolete plugin updates, + //it just hides them (see getUpdate()). We can't do that with translations - too much disk I/O. + add_action('delete_site_transient_update_plugins', array($this, 'clearCachedTranslationUpdates')); if ( did_action('plugins_loaded') ) { $this->initDebugBarPanel(); @@ -252,7 +257,35 @@ public function requestUpdate(){ if ( $pluginInfo == null ){ return null; } - return PluginUpdate_3_0::fromPluginInfo($pluginInfo); + $update = PluginUpdate_3_0::fromPluginInfo($pluginInfo); + + //Remove translation updates that don't apply to this site. + $languages = array_flip(array_values(get_available_languages())); + $installedTranslations = wp_get_installed_translations('plugins'); + if ( isset($installedTranslations[$this->slug]) ) { + $installedTranslations = $installedTranslations[$this->slug]; + } else { + $installedTranslations = array(); + } + + $applicableTranslations = array(); + foreach($update->translations as $translation) { + //Does it match one of the available core languages? + $isApplicable = array_key_exists($translation->language, $languages); + //Is it more recent than an already-installed translation? + if ( isset($installedTranslations[$translation->language]) ) { + $updateTimestamp = strtotime($translation->updated); + $installedTimestamp = strtotime($installedTranslations[$translation->language]['PO-Revision-Date']); + $isApplicable = $updateTimestamp > $installedTimestamp; + } + + if ( $isApplicable ) { + $applicableTranslations[] = $translation; + } + } + $update->translations = $applicableTranslations; + + return $update; } /** @@ -477,6 +510,58 @@ private function removeUpdateFromList($updates) { return $updates; } + /** + * Insert translation updates into the list maintained by WordPress. + * + * @param stdClass $updates + * @return stdClass + */ + public function injectTranslationUpdates($updates) { + $translationUpdates = $this->getTranslationUpdates(); + if ( empty($translationUpdates) ) { + return $updates; + } + + //Being defensive. + if ( !is_object($updates) ) { + $updates = new stdClass(); + } + if ( !isset($updates->translations) ) { + $updates->translations = array(); + } + + //In case there's a name collision with a plugin hosted on wordpress.org, + //remove any preexisting updates that match our plugin. + $translationType = 'plugin'; + $filteredTranslations = array(); + foreach($updates->translations as $translation) { + if ( ($translation['type'] === $translationType) && ($translation['slug'] === $this->slug) ) { + continue; + } + $filteredTranslations[] = $translation; + } + $updates->translations = $filteredTranslations; + + //Add our updates to the list. + foreach($translationUpdates as $update) { + $convertedUpdate = array_merge( + array( + 'type' => $translationType, + 'slug' => $this->slug, + 'autoupdate' => 0, + //AFAICT, WordPress doesn't actually use the "version" field for anything. + //But lets make sure it's there, just in case. + 'version' => isset($update->version) ? $update->version : ('1.' . strtotime($update->updated)), + ), + (array)$update + ); + + $updates->translations[] = $convertedUpdate; + } + + return $updates; + } + /** * Rename the update directory to match the existing plugin directory. * @@ -607,6 +692,33 @@ public function getUpdate() { return null; } + /** + * Get a list of available translation updates. + * + * This method will return an empty array if there are no updates. + * Uses cached update data. + * + * @return array + */ + public function getTranslationUpdates() { + $state = $this->getUpdateState(); + if ( isset($state, $state->update, $state->update->translations) ) { + return $state->update->translations; + } + return array(); + } + + /** + * Remove all cached translation updates. + */ + public function clearCachedTranslationUpdates() { + $state = $this->getUpdateState(); + if ( isset($state, $state->update, $state->update->translations) ) { + $state->update->translations = array(); + $this->setUpdateState($state); + } + } + /** * Add a "Check for updates" link to the plugin row in the "Plugins" page. By default, * the new link will appear after the "Visit plugin site" link. @@ -844,6 +956,7 @@ class PluginInfo_3_0 { public $homepage; public $sections = array(); public $banners; + public $translations = array(); public $download_url; public $author; @@ -984,11 +1097,13 @@ class PluginUpdate_3_0 { public $download_url; public $upgrade_notice; public $tested; + public $translations = array(); public $filename; //Plugin filename relative to the plugins directory. private static $fields = array( 'id', 'slug', 'version', 'homepage', 'tested', - 'download_url', 'upgrade_notice', 'filename' + 'download_url', 'upgrade_notice', 'filename', + 'translations' ); /** From 0cfe8deaad05aeb0ac8b55fd4245c74e795c03b0 Mon Sep 17 00:00:00 2001 From: Yahnis Elsts Date: Tue, 19 Apr 2016 15:42:38 +0300 Subject: [PATCH 2/3] Minor: Add a @see reference to the WP API that clears the update cache. --- plugin-update-checker.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin-update-checker.php b/plugin-update-checker.php index a40fe908..c1140c27 100644 --- a/plugin-update-checker.php +++ b/plugin-update-checker.php @@ -710,6 +710,8 @@ public function getTranslationUpdates() { /** * Remove all cached translation updates. + * + * @see wp_clean_update_cache */ public function clearCachedTranslationUpdates() { $state = $this->getUpdateState(); From c6970fd8831c79b98779cdf9032f657ae23a47c5 Mon Sep 17 00:00:00 2001 From: Yahnis Elsts Date: Tue, 3 May 2016 18:32:39 +0300 Subject: [PATCH 3/3] Extract translation update filter as a method. --- plugin-update-checker.php | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/plugin-update-checker.php b/plugin-update-checker.php index c1140c27..0c29d628 100644 --- a/plugin-update-checker.php +++ b/plugin-update-checker.php @@ -259,7 +259,20 @@ public function requestUpdate(){ } $update = PluginUpdate_3_0::fromPluginInfo($pluginInfo); - //Remove translation updates that don't apply to this site. + //Keep only those translation updates that apply to this site. + $update->translations = $this->filterApplicableTranslations($update->translations); + + return $update; + } + + /** + * Filter a list of translation updates and return a new list that contains only updates + * that apply to the current site. + * + * @param array $translations + * @return array + */ + private function filterApplicableTranslations($translations) { $languages = array_flip(array_values(get_available_languages())); $installedTranslations = wp_get_installed_translations('plugins'); if ( isset($installedTranslations[$this->slug]) ) { @@ -269,7 +282,7 @@ public function requestUpdate(){ } $applicableTranslations = array(); - foreach($update->translations as $translation) { + foreach($translations as $translation) { //Does it match one of the available core languages? $isApplicable = array_key_exists($translation->language, $languages); //Is it more recent than an already-installed translation? @@ -283,9 +296,8 @@ public function requestUpdate(){ $applicableTranslations[] = $translation; } } - $update->translations = $applicableTranslations; - return $update; + return $applicableTranslations; } /**