diff --git a/src/octoprint/plugins/announcements/__init__.py b/src/octoprint/plugins/announcements/__init__.py index 74bc192a..c064d860 100644 --- a/src/octoprint/plugins/announcements/__init__.py +++ b/src/octoprint/plugins/announcements/__init__.py @@ -28,7 +28,8 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin, octoprint.plugin.SettingsPlugin, octoprint.plugin.BlueprintPlugin, octoprint.plugin.StartupPlugin, - octoprint.plugin.TemplatePlugin): + octoprint.plugin.TemplatePlugin, + octoprint.plugin.EventHandlerPlugin): def __init__(self): self._cached_channel_configs = None @@ -210,6 +211,14 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin, return NO_CONTENT + ##~~ EventHandlerPlugin + + def on_event(self, event, payload): + from octoprint.events import Events + if event != Events.CONNECTIVITY_CHANGED or not payload or not payload.get("new", False): + return + self._fetch_all_channels_async() + # Internal Tools def _mark_read_until(self, channel, until): @@ -266,6 +275,11 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin, safe_key = self._slugify(key) return self._get_channel_configs(force=force).get(safe_key) + def _fetch_all_channels_async(self, force=False): + thread = threading.Thread(target=self._fetch_all_channels, kwargs=dict(force=force)) + thread.daemon = True + thread.start() + def _fetch_all_channels(self, force=False): """Fetch all channel feeds from cache or network.""" @@ -298,7 +312,10 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin, if data is None: # cache not allowed or empty, fetch from network - data = self._get_channel_data_from_network(key, config) + if self._connectivity_checker.online: + data = self._get_channel_data_from_network(key, config) + else: + self._logger.info("Looks like we are offline, can't fetch announcements for channel {} from network".format(key)) return data diff --git a/src/octoprint/plugins/announcements/static/js/announcements.js b/src/octoprint/plugins/announcements/static/js/announcements.js index a5af767d..2798b6be 100644 --- a/src/octoprint/plugins/announcements/static/js/announcements.js +++ b/src/octoprint/plugins/announcements/static/js/announcements.js @@ -317,7 +317,13 @@ $(function() { self.announcementDialog = $("#plugin_announcements_dialog"); self.announcementDialogContent = $("#plugin_announcements_dialog_content"); self.announcementDialogTabs = $("#plugin_announcements_dialog_tabs"); + }; + + self.onEventConnectivityChanged = function(payload) { + if (!payload || !payload.new) return; + self.retrieveData(); } + } // view model class, parameters for constructor, container to bind to diff --git a/src/octoprint/plugins/pluginmanager/__init__.py b/src/octoprint/plugins/pluginmanager/__init__.py index 398e5baf..64199e9a 100644 --- a/src/octoprint/plugins/pluginmanager/__init__.py +++ b/src/octoprint/plugins/pluginmanager/__init__.py @@ -36,7 +36,8 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, octoprint.plugin.AssetPlugin, octoprint.plugin.SettingsPlugin, octoprint.plugin.StartupPlugin, - octoprint.plugin.BlueprintPlugin): + octoprint.plugin.BlueprintPlugin, + octoprint.plugin.EventHandlerPlugin): ARCHIVE_EXTENSIONS = (".zip", ".tar.gz", ".tgz", ".tar") @@ -100,13 +101,7 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, self._console_logger.propagate = False # decouple repository fetching from server startup - def fetch_data(): - self._repository_available = self._fetch_repository_from_disk() - self._notices_available = self._fetch_notices_from_disk() - - thread = threading.Thread(target=fetch_data) - thread.daemon = True - thread.start() + self._fetch_all_data(async=True) ##~~ SettingsPlugin @@ -197,6 +192,14 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, except Exception as e: self._logger.warn("Could not remove temporary file {path} again: {message}".format(path=archive.name, message=str(e))) + ##~~ EventHandlerPlugin + + def on_event(self, event, payload): + from octoprint.events import Events + if event != Events.CONNECTIVITY_CHANGED or not payload or not payload.get("new", False): + return + self._fetch_all_data(async=True) + ##~~ SimpleApiPlugin def get_api_commands(self): @@ -239,7 +242,8 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, additional_args=self._settings.get(["pip_args"]), python=sys.executable ), - safe_mode=safe_mode) + safe_mode=safe_mode, + online=self._connectivity_checker.online) def etag(): import hashlib @@ -250,6 +254,7 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, hash.update(str(self._notices_available)) hash.update(repr(self._notices)) hash.update(repr(safe_mode)) + hash.update(repr(self._connectivity_checker.online)) return hash.hexdigest() def condition(): @@ -679,6 +684,18 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, elif (plugin.enabled or getattr(plugin, "safe_mode_enabled", False)) and plugin.key not in self._pending_disable: self._pending_disable.add(plugin.key) + def _fetch_all_data(self, async=False): + def run(): + self._repository_available = self._fetch_repository_from_disk() + self._notices_available = self._fetch_notices_from_disk() + + if async: + thread = threading.Thread(target=run) + thread.daemon = True + thread.start() + else: + run() + def _fetch_repository_from_disk(self): repo_data = None if os.path.isfile(self._repository_cache_path): @@ -696,6 +713,10 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, return self._refresh_repository(repo_data=repo_data) def _fetch_repository_from_url(self): + if not self._connectivity_checker.online: + self._logger.info("Looks like we are offline, can't fetch repository from network") + return None + repository_url = self._settings.get(["repository"]) try: r = requests.get(repository_url, timeout=30) @@ -758,13 +779,17 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, import json with open(self._notices_cache_path) as f: notice_data = json.load(f) - self._logger.info("Loaded notices from disk, was still valid") + self._logger.info("Loaded notice data from disk, was still valid") except: self._logger.exception("Error while loading notices from {}".format(self._notices_cache_path)) return self._refresh_notices(notice_data=notice_data) def _fetch_notices_from_url(self): + if not self._connectivity_checker.online: + self._logger.info("Looks like we are offline, can't fetch notices from network") + return None + notices_url = self._settings.get(["notices"]) try: r = requests.get(notices_url, timeout=30) diff --git a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js index 5fb04618..ba191d8e 100644 --- a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js +++ b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js @@ -174,6 +174,7 @@ $(function() { self.pipPython = ko.observable(); self.safeMode = ko.observable(); + self.online = ko.observable(); self.pipUseUserString = ko.pureComputed(function() { return self.pipUseUser() ? "yes" : "no"; @@ -234,7 +235,7 @@ $(function() { }; self.enableRepoInstall = function(data) { - return self.enableManagement() && self.pipAvailable() && !self.safeMode() && self.isCompatible(data); + return self.enableManagement() && self.pipAvailable() && !self.safeMode() && self.online() && self.isCompatible(data); }; self.invalidUrl = ko.pureComputed(function() { @@ -244,7 +245,7 @@ $(function() { self.enableUrlInstall = ko.pureComputed(function() { var url = self.installUrl(); - return self.enableManagement() && self.pipAvailable() && !self.safeMode() && url !== undefined && url.trim() != "" && !self.invalidUrl(); + return self.enableManagement() && self.pipAvailable() && !self.safeMode() && self.online() && url !== undefined && url.trim() != "" && !self.invalidUrl(); }); self.invalidArchive = ko.pureComputed(function() { @@ -321,6 +322,7 @@ $(function() { self._fromPipResponse(data.pip, options); self.safeMode(data.safe_mode || false); + self.online(data.online !== undefined ? data.online : true); }; self._fromPluginsResponse = function(data, options) { @@ -1020,6 +1022,11 @@ $(function() { self._closeAllNotifications(); }; + self.onEventConnectivityChanged = function(payload) { + if (!payload || !payload.new) return; + self.requestData({eval_notices: true}); + }; + self._closeAllNotifications = function() { if (self.notifications) { _.each(self.notifications, function(notification) { diff --git a/src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2 b/src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2 index 4931e698..10f921db 100644 --- a/src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2 +++ b/src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2 @@ -20,6 +20,13 @@ {% endtrans %} {% endmacro %} +{% macro pluginmanager_offline() %} +