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;
+ }
+}