From a7ac70087a878860c1344fb5af77f66e8858c849 Mon Sep 17 00:00:00 2001 From: pupi1985 Date: Fri, 16 Oct 2020 00:19:49 -0300 Subject: [PATCH] Cache core and plugin updates --- qa-content/qa-admin.js | 33 +++++----- qa-include/ajax/version.php | 15 +++-- qa-include/app/options.php | 27 ++++++++ qa-include/pages/admin/admin-default.php | 2 +- qa-src/Controllers/Admin/Plugins.php | 20 ++++-- qa-src/Controllers/Admin/Stats.php | 38 ++++++++++-- qa-src/Plugin/PluginManager.php | 38 +++++++++++- qa-src/Update/CoreUpdateManager.php | 78 ++++++++++++++++++++++++ 8 files changed, 215 insertions(+), 36 deletions(-) create mode 100644 qa-src/Update/CoreUpdateManager.php diff --git a/qa-content/qa-admin.js b/qa-content/qa-admin.js index 37d23e456..c335bb001 100644 --- a/qa-content/qa-admin.js +++ b/qa-content/qa-admin.js @@ -21,8 +21,7 @@ var qa_recalc_running = 0; -window.onbeforeunload = function(event) -{ +window.onbeforeunload = function (event) { if (qa_recalc_running > 0) { event = event || window.event; var message = qa_warning_recalc; @@ -43,8 +42,9 @@ function qa_recalc_click(state, elem, value, noteid) document.getElementById(noteid).innerHTML = ''; elem.qa_original_value = elem.value; - if (value) + if (value) { elem.value = value; + } qa_recalc_update(elem, state, noteid); } @@ -147,27 +147,24 @@ function qa_admin_click(target) return false; } -function qa_version_check(uri, version, elem, isCore) +function qa_version_check(uri, version, elem, componentId) { - qa_ajax_post( - 'version', - {uri: uri, version: version, isCore: isCore}, - function (response) { - if (response.result === 'error') { - alert(response.error.message); + var params = {uri: uri, version: version, componentId: componentId}; + qa_ajax_post('version', params, function (response) { + if (response.result === 'error') { + alert(response.error.message); - return; - } + return; + } - document.getElementById(elem).innerHTML = response.html; - }, 1 - ); + document.getElementById(elem).innerHTML = response.html; + }, 1); } function qa_version_check_array(versionChecks) { for (var i = 0; i < versionChecks.length; i++) { - qa_version_check(versionChecks[i].uri, versionChecks[i].version, versionChecks[i].elem, false) + qa_version_check(versionChecks[i].uri, versionChecks[i].version, versionChecks[i].elem, versionChecks[i].componentId); } } @@ -175,8 +172,8 @@ function qa_get_enabled_plugins_hashes() { var hashes = []; $('[id^=plugin_enabled]:checked').each( - function(idx, elem) { - hashes.push(elem.id.replace("plugin_enabled_", "")); + function (idx, elem) { + hashes.push(elem.id.replace('plugin_enabled_', '')); } ); diff --git a/qa-include/ajax/version.php b/qa-include/ajax/version.php index 805b3cbb8..f29bae817 100644 --- a/qa-include/ajax/version.php +++ b/qa-include/ajax/version.php @@ -36,13 +36,13 @@ $uri = qa_post_text('uri'); $currentVersion = qa_post_text('version'); -$isCore = qa_post_text('isCore') === "true"; +$componentId = qa_post_text('componentId'); -if ($isCore) { +if ($componentId === 'core') { $contents = qa_retrieve_url($uri); if (strlen($contents) > 0) { - if (qa_qa_version_below($contents)) { + if (version_compare($currentVersion, $contents) < 0) { $versionResponse = '' . qa_lang_html_sub('admin/version_get_x', qa_html('v' . $contents)) . @@ -50,12 +50,13 @@ } else { $versionResponse = qa_html($contents); // Output the current version number } + + (new \Q2A\Update\CoreUpdateManager())->setCachedVersion($contents); } else { $versionResponse = qa_lang_html('admin/version_latest_unknown'); } } else { - $metadataUtil = new \Q2A\Util\Metadata(); - $metadata = $metadataUtil->fetchFromUrl($uri); + $metadata = (new \Q2A\Util\Metadata())->fetchFromUrl($uri); if (strlen(@$metadata['version']) > 0) { if (version_compare($currentVersion, $metadata['version']) < 0) { @@ -72,6 +73,10 @@ } else { $versionResponse = qa_lang_html_sub('admin/version_get_x', qa_html('v' . $metadata['version'])); + if ($componentId !== null) { + (new \Q2A\Plugin\PluginManager())->addCachedUpdate($componentId, $metadata['version']); + } + if (strlen(@$metadata['uri'])) { $versionResponse = '' . $versionResponse . ''; } diff --git a/qa-include/app/options.php b/qa-include/app/options.php index ae1ffa4ee..e3c529356 100644 --- a/qa-include/app/options.php +++ b/qa-include/app/options.php @@ -148,6 +148,33 @@ function qa_load_options_results($results) } +/** + * Get option $name from the database + * @param string $name + * @return string + */ +function qa_db_get_option($name) +{ + global $qa_options_cache, $qa_options_loaded; + + $selectSpec = [ + 'columns' => array('content'), + 'source' => '^options WHERE title = #', + 'arrayvalue' => 'content', + 'single' => true, + 'arguments' => [$name], + ]; + + $value = qa_db_single_select($selectSpec); + + if (isset($qa_options_loaded)) { + $qa_options_cache[$name] = $value; + } + + return $value; +} + + /** * Set an option $name to $value (application level) in both cache and database, unless * $todatabase=false, in which case set it in the cache only diff --git a/qa-include/pages/admin/admin-default.php b/qa-include/pages/admin/admin-default.php index ffde789be..875480a78 100644 --- a/qa-include/pages/admin/admin-default.php +++ b/qa-include/pages/admin/admin-default.php @@ -1076,7 +1076,7 @@ function qa_optionfield_make_select(&$optionfield, $options, $value, $default) $updatehtml = '(...)'; $qa_content['script_onloads'][] = array( - "qa_version_check(" . qa_js($metadata['update_uri']) . ", " . qa_js($metadata['version'], true) . ", " . qa_js($elementid) . ", false);" + "qa_version_check(" . qa_js($metadata['update_uri']) . ", " . qa_js($metadata['version'], true) . ", " . qa_js($elementid) . ", null);" ); } diff --git a/qa-src/Controllers/Admin/Plugins.php b/qa-src/Controllers/Admin/Plugins.php index d8fc65fc5..202e44dc2 100644 --- a/qa-src/Controllers/Admin/Plugins.php +++ b/qa-src/Controllers/Admin/Plugins.php @@ -51,6 +51,7 @@ public function index() $pluginManager = new \Q2A\Plugin\PluginManager(); $pluginManager->cleanRemovedPlugins(); + $cachedUpdates = $pluginManager->getCachedUpdates(true); $enabledPlugins = $pluginManager->getEnabledPlugins(); $fileSystemPlugins = $pluginManager->getFilesystemPlugins(); @@ -178,18 +179,27 @@ public function index() $authorhtml = ''; } + $pluginId = md5($pluginDirectory); + $elementId = 'version_check_' . $pluginId; if ($shouldCheckForUpdate && $metaver && isset($metadata['update_uri']) && strlen($metadata['update_uri'])) { - $elementid = 'version_check_' . md5($pluginDirectory); - $versionChecks[] = array( 'uri' => $metadata['update_uri'], 'version' => $metadata['version'], - 'elem' => $elementid, + 'elem' => $elementId, + 'componentId' => $pluginId, ); - $updatehtml = '(...)'; + $updatehtml = sprintf('(...)', $elementId); } else { - $updatehtml = ''; + if (isset($cachedUpdates[$pluginId]) && isset($metadata['uri']) && $metadata['version'] !== $cachedUpdates[$pluginId]) { + $updatehtml = sprintf( + '(%s)', + qa_html($metadata['uri']), + qa_lang_html_sub('admin/version_get_x', qa_html('v' . $cachedUpdates[$pluginId])) + ); + } else { + $updatehtml = ''; + } } if (isset($metadata['description'])) diff --git a/qa-src/Controllers/Admin/Stats.php b/qa-src/Controllers/Admin/Stats.php index 3b9c42f7c..9e5af8b05 100644 --- a/qa-src/Controllers/Admin/Stats.php +++ b/qa-src/Controllers/Admin/Stats.php @@ -21,12 +21,16 @@ use Q2A\Controllers\BaseController; use Q2A\Database\DbConnection; use Q2A\Middleware\Auth\MinimumUserLevel; +use Q2A\Update\CoreUpdateManager; /** * Controller for admin page showing usage statistics and clean-up buttons */ class Stats extends BaseController { + /** @var CoreUpdateManager */ + private $coreUpdateManager; + public function __construct(DbConnection $db) { require_once QA_INCLUDE_DIR . 'db/recalc.php'; @@ -37,6 +41,8 @@ public function __construct(DbConnection $db) parent::__construct($db); $this->addMiddleware(new MinimumUserLevel(QA_USER_LEVEL_ADMIN)); + + $this->coreUpdateManager = new CoreUpdateManager(); } public function index() @@ -79,7 +85,7 @@ public function index() 'q2a_latest' => array( 'label' => qa_lang_html('admin/q2a_latest_version'), 'type' => 'custom', - 'html' => '...', + 'html' => $this->getUpdateVersionText(), ), 'break0' => array( @@ -282,17 +288,39 @@ public function index() } } - $qa_content['script_rel'][] = 'qa-content/qa-admin.js?' . QA_VERSION; $qa_content['script_var']['qa_warning_recalc'] = qa_lang('admin/stop_recalc_warning'); - $qa_content['script_onloads'][] = array( - "qa_version_check('https://raw.githubusercontent.com/q2a/question2answer/master/VERSION.txt', " . qa_js(qa_html(QA_VERSION), true) . ", 'q2a-version', true);" - ); + if ($this->coreUpdateManager->shouldCheckForUpdate()) { + $qa_content['script_onloads'][] = array( + sprintf("qa_version_check('https://raw.githubusercontent.com/q2a/question2answer/master/VERSION.txt', '%s', 'q2a-version', 'core');", QA_VERSION), + ); + } $qa_content['navigation']['sub'] = qa_admin_sub_navigation(); return $qa_content; } + + /** + * @return string + */ + private function getUpdateVersionText() + { + if ($this->coreUpdateManager->shouldCheckForUpdate()) { + return '...'; + } + + $version = $this->coreUpdateManager->getCachedVersion(); + + if (!isset($version) || $version === QA_VERSION) { + return QA_VERSION; + } + + return + '' . + qa_lang_html_sub('admin/version_get_x', qa_html('v' . $version)) . + ''; + } } diff --git a/qa-src/Plugin/PluginManager.php b/qa-src/Plugin/PluginManager.php index 95dcb92ba..b93f7d8fb 100644 --- a/qa-src/Plugin/PluginManager.php +++ b/qa-src/Plugin/PluginManager.php @@ -25,7 +25,8 @@ class PluginManager { const PLUGIN_DELIMITER = ';'; const OPT_ENABLED_PLUGINS = 'enabled_plugins'; - const OPT_LAST_UPDATE_CHECK = 'last_plugin_update_check'; + const OPT_LAST_UPDATE_CHECK = 'plugin_update_last_check'; + const OPT_PLUGIN_UPDATES_CACHE = 'plugin_update_cache'; const NUMBER_OF_DAYS_TO_CHECK_FOR_UPDATE = 14; private $loadBeforeDbInit = array(); @@ -200,9 +201,42 @@ public function shouldCheckForUpdate() public function performUpdateCheck($time = null) { if ($time === null) { - $time = time(); + $time = (int)qa_opt('db_time'); } qa_opt(self::OPT_LAST_UPDATE_CHECK, $time); + $this->setCachedUpdates([]); + } + + /** + * @param bool $fromDatabase + * @return array + */ + public function getCachedUpdates($fromDatabase = false) + { + $option = $fromDatabase + ? qa_db_get_option(self::OPT_PLUGIN_UPDATES_CACHE) + : qa_opt(self::OPT_PLUGIN_UPDATES_CACHE); + + return json_decode($option, true); + } + + /** + * @param array $pluginUpdates Key-value array relating MD5 plugin IDs and versions + */ + public function setCachedUpdates($pluginUpdates) + { + qa_opt(self::OPT_PLUGIN_UPDATES_CACHE, json_encode($pluginUpdates)); + } + + /** + * @param string $componentId + * @param string $version + */ + public function addCachedUpdate($componentId, $version) + { + $pluginUpdates = $this->getCachedUpdates(true); + $pluginUpdates[$componentId] = $version; + $this->setCachedUpdates($pluginUpdates); } } diff --git a/qa-src/Update/CoreUpdateManager.php b/qa-src/Update/CoreUpdateManager.php new file mode 100644 index 000000000..2a6c0ed4f --- /dev/null +++ b/qa-src/Update/CoreUpdateManager.php @@ -0,0 +1,78 @@ + self::NUMBER_OF_DAYS_TO_CHECK_FOR_UPDATE * 24 * 60 * 60; + } + + /** + * @param string $version + * @param int|null $time + */ + public function setCachedVersion($version, $time = null) + { + if ($time === null) { + $time = (int)qa_opt('db_time'); + } + + $option = [ + self::OPT_FIELD_TIME => $time, + self::OPT_FIELD_VERSION => $version, + ]; + + qa_opt(self::OPT_CORE_UPDATE_CACHE, json_encode($option)); + } + + /** + * @return array + */ + public function getCachedVersion() + { + $option = qa_opt(self::OPT_CORE_UPDATE_CACHE); + $option = json_decode($option, true); + + return isset($option[self::OPT_FIELD_VERSION]) + ? $option[self::OPT_FIELD_VERSION] + : null; + } +}