From 19dc238f0608077ab458dadce3cef98d3d93d058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Sat, 13 Apr 2013 21:40:28 +0200 Subject: [PATCH] User management now working Also reorganized javascripts a bit (as preparation for some refactoring coming up) and renamed templates from ".html" to ".jinja2". --- octoprint/server.py | 83 ++++-- .../static/js/{ => bootstrap}/bootstrap.js | 0 .../js/{ => bootstrap}/bootstrap.min.js | 0 .../js/{ => jquery}/jquery.fileupload.js | 2 +- .../static/js/{ => jquery}/jquery.flot.js | 0 .../{ => jquery}/jquery.iframe-transport.js | 0 .../js/{ => jquery}/jquery.pnotify.min.js | 0 .../static/js/{ => jquery}/jquery.ui.core.js | 0 .../static/js/{ => jquery}/jquery.ui.mouse.js | 0 .../js/{ => jquery}/jquery.ui.slider.js | 0 .../js/{ => jquery}/jquery.ui.widget.js | 0 .../js/{knockout-2.2.1.js => knockout.js} | 0 .../js/{ => socket.io}/WebSocketMain.swf | Bin .../{ => socket.io}/WebSocketMainInsecure.swf | Bin .../static/js/{ => socket.io}/socket.io.js | 0 .../js/{ => socket.io}/socket.io.min.js | 0 octoprint/static/js/ui.js | 164 +++++------- .../js/{underscore-min.js => underscore.js} | 0 .../{dialogs.html => dialogs.jinja2} | 0 .../templates/{index.html => index.jinja2} | 242 ++++++++---------- .../{settings.html => settings.jinja2} | 6 +- octoprint/users.py | 5 +- 22 files changed, 241 insertions(+), 261 deletions(-) rename octoprint/static/js/{ => bootstrap}/bootstrap.js (100%) rename octoprint/static/js/{ => bootstrap}/bootstrap.min.js (100%) rename octoprint/static/js/{ => jquery}/jquery.fileupload.js (99%) rename octoprint/static/js/{ => jquery}/jquery.flot.js (100%) rename octoprint/static/js/{ => jquery}/jquery.iframe-transport.js (100%) rename octoprint/static/js/{ => jquery}/jquery.pnotify.min.js (100%) rename octoprint/static/js/{ => jquery}/jquery.ui.core.js (100%) rename octoprint/static/js/{ => jquery}/jquery.ui.mouse.js (100%) rename octoprint/static/js/{ => jquery}/jquery.ui.slider.js (100%) rename octoprint/static/js/{ => jquery}/jquery.ui.widget.js (100%) rename octoprint/static/js/{knockout-2.2.1.js => knockout.js} (100%) rename octoprint/static/js/{ => socket.io}/WebSocketMain.swf (100%) rename octoprint/static/js/{ => socket.io}/WebSocketMainInsecure.swf (100%) rename octoprint/static/js/{ => socket.io}/socket.io.js (100%) rename octoprint/static/js/{ => socket.io}/socket.io.min.js (100%) rename octoprint/static/js/{underscore-min.js => underscore.js} (100%) rename octoprint/templates/{dialogs.html => dialogs.jinja2} (100%) rename octoprint/templates/{index.html => index.jinja2} (77%) rename octoprint/templates/{settings.html => settings.jinja2} (99%) diff --git a/octoprint/server.py b/octoprint/server.py index 289f8e29..ae6c2788 100644 --- a/octoprint/server.py +++ b/octoprint/server.py @@ -4,7 +4,7 @@ __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agp from werkzeug.utils import secure_filename import tornadio2 -from flask import Flask, request, render_template, jsonify, send_from_directory, url_for, current_app, session +from flask import Flask, request, render_template, jsonify, send_from_directory, url_for, current_app, session, abort from flask.ext.login import LoginManager, login_user, logout_user, login_required, current_user from flask.ext.principal import Principal, Permission, RoleNeed, Identity, identity_changed, AnonymousIdentity, identity_loaded, UserNeed @@ -112,7 +112,8 @@ class PrinterStateConnection(tornadio2.SocketConnection): @app.route("/") def index(): return render_template( - "index.html", + "index.jinja2", + ajaxBaseUrl=BASEURL, webcamStream=settings().get(["webcam", "stream"]), enableTimelapse=(settings().get(["webcam", "snapshot"]) is not None and settings().get(["webcam", "ffmpeg"]) is not None), enableGCodeVisualizer=settings().get(["feature", "gCodeVisualizer"]), @@ -397,7 +398,7 @@ def getSettings(): @app.route(BASEURL + "settings", methods=["POST"]) @login_required -@admin_permission.require() +@admin_permission.require(403) def setSettings(): if "application/json" in request.headers["Content-Type"]: data = request.json @@ -444,14 +445,20 @@ def setSettings(): @app.route(BASEURL + "users", methods=["GET"]) @login_required -@admin_permission.require() +@admin_permission.require(403) def getUsers(): + if userManager is None: + return jsonify(SUCCESS) + return jsonify({"users": userManager.getAllUsers()}) @app.route(BASEURL + "users", methods=["POST"]) @login_required -@admin_permission.require() +@admin_permission.require(403) def addUser(): + if userManager is None: + return jsonify(SUCCESS) + if "application/json" in request.headers["Content-Type"]: data = request.json @@ -466,23 +473,31 @@ def addUser(): try: userManager.addUser(name, password, active, roles) except users.UserAlreadyExists: - return app.make_response(("User already exists: " % name, 409, [])) + abort(409) return getUsers() @app.route(BASEURL + "users/", methods=["GET"]) @login_required -@admin_permission.require() def getUser(username): - user = userManager.findUser(username) - if user is not None: - return jsonify(user.asDict()) + if userManager is None: + return jsonify(SUCCESS) + + if current_user is not None and not current_user.is_anonymous() and (current_user.get_name() == username or current_user.is_admin()): + user = userManager.findUser(username) + if user is not None: + return jsonify(user.asDict()) + else: + abort(404) else: - return app.make_response(("Unknown user: " % username, 404, [])) + abort(403) @app.route(BASEURL + "users/", methods=["PUT"]) @login_required -@admin_permission.require() +@admin_permission.require(403) def updateUser(username): + if userManager is None: + return jsonify(SUCCESS) + user = userManager.findUser(username) if user is not None: if "application/json" in request.headers["Content-Type"]: @@ -499,36 +514,44 @@ def updateUser(username): userManager.changeUserActivation(username, data["active"]) return getUsers() else: - return app.make_response(("Unknown user: " % username, 404, [])) + abort(404) @app.route(BASEURL + "users/", methods=["DELETE"]) @login_required -@admin_permission.require() +@admin_permission.require(http_exception=403) def removeUser(username): + if userManager is None: + return jsonify(SUCCESS) + try: userManager.removeUser(username) return getUsers() except users.UnknownUser: - return app.make_response(("Unknown user: " % username, 404, [])) + abort(404) @app.route(BASEURL + "users//password", methods=["PUT"]) @login_required -@admin_permission.require() def changePasswordForUser(username): - if "application/json" in request.headers["Content-Type"]: - data = request.json - if "password" in data.keys() and data["password"]: - try: - userManager.changeUserPassword(username, data["password"]) - except users.UnknownUser: - return app.make_response(("Unknown user: " % username, 404, [])) - return jsonify(SUCCESS) + if userManager is None: + return jsonify(SUCCESS) + + if current_user is not None and not current_user.is_anonymous() and (current_user.get_name() == username or current_user.is_admin()): + if "application/json" in request.headers["Content-Type"]: + data = request.json + if "password" in data.keys() and data["password"]: + try: + userManager.changeUserPassword(username, data["password"]) + except users.UnknownUser: + return app.make_response(("Unknown user: %s" % username, 404, [])) + return jsonify(SUCCESS) + else: + return app.make_response(("Forbidden", 403, [])) #~~ system control @app.route(BASEURL + "system", methods=["POST"]) @login_required -@admin_permission.require() +@admin_permission.require(403) def performSystemAction(): logger = logging.getLogger(__name__) if request.values.has_key("action"): @@ -565,12 +588,12 @@ def login(): if user.check_password(users.UserManager.createPasswordHash(password)): login_user(user, remember=remember) identity_changed.send(current_app._get_current_object(), identity=Identity(user.get_id())) - return jsonify({"name": user.get_name(), "user": user.is_user(), "admin": user.is_admin()}) + return jsonify(user.asDict()) return app.make_response(("User unknown or password incorrect", 401, [])) elif "passive" in request.values.keys(): user = current_user if user is not None and not user.is_anonymous(): - return jsonify({"name": user.get_name(), "user": user.is_user(), "admin": user.is_admin()}) + return jsonify(user.asDict()) else: return jsonify(SUCCESS) @@ -590,7 +613,11 @@ def logout(): def on_identity_loaded(sender, identity): user = load_user(identity.name) if user is None: - return + if userManager is None: + # access control is disabled, we'll create permissions for the DummyUser + user = users.DummyUser() + else: + return identity.provides.add(UserNeed(user.get_name())) if user.is_user(): diff --git a/octoprint/static/js/bootstrap.js b/octoprint/static/js/bootstrap/bootstrap.js similarity index 100% rename from octoprint/static/js/bootstrap.js rename to octoprint/static/js/bootstrap/bootstrap.js diff --git a/octoprint/static/js/bootstrap.min.js b/octoprint/static/js/bootstrap/bootstrap.min.js similarity index 100% rename from octoprint/static/js/bootstrap.min.js rename to octoprint/static/js/bootstrap/bootstrap.min.js diff --git a/octoprint/static/js/jquery.fileupload.js b/octoprint/static/js/jquery/jquery.fileupload.js similarity index 99% rename from octoprint/static/js/jquery.fileupload.js rename to octoprint/static/js/jquery/jquery.fileupload.js index bc96a07e..10f20d3e 100644 --- a/octoprint/static/js/jquery.fileupload.js +++ b/octoprint/static/js/jquery/jquery.fileupload.js @@ -18,7 +18,7 @@ // Register as an anonymous AMD module: define([ 'jquery', - 'jquery.ui.widget' + 'jquery.ui.widget.js' ], factory); } else { // Browser globals: diff --git a/octoprint/static/js/jquery.flot.js b/octoprint/static/js/jquery/jquery.flot.js similarity index 100% rename from octoprint/static/js/jquery.flot.js rename to octoprint/static/js/jquery/jquery.flot.js diff --git a/octoprint/static/js/jquery.iframe-transport.js b/octoprint/static/js/jquery/jquery.iframe-transport.js similarity index 100% rename from octoprint/static/js/jquery.iframe-transport.js rename to octoprint/static/js/jquery/jquery.iframe-transport.js diff --git a/octoprint/static/js/jquery.pnotify.min.js b/octoprint/static/js/jquery/jquery.pnotify.min.js similarity index 100% rename from octoprint/static/js/jquery.pnotify.min.js rename to octoprint/static/js/jquery/jquery.pnotify.min.js diff --git a/octoprint/static/js/jquery.ui.core.js b/octoprint/static/js/jquery/jquery.ui.core.js similarity index 100% rename from octoprint/static/js/jquery.ui.core.js rename to octoprint/static/js/jquery/jquery.ui.core.js diff --git a/octoprint/static/js/jquery.ui.mouse.js b/octoprint/static/js/jquery/jquery.ui.mouse.js similarity index 100% rename from octoprint/static/js/jquery.ui.mouse.js rename to octoprint/static/js/jquery/jquery.ui.mouse.js diff --git a/octoprint/static/js/jquery.ui.slider.js b/octoprint/static/js/jquery/jquery.ui.slider.js similarity index 100% rename from octoprint/static/js/jquery.ui.slider.js rename to octoprint/static/js/jquery/jquery.ui.slider.js diff --git a/octoprint/static/js/jquery.ui.widget.js b/octoprint/static/js/jquery/jquery.ui.widget.js similarity index 100% rename from octoprint/static/js/jquery.ui.widget.js rename to octoprint/static/js/jquery/jquery.ui.widget.js diff --git a/octoprint/static/js/knockout-2.2.1.js b/octoprint/static/js/knockout.js similarity index 100% rename from octoprint/static/js/knockout-2.2.1.js rename to octoprint/static/js/knockout.js diff --git a/octoprint/static/js/WebSocketMain.swf b/octoprint/static/js/socket.io/WebSocketMain.swf similarity index 100% rename from octoprint/static/js/WebSocketMain.swf rename to octoprint/static/js/socket.io/WebSocketMain.swf diff --git a/octoprint/static/js/WebSocketMainInsecure.swf b/octoprint/static/js/socket.io/WebSocketMainInsecure.swf similarity index 100% rename from octoprint/static/js/WebSocketMainInsecure.swf rename to octoprint/static/js/socket.io/WebSocketMainInsecure.swf diff --git a/octoprint/static/js/socket.io.js b/octoprint/static/js/socket.io/socket.io.js similarity index 100% rename from octoprint/static/js/socket.io.js rename to octoprint/static/js/socket.io/socket.io.js diff --git a/octoprint/static/js/socket.io.min.js b/octoprint/static/js/socket.io/socket.io.min.js similarity index 100% rename from octoprint/static/js/socket.io.min.js rename to octoprint/static/js/socket.io/socket.io.min.js diff --git a/octoprint/static/js/ui.js b/octoprint/static/js/ui.js index d35ebdc7..8c187f4e 100644 --- a/octoprint/static/js/ui.js +++ b/octoprint/static/js/ui.js @@ -8,6 +8,8 @@ function LoginStateViewModel() { self.isAdmin = ko.observable(false); self.isUser = ko.observable(false); + self.currentUser = ko.observable(undefined); + self.userMenuText = ko.computed(function() { if (self.loggedIn()) { return "\"" + self.username() + "\""; @@ -16,6 +18,11 @@ function LoginStateViewModel() { } }) + self.subscribers = []; + self.subscribe = function(callback) { + self.subscribers.push(callback); + } + self.requestData = function() { $.ajax({ url: AJAX_BASEURL + "login", @@ -31,11 +38,19 @@ function LoginStateViewModel() { self.username(response.name); self.isUser(response.user); self.isAdmin(response.admin); + + self.currentUser(response); + + _.each(self.subscribers, function(callback) { callback("login", response); }); } else { self.loggedIn(false); self.username(undefined); self.isUser(false); self.isAdmin(false); + + self.currentUser(undefined); + + _.each(self.subscribers, function(callback) { callback("logout", {}); }); } } @@ -44,12 +59,16 @@ function LoginStateViewModel() { var password = $("#login_pass").val(); var remember = $("#login_remember").is(":checked"); + $("#login_user").val(""); + $("#login_pass").val(""); + $("#login_remember").prop("checked", false); + $.ajax({ url: AJAX_BASEURL + "login", type: "POST", data: {"user": username, "pass": password, "remember": remember}, success: function(response) { - $.pnotify({title: "Login successful", text: "You are now logged in", type: "success"}); + $.pnotify({title: "Login successful", text: "You are now logged in as \"" + response.name + "\"", type: "success"}); self.fromResponse(response); }, error: function(jqXHR, textStatus, errorThrown) { @@ -647,66 +666,6 @@ function ControlViewModel(loginStateViewModel) { } -function SpeedViewModel(loginStateViewModel) { - var self = this; - - self.loginState = loginStateViewModel; - - self.outerWall = ko.observable(undefined); - self.innerWall = ko.observable(undefined); - self.fill = ko.observable(undefined); - self.support = ko.observable(undefined); - - self.isErrorOrClosed = ko.observable(undefined); - self.isOperational = ko.observable(undefined); - self.isPrinting = ko.observable(undefined); - self.isPaused = ko.observable(undefined); - self.isError = ko.observable(undefined); - self.isReady = ko.observable(undefined); - self.isLoading = ko.observable(undefined); - - self._fromCurrentData = function(data) { - self._processStateData(data.state); - } - - self._fromHistoryData = function(data) { - self._processStateData(data.state); - } - - self._processStateData = function(data) { - self.isErrorOrClosed(data.flags.closedOrError); - self.isOperational(data.flags.operational); - self.isPaused(data.flags.paused); - self.isPrinting(data.flags.printing); - self.isError(data.flags.error); - self.isReady(data.flags.ready); - self.isLoading(data.flags.loading); - } - - self.requestData = function() { - $.ajax({ - url: AJAX_BASEURL + "control/speed", - type: "GET", - dataType: "json", - success: self._fromResponse - }); - } - - self._fromResponse = function(response) { - if (response.feedrate) { - self.outerWall(response.feedrate.outerWall); - self.innerWall(response.feedrate.innerWall); - self.fill(response.feedrate.fill); - self.support(response.feedrate.support); - } else { - self.outerWall(undefined); - self.innerWall(undefined); - self.fill(undefined); - self.support(undefined); - } - } -} - function TerminalViewModel(loginStateViewModel) { var self = this; @@ -822,11 +781,11 @@ function GcodeFilesViewModel(loginStateViewModel) { ); self.isLoadActionPossible = ko.computed(function() { - return !self.isPrinting() && !self.isPaused() && !self.isLoading(); + return self.loginState.isUser() && !self.isPrinting() && !self.isPaused() && !self.isLoading(); }); self.isLoadAndPrintActionPossible = ko.computed(function() { - return self.isOperational() && self.isLoadActionPossible(); + return self.loginState.isUser() && self.isOperational() && self.isLoadActionPossible(); }); self.fromCurrentData = function(data) { @@ -1117,7 +1076,7 @@ function UsersViewModel(loginStateViewModel) { "name", [], CONFIG_USERSPERPAGE - ) + ); self.emptyUser = {name: "", admin: false, active: false}; @@ -1148,6 +1107,8 @@ function UsersViewModel(loginStateViewModel) { }); self.requestData = function() { + if (!CONFIG_ACCESS_CONTROL) return; + $.ajax({ url: AJAX_BASEURL + "users", type: "GET", @@ -1161,11 +1122,16 @@ function UsersViewModel(loginStateViewModel) { } self.showAddUserDialog = function() { + if (!CONFIG_ACCESS_CONTROL) return; + self.currentUser(undefined); + self.editorActive(true); $("#settings-usersDialogAddUser").modal("show"); } self.confirmAddUser = function() { + if (!CONFIG_ACCESS_CONTROL) return; + var user = {name: self.editorUsername(), password: self.editorPassword(), admin: self.editorAdmin(), active: self.editorActive()}; self.addUser(user, function() { // close dialog @@ -1175,11 +1141,15 @@ function UsersViewModel(loginStateViewModel) { } self.showEditUserDialog = function(user) { + if (!CONFIG_ACCESS_CONTROL) return; + self.currentUser(user); $("#settings-usersDialogEditUser").modal("show"); } self.confirmEditUser = function() { + if (!CONFIG_ACCESS_CONTROL) return; + var user = self.currentUser(); user.active = self.editorActive(); user.admin = self.editorAdmin(); @@ -1193,11 +1163,15 @@ function UsersViewModel(loginStateViewModel) { } self.showChangePasswordDialog = function(user) { + if (!CONFIG_ACCESS_CONTROL) return; + self.currentUser(user); $("#settings-usersDialogChangePassword").modal("show"); } self.confirmChangePassword = function() { + if (!CONFIG_ACCESS_CONTROL) return; + self.updatePassword(self.currentUser().name, self.editorPassword(), function() { // close dialog self.currentUser(undefined); @@ -1208,6 +1182,7 @@ function UsersViewModel(loginStateViewModel) { //~~ AJAX calls self.addUser = function(user, callback) { + if (!CONFIG_ACCESS_CONTROL) return; if (user === undefined) return; $.ajax({ @@ -1223,7 +1198,9 @@ function UsersViewModel(loginStateViewModel) { } self.removeUser = function(user, callback) { + if (!CONFIG_ACCESS_CONTROL) return; if (user === undefined) return; + if (user.name == loginStateViewModel.username()) { // we do not allow to delete ourself $.pnotify({title: "Not possible", text: "You may not delete your own account.", type: "error"}); @@ -1241,6 +1218,7 @@ function UsersViewModel(loginStateViewModel) { } self.updateUser = function(user, callback) { + if (!CONFIG_ACCESS_CONTROL) return; if (user === undefined) return; $.ajax({ @@ -1256,6 +1234,8 @@ function UsersViewModel(loginStateViewModel) { } self.updatePassword = function(username, password, callback) { + if (!CONFIG_ACCESS_CONTROL) return; + $.ajax({ url: AJAX_BASEURL + "users/" + username + "/password", type: "PUT", @@ -1316,7 +1296,6 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) { dataType: "json", success: self.fromResponse }); - self.users.requestData(); } self.fromResponse = function(response) { @@ -1399,12 +1378,13 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) { } -function NavigationViewModel(loginStateViewModel, appearanceViewModel, settingsViewModel) { +function NavigationViewModel(loginStateViewModel, appearanceViewModel, settingsViewModel, usersViewModel) { var self = this; self.loginState = loginStateViewModel; self.appearance = appearanceViewModel; self.systemActions = settingsViewModel.system_actions; + self.users = usersViewModel; self.triggerAction = function(action) { var callback = function() { @@ -1431,7 +1411,7 @@ function NavigationViewModel(loginStateViewModel, appearanceViewModel, settingsV } } -function DataUpdater(loginStateViewModel, connectionViewModel, printerStateViewModel, temperatureViewModel, controlViewModel, speedViewModel, terminalViewModel, gcodeFilesViewModel, timelapseViewModel, gcodeViewModel) { +function DataUpdater(loginStateViewModel, connectionViewModel, printerStateViewModel, temperatureViewModel, controlViewModel, terminalViewModel, gcodeFilesViewModel, timelapseViewModel, gcodeViewModel) { var self = this; self.loginStateViewModel = loginStateViewModel; @@ -1440,7 +1420,6 @@ function DataUpdater(loginStateViewModel, connectionViewModel, printerStateViewM self.temperatureViewModel = temperatureViewModel; self.controlViewModel = controlViewModel; self.terminalViewModel = terminalViewModel; - self.speedViewModel = speedViewModel; self.gcodeFilesViewModel = gcodeFilesViewModel; self.timelapseViewModel = timelapseViewModel; self.gcodeViewModel = gcodeViewModel; @@ -1755,12 +1734,11 @@ $(function() { var appearanceViewModel = new AppearanceViewModel(settingsViewModel); var temperatureViewModel = new TemperatureViewModel(loginStateViewModel, settingsViewModel); var controlViewModel = new ControlsViewModel(loginStateViewModel); - var speedViewModel = new SpeedViewModel(loginStateViewModel); var terminalViewModel = new TerminalViewModel(loginStateViewModel); var gcodeFilesViewModel = new GcodeFilesViewModel(loginStateViewModel); var timelapseViewModel = new TimelapseViewModel(loginStateViewModel); var gcodeViewModel = new GcodeViewModel(loginStateViewModel); - var navigationViewModel = new NavigationViewModel(loginStateViewModel, appearanceViewModel, settingsViewModel); + var navigationViewModel = new NavigationViewModel(loginStateViewModel, appearanceViewModel, settingsViewModel, usersViewModel); var dataUpdater = new DataUpdater( loginStateViewModel, @@ -1768,7 +1746,6 @@ $(function() { printerStateViewModel, temperatureViewModel, controlViewModel, - speedViewModel, terminalViewModel, gcodeFilesViewModel, timelapseViewModel, @@ -1821,28 +1798,6 @@ $(function() { terminalViewModel.updateOutput(); }); - //~~ Speed controls - - function speedCommand(structure) { - var speedSetting = $("#speed_" + structure).val(); - if (speedSetting) { - $.ajax({ - url: AJAX_BASEURL + "control/speed", - type: "POST", - dataType: "json", - data: structure + "=" + speedSetting, - success: function(response) { - $("#speed_" + structure).val("") - speedViewModel.fromResponse(response); - } - }) - } - } - $("#speed_outerWall_set").click(function() {speedCommand("outerWall")}); - $("#speed_innerWall_set").click(function() {speedCommand("innerWall")}); - $("#speed_support_set").click(function() {speedCommand("support")}); - $("#speed_fill_set").click(function() {speedCommand("fill")}); - //~~ Terminal $("#terminal-send").click(function () { @@ -1909,14 +1864,12 @@ $(function() { } } - ko.applyBindings(connectionViewModel, document.getElementById("connection")); - ko.applyBindings(printerStateViewModel, document.getElementById("state")); - ko.applyBindings(gcodeFilesViewModel, document.getElementById("files")); - ko.applyBindings(gcodeFilesViewModel, document.getElementById("files-heading")); + ko.applyBindings(connectionViewModel, document.getElementById("connection_accordion")); + ko.applyBindings(printerStateViewModel, document.getElementById("state_accordion")); + ko.applyBindings(gcodeFilesViewModel, document.getElementById("files_accordion")); ko.applyBindings(temperatureViewModel, document.getElementById("temp")); ko.applyBindings(controlViewModel, document.getElementById("control")); ko.applyBindings(terminalViewModel, document.getElementById("term")); - ko.applyBindings(speedViewModel, document.getElementById("speed")); ko.applyBindings(gcodeViewModel, document.getElementById("gcode")); ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog")); ko.applyBindings(navigationViewModel, document.getElementById("navbar")); @@ -1930,6 +1883,7 @@ $(function() { if (gCodeVisualizerElement) { gcodeViewModel.initialize(); } + //~~ startup commands loginStateViewModel.requestData(); @@ -1937,7 +1891,19 @@ $(function() { controlViewModel.requestData(); gcodeFilesViewModel.requestData(); timelapseViewModel.requestData(); - settingsViewModel.requestData(); + + loginStateViewModel.subscribe(function(change, data) { + if ("login" == change) { + $("#gcode_upload").fileupload("enable"); + + settingsViewModel.requestData(); + if (data.admin) { + usersViewModel.requestData(); + } + } else { + $("#gcode_upload").fileupload("disable"); + } + }) //~~ UI stuff diff --git a/octoprint/static/js/underscore-min.js b/octoprint/static/js/underscore.js similarity index 100% rename from octoprint/static/js/underscore-min.js rename to octoprint/static/js/underscore.js diff --git a/octoprint/templates/dialogs.html b/octoprint/templates/dialogs.jinja2 similarity index 100% rename from octoprint/templates/dialogs.html rename to octoprint/templates/dialogs.jinja2 diff --git a/octoprint/templates/index.html b/octoprint/templates/index.jinja2 similarity index 77% rename from octoprint/templates/index.html rename to octoprint/templates/index.jinja2 index 56037164..d0215627 100644 --- a/octoprint/templates/index.html +++ b/octoprint/templates/index.jinja2 @@ -17,13 +17,15 @@ @@ -35,13 +37,13 @@ OctoPrint @@ -80,7 +83,7 @@
-
+
@@ -97,7 +100,7 @@
-
+
@@ -124,8 +127,8 @@
-
-
+
+
Files
@@ -156,7 +159,7 @@ -  |  |  +  |  |  @@ -172,16 +175,18 @@
  • ยป
  • - - - Upload - - -
    -
    -
    -
    - Hint: You can also drag and drop files on this page to upload them. +
    + + + Upload + + +
    +
    +
    +
    + Hint: You can also drag and drop files on this page to upload them. +
    @@ -193,7 +198,6 @@
  • Temperature
  • Control
  • {% if enableGCodeVisualizer %}
  • GCode Viewer
  • {% endif %} -
  • Terminal
  • {% if enableTimelapse %}
  • Timelapse
  • {% endif %} @@ -211,27 +215,29 @@ - -
    - - °C -
    -
    - - - +
    + +
    + + °C +
    +
    + + + +
    @@ -241,27 +247,29 @@ - -
    - - °C -
    -
    - - - +
    + +
    + + °C +
    +
    + + + +
    @@ -273,7 +281,7 @@
    {% endif %} -
    + -
    +