Incorporate custom update notifier for all apps

Former-commit-id: a81d4282f81cae221ec27576e4ee6b5971481ade [formerly 2d0dc3ef5a0e5d014c14438f767e7742cbcecb7c [formerly 81710ec0493e9f2727baa8bc2eeaa3511229a232]]
Former-commit-id: 751684742a550c7d8ca5e057390df773d27cab24 [formerly 2652298d2a974e213cbf899f1140b3dd1938aa89]
Former-commit-id: d622327206178622525a611667bec2acc7761e56
This commit is contained in:
Jaifroid 2022-06-04 19:22:14 +01:00
parent 99e9cdb360
commit 3a059ad0ff
5 changed files with 150 additions and 10 deletions

View File

@ -738,7 +738,7 @@
<label class="checkbox">
<input type="checkbox" name="allowInternetAccess" id="allowInternetAccessCheck">
<span class="checkmark"></span>
<b>Allow Internet access?</b>
<b>Allow Internet access?</b> <span id="updateStatus">(and check for updates)</span>
</label>
</div>
<div id="downloadLinks" style="display: none;"></div>

View File

@ -26,8 +26,8 @@
// This uses require.js to structure javascript:
// http://requirejs.org/docs/api.html#define
define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'cache', 'images', 'settingsStore', 'transformStyles', 'kiwixServe'],
function ($, zimArchiveLoader, uiUtil, util, utf8, cache, images, settingsStore, transformStyles, kiwixServe) {
define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'cache', 'images', 'settingsStore', 'transformStyles', 'kiwixServe', 'updater'],
function ($, zimArchiveLoader, uiUtil, util, utf8, cache, images, settingsStore, transformStyles, kiwixServe, updater) {
/**
* The delay (in milliseconds) between two "keepalive" messages
@ -866,6 +866,27 @@ define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'cache', 'images
});
}
// Check for GitHub updates
function checkUpdateServer() {
if (!params.allowInternetAccess) {
console.log("The update check was blocked because the user has not allowed Internet access.")
return;
}
updater.getLatestUpdates(function (tag, url, releases) {
var updateSpan = document.getElementById('updateStatus');
if (!tag) {
updateSpan.innerHTML = '[ <b><i>App is up to date</i></b> ]';
console.log('No new update was found.');
return;
}
console.log('We found this update: [' + tag + '] ' + url, releases);
updateSpan.innerHTML = '[ <b><i><a href="#alertBoxPersistent">New update!</a></i></b> ]';
uiUtil.showUpgradeReady(tag.replace(/^v/, ''), 'download', url);
});
}
// Do check on startup
setTimeout(checkUpdateServer, 15000);
function setActiveBtn(activeBtn) {
document.getElementById('btnHome').classList.remove("active");
document.getElementById('btnRandomArticle').classList.remove("active");
@ -1125,7 +1146,7 @@ define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'cache', 'images
setContentInjectionMode(this.value);
// If we're in a PWA UWP app, warn the user that this does not disable the PWA
if (this.value === 'jquery' && /^http/i.test(window.location.protocol) && /UWP\|PWA/.test(params.appType) &&
settingsStore.getItem('allowInternetAccess') === 'true') {
params.allowInternetAccess === 'true') {
uiUtil.systemAlert(
'<p>Please note that switching content injection mode does not revert to local code.</p>' +
'<p>If you wish to exit the PWA, you will need to turn off "Allow Internet access?" above.</p>'
@ -1188,8 +1209,11 @@ define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'cache', 'images
});
}
}
settingsStore.setItem('allowInternetAccess', false, Infinity);
} else {
// We can check for updates if the user has allowed Internet access
checkUpdateServer();
}
settingsStore.setItem('allowInternetAccess', params.allowInternetAccess, Infinity);
});
$('input:checkbox[name=cssCacheMode]').on('change', function (e) {
params.cssCache = this.checked ? true : false;

View File

@ -99,7 +99,7 @@ params['alphaChar'] = getSetting('alphaChar') || 'A'; //Set default start of alp
params['omegaChar'] = getSetting('omegaChar') || 'Z'; //Set default end of alphabet string
params['contentInjectionMode'] = getSetting('contentInjectionMode') || ((navigator.serviceWorker && !/^(ms-appx-web:)$/i.test(window.location.protocol)
&& !/Android/.test(params.appType) && !window.nw) ? 'serviceworker' : 'jquery'); // Deafault to SW mode if the browser supports it
params['allowInternetAccess'] = getSetting('allowInternetAccess');
params['allowInternetAccess'] = getSetting('allowInternetAccess') !== null ? getSetting('allowInternetAccess') : true;
params['openExternalLinksInNewTabs'] = getSetting('openExternalLinksInNewTabs') !== null ? getSetting('openExternalLinksInNewTabs') : true; // Parameter to turn on/off opening external links in new tab
params['windowOpener'] = getSetting('windowOpener'); // 'tab|window|false' A setting that determines whether right-click/long-press of a ZIM link opens a new window/tab
params['rightClickType'] = getSetting('rightClickType'); // 'single|double|false' A setting that determines whether a single or double right-click is used to open a new window/tab

View File

@ -674,17 +674,19 @@ define(rqDef, function(util) {
/**
* Shows that an upgrade is ready to install
* @param {String} ver The version of the upgrade
* @param {String} type Either 'load' or 'install' according to the type of upgrade
* @param {String} type Either 'load', 'install' or 'download' according to the type of upgrade
* @param {String} url An optional download URL
*/
function showUpgradeReady(ver, type) {
function showUpgradeReady(ver, type, url) {
params.upgradeNeeded = true;
document.getElementById('alertBoxPersistent').innerHTML =
'<div id="upgradeAlert" class="alert alert-info alert-dismissible">\n' +
' <a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>\n' +
' <span id="persistentMessage"></span>\n' +
'</div>\n';
document.getElementById('persistentMessage').innerHTML = 'Version ' + ver + ' is ready to '
+ type + '! (Re-launch app to ' + type + '.)';
document.getElementById('persistentMessage').innerHTML = 'Version ' + ver +
(url ? ' is available to ' + type + '! Go to <a href="' + url + '" style="color:white;" target="_blank">' + url + '</a>'
: ' is ready to ' + type + '! (Re-launch app to ' + type + '.)');
}
/**

114
www/js/lib/updater.js Normal file
View File

@ -0,0 +1,114 @@
/**
* updater.js : Functions for checking and initiating app updtes
*
* Copyright 2013-2022 Jaifroid, Mossroy and contributors
* License GPL v3:
*
* This file is part of Kiwix.
*
* Kiwix is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Kiwix is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kiwix (file LICENSE-GPLv3.txt). If not, see <http://www.gnu.org/licenses/>
*/
'use strict';
define(['uiUtil'], function (uiUtil) {
/**
* The update server configuration
*/
params.updateServer = {
url: 'https://api.github.com/repos/kiwix/kiwix-js-windows/',
releases: 'releases'
};
// A RegExp prototype string to match the current app's releases
const baseApp = (params.packagedFile && /wikivoyage/.test(params.packagedFile)) ? 'wikivoyage' :
(params.packagedFile && /wikmed|mdwiki/.test(params.packagedFile)) ? 'wikimed' :
'windows|electron|kiwixwebapp_'; // Default value
// A RegExp to match download URLs of releases
const regexpMatchGitHubReleases = RegExp('"browser_download_url[":\\s]+"(https:.*download\\/([^\\/]+).*(?:' + baseApp + ')[^"]+)"', 'ig');
/**
* Get and return the JSON list of releases from the update server's REST API
*
* @param {Function} callback The function to call with the data
* @returns {String} A JSON string containing hierarchical release data
*/
function getReleasesObject(callback) {
uiUtil.XHR(params.updateServer.url + params.updateServer.releases, 'text',
function (response, mimetype, status) {
if (status === 200) {
callback(response);
} else callback('');
}
);
}
/**
* A function to get the latest updates from a GitHub releases source
* Only updates that are greater than the current update are returned
* Attempts to match by channel, but also matches non-channel releases
*
* @param {Function} callback A function to call back with the results
* @returns {Object} Calls back with update tag, update URL, and array of releases
*/
function getLatestUpdates(callback) {
var updatedReleases = [];
var currentRelease = params.appVersion.replace(/^v?([\d.]+)/, '$1');
var currentReleaseChannel = params.appVersion.replace(/^[v\d.]+/, '');
var updateTag;
var channelMatchedTag;
var updateUrl;
var channelMatchedUpdateUrl;
getReleasesObject(function (releases) {
var releaseFile;
var releaseVersion;
var releaseChannel;
// Loop through every line in releases
var matchedRelease = regexpMatchGitHubReleases.exec(releases);
while (matchedRelease != null) {
releaseFile = matchedRelease[1];
releaseVersion = matchedRelease[2].replace(/^v?([\d.]+).*/, '$1');
releaseChannel = matchedRelease[2].replace(/^[v\d.]+/, '');
// Compare the releases using a version-type comparison
if (releaseVersion.localeCompare(currentRelease, { numeric: true, sensitivity: 'base' }) === 1) {
if (!channelMatchedTag && currentReleaseChannel === releaseChannel) {
channelMatchedTag = matchedRelease[2];
channelMatchedUpdateUrl = releaseFile.replace(/\/download\//, '/tag/').replace(/[^/]+$/, '');
}
if (!updateTag) updateTag = matchedRelease[2];
if (!updateUrl) updateUrl = releaseFile.replace(/\/download\//, '/tag/').replace(/[^/]+$/, '');
updatedReleases.push(releaseFile)
}
matchedRelease = regexpMatchGitHubReleases.exec(releases);
}
// We should now have a list of all candidate updates, and candidate channel update
// Compare the channel-matched update wiht the update, and if they are same underlying version number, choose channel match
if (updateTag && updateTag.replace(/^v?([\d.]+).*/, '$1') === channelMatchedTag.replace(/^v?([\d.]+).*/, '$1')) {
updateTag = channelMatchedTag;
updateUrl = channelMatchedUpdateUrl;
}
callback(updateTag, updateUrl, updatedReleases);
});
}
/**
* Functions and classes exposed by this module
*/
return {
getLatestUpdates: getLatestUpdates
};
});