From b1e4a1d9dc05e1f9017b65c9bb79f55fcd73396c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?=
- OctoPrint by default now ships with Access Control enabled, meaning you won't be able to do anything with the + OctoPrint by default ships with Access Control enabled, meaning you won't be able to do anything with the printer unless you login first as a configured user. This is to prevent strangers - possibly with malicious intent - to gain access to your printer via the internet or another untrustworthy network and using it in such a way that it is damaged or worse (i.e. causes a fire).
- It looks like you haven't configured access control yet. Please set up an username and password for the + It looks like you haven't configured access control yet. Please set up a username and password for the initial administrator account who will have full access to both the printer and OctoPrint's settings, then click on "Keep Access Control Enabled":
{% endtrans %} diff --git a/src/octoprint/plugins/corewizard/templates/corewizard_webcam_wizard.jinja2 b/src/octoprint/plugins/corewizard/templates/corewizard_webcam_wizard.jinja2 new file mode 100644 index 00000000..052961df --- /dev/null +++ b/src/octoprint/plugins/corewizard/templates/corewizard_webcam_wizard.jinja2 @@ -0,0 +1,41 @@ ++ If you have a webcam, you may now configure it here. +
{% endtrans %} + ++ The Stream URL is the URL OctoPrint uses to embed the + actual webcam stream (which should be an MJPG stream) into the web + interface. This needs to be reachable from your browser. +
+ It may be +
http://host:port/path),//host:port/path),/absolute/path) orrelative/path)+ The Snapshot URL is the URL OctoPrint uses to fetch single + images from the webcam for creating timelapse recordings. This needs to be + reachable from the OctoPrint server. +
{% endtrans %} + + + ++ To render the snapshots into timelapse recordings, OctoPrint also needs to + know the correct path to FFMPEG. +
{% endtrans %} + + diff --git a/src/octoprint/server/api/__init__.py b/src/octoprint/server/api/__init__.py index 170dbbac..53cbdf83 100644 --- a/src/octoprint/server/api/__init__.py +++ b/src/octoprint/server/api/__init__.py @@ -100,7 +100,7 @@ def pluginCommand(name): @api.route("/setup/wizard", methods=["GET"]) def wizardState(): - if not s().getBoolean(["server", "firstRun"]) and not admin_permission.can(): + if not admin_permission.can(): abort(403) result = dict() @@ -117,29 +117,28 @@ def wizardState(): return jsonify(result) -@api.route("/setup", methods=["POST"]) -def firstRunSetup(): - if not s().getBoolean(["server", "firstRun"]): + +@api.route("/setup/wizard", methods=["POST"]) +def wizardFinish(): + if not admin_permission.can(): abort(403) - if "ac" in request.values.keys() and request.values["ac"] in valid_boolean_trues and \ - "user" in request.values.keys() and "pass1" in request.values.keys() and \ - "pass2" in request.values.keys() and request.values["pass1"] == request.values["pass2"]: - # configure access control - s().setBoolean(["accessControl", "enabled"], True) - octoprint.server.userManager.addUser(request.values["user"], request.values["pass1"], True, ["user", "admin"], overwrite=True) - s().setBoolean(["server", "firstRun"], False) - elif "ac" in request.values.keys() and not request.values["ac"] in valid_boolean_trues: - # disable access control - s().setBoolean(["accessControl", "enabled"], False) + if s().getBoolean(["server", "firstRun"]): s().setBoolean(["server", "firstRun"], False) - octoprint.server.loginManager.anonymous_user = octoprint.users.DummyUser - octoprint.server.principals.identity_loaders.appendleft(octoprint.users.dummy_identity_loader) + wizard_plugins = octoprint.server.pluginManager.get_implementations(octoprint.plugin.WizardPlugin) + for implementation in wizard_plugins: + name = implementation._identifier + try: + implementation.on_wizard_finish() + except: + logging.getLogger(__name__).exceptino("There was an error finishing the wizard for {}, ignoring".format(name)) s().save() + return NO_CONTENT + #~~ system state diff --git a/src/octoprint/server/util/flask.py b/src/octoprint/server/util/flask.py index 91d41ebd..0337ffdd 100644 --- a/src/octoprint/server/util/flask.py +++ b/src/octoprint/server/util/flask.py @@ -613,7 +613,6 @@ def collect_plugin_assets(enable_gcodeviewer=True, enable_timelapse=True, prefer 'js/app/viewmodels/appearance.js', 'js/app/viewmodels/connection.js', 'js/app/viewmodels/control.js', - 'js/app/viewmodels/firstrun_wizard.js', 'js/app/viewmodels/files.js', 'js/app/viewmodels/loginstate.js', 'js/app/viewmodels/navigation.js', diff --git a/src/octoprint/server/views.py b/src/octoprint/server/views.py index 028aba9a..b22e828e 100644 --- a/src/octoprint/server/views.py +++ b/src/octoprint/server/views.py @@ -182,7 +182,6 @@ def index(): templates["wizard"]["entries"] = dict( firstrunstart=(gettext("Start"), dict(template="dialogs/wizard/firstrun_start.jinja2", _div="wizard_firstrun_start")), firstrunend=(gettext("Finish"), dict(template="dialogs/wizard/firstrun_end.jinja2", _div="wizard_firstrun_end")), - access=(gettext("Access Control"), dict(template="dialogs/wizard/firstrun_acl.jinja2", _div="wizard_firstrun_acl", custom_bindings=True)) ) # extract data from template plugins diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index cb518e0b..58b1e9e7 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -446,12 +446,14 @@ $(function() { if (object == undefined || !object.length) { log.info("Did not bind view model", viewModel.constructor.name, "to target", target, "since it does not exist"); + viewModel.unbound = true; return; } var element = object.get(0); if (element == undefined) { log.info("Did not bind view model", viewModel.constructor.name, "to target", target, "since it does not exist"); + viewModel.unbound = true; return; } @@ -460,6 +462,7 @@ $(function() { log.debug("View model", viewModel.constructor.name, "bound to", target); } catch (exc) { log.error("Could not bind view model", viewModel.constructor.name, "to target", target, ":", (exc.stack || exc)); + viewModel.unbound = true; } }); } diff --git a/src/octoprint/static/js/app/viewmodels/wizard.js b/src/octoprint/static/js/app/viewmodels/wizard.js index c73d504b..00879c30 100644 --- a/src/octoprint/static/js/app/viewmodels/wizard.js +++ b/src/octoprint/static/js/app/viewmodels/wizard.js @@ -9,6 +9,8 @@ $(function() { self.allViewModels = undefined; + self.finishing = false; + self.isDialogActive = function() { return self.wizardDialog.is(":visible"); }; @@ -18,17 +20,19 @@ $(function() { self.getWizardDetails(function(response) { _.each(self.allViewModels, function(viewModel) { - if (viewModel.hasOwnProperty("onWizardDetails")) { + if (!viewModel.unbound && viewModel.hasOwnProperty("onWizardDetails")) { viewModel.onWizardDetails(response); } }); - self.wizardDialog.modal({ - minHeight: function() { return Math.max($.fn.modal.defaults.maxHeight() - 80, 250); } - }).css({ - width: 'auto', - 'margin-left': function() { return -($(this).width() /2); } - }); + if (!self.isDialogActive()) { + self.wizardDialog.modal({ + minHeight: function() { return Math.max($.fn.modal.defaults.maxHeight() - 80, 250); } + }).css({ + width: 'auto', + 'margin-left': function() { return -($(this).width() /2); } + }); + } }); }; @@ -73,7 +77,7 @@ $(function() { var active = tab[0].id; if (active != undefined) { _.each(allViewModels, function(viewModel) { - if (viewModel.hasOwnProperty("onAfterWizardTabChange")) { + if (!viewModel.unbound && viewModel.hasOwnProperty("onAfterWizardTabChange")) { viewModel.onAfterWizardTabChange(active); } }); @@ -96,7 +100,7 @@ $(function() { if (current != undefined && next != undefined) { var result = true; _.each(allViewModels, function(viewModel) { - if (viewModel.hasOwnProperty("onWizardTabChange")) { + if (!viewModel.unbound && viewModel.hasOwnProperty("onWizardTabChange")) { result = result && (viewModel.onWizardTabChange(current, next) !== false); } }); @@ -106,19 +110,20 @@ $(function() { onFinish: function(tab, navigation, index) { var closeDialog = true; _.each(allViewModels, function(viewModel) { - if (viewModel.hasOwnProperty("onBeforeWizardFinish")) { + if (!viewModel.unbound && viewModel.hasOwnProperty("onBeforeWizardFinish")) { closeDialog = closeDialog && (viewModel.onBeforeWizardFinish() !== false); } }); if (closeDialog) { _.each(allViewModels, function(viewModel) { - if (viewModel.hasOwnProperty("onWizardFinish")) { + if (!viewModel.unbound && viewModel.hasOwnProperty("onWizardFinish")) { viewModel.onWizardFinish(); } }); - self.settingsViewModel.saveEnqueued(); - self.closeDialog(); + self.finishWizard(function() { + self.closeDialog(); + }); } } }); @@ -136,8 +141,28 @@ $(function() { }); }; + self.finishWizard = function(callback) { + self.finishing = true; + + self.settingsViewModel.saveEnqueued(); + $.ajax({ + url: API_BASEURL + "setup/wizard", + type: "POST", + dataType: "json", + contentType: "application/json; charset=UTF-8", + success: function() { + self.finishing = false; + callback(); + }, + failure: function() { + self.finishing = false; + } + }) + }; + self.onSettingsPreventRefresh = function() { - if (self.isDialogActive() && hasDataChanged(self.settingsViewModel.getLocalData(), self.settingsViewModel.lastReceivedSettings)) { + if (!self.finishing && self.isDialogActive() + && hasDataChanged(self.settingsViewModel.getLocalData(), self.settingsViewModel.lastReceivedSettings)) { // we have local changes, show update dialog self.settingsViewModel.settingsUpdatedDialog.modal("show"); return true; diff --git a/src/octoprint/templates/dialogs/firstrun.jinja2 b/src/octoprint/templates/dialogs/firstrun.jinja2 deleted file mode 100644 index 9c3360d4..00000000 --- a/src/octoprint/templates/dialogs/firstrun.jinja2 +++ /dev/null @@ -1,54 +0,0 @@ - diff --git a/src/octoprint/templates/index.jinja2 b/src/octoprint/templates/index.jinja2 index 3538f39e..415734d9 100644 --- a/src/octoprint/templates/index.jinja2 +++ b/src/octoprint/templates/index.jinja2 @@ -124,7 +124,6 @@ - {% include 'dialogs/firstrun.jinja2' %} {% include 'dialogs/settings.jinja2' %} {% include 'dialogs/slicing.jinja2' %} {% include 'dialogs/usersettings.jinja2' %}