"Writing" backend methods now protected (need logged in user (or dummy user if access control is disabled) to perform action), most "writing" frontend controls disabled if no logged in (or dummy) user returned from backend.
parent
b27e1ce15e
commit
1febcd671a
|
@ -138,11 +138,13 @@ def connect():
|
|||
return jsonify(state="Connecting")
|
||||
|
||||
@app.route(BASEURL + "control/disconnect", methods=["POST"])
|
||||
@login_required
|
||||
def disconnect():
|
||||
printer.disconnect()
|
||||
return jsonify(state="Offline")
|
||||
|
||||
@app.route(BASEURL + "control/command", methods=["POST"])
|
||||
@login_required
|
||||
def printerCommand():
|
||||
if "application/json" in request.headers["Content-Type"]:
|
||||
data = request.json
|
||||
|
@ -166,21 +168,25 @@ def printerCommand():
|
|||
return jsonify(SUCCESS)
|
||||
|
||||
@app.route(BASEURL + "control/print", methods=["POST"])
|
||||
@login_required
|
||||
def printGcode():
|
||||
printer.startPrint()
|
||||
return jsonify(SUCCESS)
|
||||
|
||||
@app.route(BASEURL + "control/pause", methods=["POST"])
|
||||
@login_required
|
||||
def pausePrint():
|
||||
printer.togglePausePrint()
|
||||
return jsonify(SUCCESS)
|
||||
|
||||
@app.route(BASEURL + "control/cancel", methods=["POST"])
|
||||
@login_required
|
||||
def cancelPrint():
|
||||
printer.cancelPrint()
|
||||
return jsonify(SUCCESS)
|
||||
|
||||
@app.route(BASEURL + "control/temperature", methods=["POST"])
|
||||
@login_required
|
||||
def setTargetTemperature():
|
||||
if not printer.isOperational():
|
||||
return jsonify(SUCCESS)
|
||||
|
@ -198,6 +204,7 @@ def setTargetTemperature():
|
|||
return jsonify(SUCCESS)
|
||||
|
||||
@app.route(BASEURL + "control/jog", methods=["POST"])
|
||||
@login_required
|
||||
def jog():
|
||||
if not printer.isOperational() or printer.isPrinting():
|
||||
# do not jog when a print job is running or we don't have a connection
|
||||
|
@ -234,6 +241,7 @@ def getSpeedValues():
|
|||
return jsonify(feedrate=printer.feedrateState())
|
||||
|
||||
@app.route(BASEURL + "control/speed", methods=["POST"])
|
||||
@login_required
|
||||
def speed():
|
||||
if not printer.isOperational():
|
||||
return jsonify(SUCCESS)
|
||||
|
@ -261,6 +269,7 @@ def readGcodeFile(filename):
|
|||
return send_from_directory(settings().getBaseFolder("uploads"), filename, as_attachment=True)
|
||||
|
||||
@app.route(BASEURL + "gcodefiles/upload", methods=["POST"])
|
||||
@login_required
|
||||
def uploadGcodeFile():
|
||||
filename = None
|
||||
if "gcode_file" in request.files.keys():
|
||||
|
@ -269,6 +278,7 @@ def uploadGcodeFile():
|
|||
return jsonify(files=gcodeManager.getAllFileData(), filename=filename)
|
||||
|
||||
@app.route(BASEURL + "gcodefiles/load", methods=["POST"])
|
||||
@login_required
|
||||
def loadGcodeFile():
|
||||
if "filename" in request.values.keys():
|
||||
printAfterLoading = False
|
||||
|
@ -280,6 +290,7 @@ def loadGcodeFile():
|
|||
return jsonify(SUCCESS)
|
||||
|
||||
@app.route(BASEURL + "gcodefiles/delete", methods=["POST"])
|
||||
@login_required
|
||||
def deleteGcodeFile():
|
||||
if "filename" in request.values.keys():
|
||||
filename = request.values["filename"]
|
||||
|
@ -318,6 +329,7 @@ def downloadTimelapse(filename):
|
|||
return send_from_directory(settings().getBaseFolder("timelapse"), filename, as_attachment=True)
|
||||
|
||||
@app.route(BASEURL + "timelapse/<filename>", methods=["DELETE"])
|
||||
@login_required
|
||||
def deleteTimelapse(filename):
|
||||
if util.isAllowedFile(filename, set(["mpg"])):
|
||||
secure = os.path.join(settings().getBaseFolder("timelapse"), secure_filename(filename))
|
||||
|
@ -326,6 +338,7 @@ def deleteTimelapse(filename):
|
|||
return getTimelapseData()
|
||||
|
||||
@app.route(BASEURL + "timelapse/config", methods=["POST"])
|
||||
@login_required
|
||||
def setTimelapseConfig():
|
||||
if request.values.has_key("type"):
|
||||
type = request.values["type"]
|
||||
|
@ -389,6 +402,7 @@ def getSettings():
|
|||
})
|
||||
|
||||
@app.route(BASEURL + "settings", methods=["POST"])
|
||||
@login_required
|
||||
def setSettings():
|
||||
if "application/json" in request.headers["Content-Type"]:
|
||||
data = request.json
|
||||
|
@ -434,6 +448,7 @@ def setSettings():
|
|||
#~~ system control
|
||||
|
||||
@app.route(BASEURL + "system", methods=["POST"])
|
||||
@login_required
|
||||
def performSystemAction():
|
||||
logger = logging.getLogger(__name__)
|
||||
if request.values.has_key("action"):
|
||||
|
@ -467,15 +482,14 @@ def login():
|
|||
|
||||
user = userManager.findUser(username)
|
||||
if user is not None:
|
||||
passwordHash = users.UserManager.createPasswordHash(password)
|
||||
if passwordHash == user.passwordHash:
|
||||
if user.check_password(users.UserManager.createPasswordHash(password)):
|
||||
login_user(user, remember=remember)
|
||||
return jsonify({"name": user.username, "roles": user.roles})
|
||||
return jsonify({"name": user.get_name(), "user": user.is_user(), "admin": user.is_admin()})
|
||||
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.username, "roles": user.roles})
|
||||
return jsonify({"name": user.get_name(), "user": user.is_user(), "admin": user.is_admin()})
|
||||
else:
|
||||
return jsonify(SUCCESS)
|
||||
|
||||
|
@ -488,8 +502,7 @@ def logout():
|
|||
def load_user(id):
|
||||
if userManager is not None:
|
||||
return userManager.findUser(id)
|
||||
else:
|
||||
return users.DummyUser()
|
||||
return None
|
||||
|
||||
#~~ startup code
|
||||
class Server():
|
||||
|
|
|
@ -1,8 +1,80 @@
|
|||
//~~ View models
|
||||
|
||||
function ConnectionViewModel() {
|
||||
function LoginStateViewModel() {
|
||||
var self = this;
|
||||
|
||||
self.loggedIn = ko.observable(false);
|
||||
self.username = ko.observable(undefined);
|
||||
self.isAdmin = ko.observable(false);
|
||||
self.isUser = ko.observable(false);
|
||||
|
||||
self.userMenuText = ko.computed(function() {
|
||||
if (self.loggedIn()) {
|
||||
return "\"" + self.username() + "\"";
|
||||
} else {
|
||||
return "Login";
|
||||
}
|
||||
})
|
||||
|
||||
self.requestData = function() {
|
||||
$.ajax({
|
||||
url: AJAX_BASEURL + "login",
|
||||
type: "POST",
|
||||
data: {"passive": true},
|
||||
success: self.fromResponse
|
||||
})
|
||||
}
|
||||
|
||||
self.fromResponse = function(response) {
|
||||
if (response && response.name) {
|
||||
self.loggedIn(true);
|
||||
self.username(response.name);
|
||||
self.isUser(response.user);
|
||||
self.isAdmin(response.admin);
|
||||
} else {
|
||||
self.loggedIn(false);
|
||||
self.username(undefined);
|
||||
self.isUser(false);
|
||||
self.isAdmin(false);
|
||||
}
|
||||
}
|
||||
|
||||
self.login = function() {
|
||||
var username = $("#login_user").val();
|
||||
var password = $("#login_pass").val();
|
||||
var remember = $("#login_remember").is(":checked");
|
||||
|
||||
$.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"});
|
||||
self.fromResponse(response);
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
$.pnotify({title: "Login failed", text: "User unknown or wrong password", type: "error"});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
self.logout = function() {
|
||||
$.ajax({
|
||||
url: AJAX_BASEURL + "logout",
|
||||
type: "POST",
|
||||
success: function(response) {
|
||||
$.pnotify({title: "Logout successful", text: "You are now logged out", type: "success"});
|
||||
self.fromResponse(response);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function ConnectionViewModel(loginStateViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
|
||||
self.portOptions = ko.observableArray(undefined);
|
||||
self.baudrateOptions = ko.observableArray(undefined);
|
||||
self.selectedPort = ko.observable(undefined);
|
||||
|
@ -107,9 +179,11 @@ function ConnectionViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
function PrinterStateViewModel() {
|
||||
function PrinterStateViewModel(loginStateViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
|
||||
self.stateString = ko.observable(undefined);
|
||||
self.isErrorOrClosed = ko.observable(undefined);
|
||||
self.isOperational = ko.observable(undefined);
|
||||
|
@ -238,9 +312,11 @@ function PrinterStateViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
function TemperatureViewModel(settingsViewModel) {
|
||||
function TemperatureViewModel(loginStateViewModel, settingsViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
|
||||
self.temp = ko.observable(undefined);
|
||||
self.bedTemp = ko.observable(undefined);
|
||||
self.targetTemp = ko.observable(undefined);
|
||||
|
@ -412,9 +488,11 @@ function TemperatureViewModel(settingsViewModel) {
|
|||
}
|
||||
}
|
||||
|
||||
function ControlViewModel() {
|
||||
function ControlViewModel(loginStateViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
|
||||
self.isErrorOrClosed = ko.observable(undefined);
|
||||
self.isOperational = ko.observable(undefined);
|
||||
self.isPrinting = ko.observable(undefined);
|
||||
|
@ -567,9 +645,11 @@ function ControlViewModel() {
|
|||
|
||||
}
|
||||
|
||||
function SpeedViewModel() {
|
||||
function SpeedViewModel(loginStateViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
|
||||
self.outerWall = ko.observable(undefined);
|
||||
self.innerWall = ko.observable(undefined);
|
||||
self.fill = ko.observable(undefined);
|
||||
|
@ -625,9 +705,11 @@ function SpeedViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
function TerminalViewModel() {
|
||||
function TerminalViewModel(loginStateViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
|
||||
self.log = [];
|
||||
|
||||
self.isErrorOrClosed = ko.observable(undefined);
|
||||
|
@ -654,6 +736,7 @@ function TerminalViewModel() {
|
|||
if (!self.log)
|
||||
self.log = []
|
||||
self.log = self.log.concat(data)
|
||||
self.log = self.log.slice(-300)
|
||||
self.updateOutput();
|
||||
}
|
||||
|
||||
|
@ -690,9 +773,11 @@ function TerminalViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
function GcodeFilesViewModel() {
|
||||
function GcodeFilesViewModel(loginStateViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
|
||||
self.isErrorOrClosed = ko.observable(undefined);
|
||||
self.isOperational = ko.observable(undefined);
|
||||
self.isPrinting = ko.observable(undefined);
|
||||
|
@ -824,9 +909,11 @@ function GcodeFilesViewModel() {
|
|||
|
||||
}
|
||||
|
||||
function TimelapseViewModel() {
|
||||
function TimelapseViewModel(loginStateViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
|
||||
self.timelapseType = ko.observable(undefined);
|
||||
self.timelapseTimedInterval = ko.observable(undefined);
|
||||
|
||||
|
@ -943,9 +1030,11 @@ function TimelapseViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
function GcodeViewModel() {
|
||||
function GcodeViewModel(loginStateViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
|
||||
self.loadedFilename = undefined;
|
||||
self.status = 'idle';
|
||||
self.enabled = false;
|
||||
|
@ -1006,9 +1095,11 @@ function GcodeViewModel() {
|
|||
|
||||
}
|
||||
|
||||
function SettingsViewModel() {
|
||||
function SettingsViewModel(loginStateViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
|
||||
self.appearance_name = ko.observable(undefined);
|
||||
self.appearance_color = ko.observable(undefined);
|
||||
|
||||
|
@ -1135,9 +1226,10 @@ function SettingsViewModel() {
|
|||
|
||||
}
|
||||
|
||||
function NavigationViewModel(appearanceViewModel, settingsViewModel) {
|
||||
function NavigationViewModel(loginStateViewModel, appearanceViewModel, settingsViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
self.appearance = appearanceViewModel;
|
||||
self.systemActions = settingsViewModel.system_actions;
|
||||
|
||||
|
@ -1166,9 +1258,10 @@ function NavigationViewModel(appearanceViewModel, settingsViewModel) {
|
|||
}
|
||||
}
|
||||
|
||||
function DataUpdater(connectionViewModel, printerStateViewModel, temperatureViewModel, controlViewModel, speedViewModel, terminalViewModel, gcodeFilesViewModel, timelapseViewModel, gcodeViewModel) {
|
||||
function DataUpdater(loginStateViewModel, connectionViewModel, printerStateViewModel, temperatureViewModel, controlViewModel, speedViewModel, terminalViewModel, gcodeFilesViewModel, timelapseViewModel, gcodeViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginStateViewModel = loginStateViewModel;
|
||||
self.connectionViewModel = connectionViewModel;
|
||||
self.printerStateViewModel = printerStateViewModel;
|
||||
self.temperatureViewModel = temperatureViewModel;
|
||||
|
@ -1185,6 +1278,7 @@ function DataUpdater(connectionViewModel, printerStateViewModel, temperatureView
|
|||
$("#offline_overlay").hide();
|
||||
self.timelapseViewModel.requestData();
|
||||
$("#webcam_image").attr("src", CONFIG_WEBCAM_STREAM + "?" + new Date().getTime());
|
||||
self.loginStateViewModel.requestData();
|
||||
}
|
||||
})
|
||||
self._socket.on("disconnect", function() {
|
||||
|
@ -1480,20 +1574,22 @@ function AppearanceViewModel(settingsViewModel) {
|
|||
$(function() {
|
||||
|
||||
//~~ View models
|
||||
var connectionViewModel = new ConnectionViewModel();
|
||||
var printerStateViewModel = new PrinterStateViewModel();
|
||||
var settingsViewModel = new SettingsViewModel();
|
||||
var loginStateViewModel = new LoginStateViewModel();
|
||||
var connectionViewModel = new ConnectionViewModel(loginStateViewModel);
|
||||
var printerStateViewModel = new PrinterStateViewModel(loginStateViewModel);
|
||||
var settingsViewModel = new SettingsViewModel(loginStateViewModel);
|
||||
var appearanceViewModel = new AppearanceViewModel(settingsViewModel);
|
||||
var temperatureViewModel = new TemperatureViewModel(settingsViewModel);
|
||||
var controlViewModel = new ControlViewModel();
|
||||
var speedViewModel = new SpeedViewModel();
|
||||
var terminalViewModel = new TerminalViewModel();
|
||||
var gcodeFilesViewModel = new GcodeFilesViewModel();
|
||||
var timelapseViewModel = new TimelapseViewModel();
|
||||
var gcodeViewModel = new GcodeViewModel();
|
||||
var navigationViewModel = new NavigationViewModel(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 dataUpdater = new DataUpdater(
|
||||
loginStateViewModel,
|
||||
connectionViewModel,
|
||||
printerStateViewModel,
|
||||
temperatureViewModel,
|
||||
|
@ -1505,7 +1601,8 @@ $(function() {
|
|||
gcodeViewModel
|
||||
);
|
||||
|
||||
//work around a stupid iOS6 bug where ajax requests get cached and only work once, as described at http://stackoverflow.com/questions/12506897/is-safari-on-ios-6-caching-ajax-results
|
||||
// work around a stupid iOS6 bug where ajax requests get cached and only work once, as described at
|
||||
// http://stackoverflow.com/questions/12506897/is-safari-on-ios-6-caching-ajax-results
|
||||
$.ajaxSetup({
|
||||
type: 'POST',
|
||||
headers: { "cache-control": "no-cache" }
|
||||
|
@ -1547,6 +1644,7 @@ $(function() {
|
|||
})
|
||||
$('#tabs a[data-toggle="tab"]').on('shown', function (e) {
|
||||
temperatureViewModel.updatePlot();
|
||||
terminalViewModel.updateOutput();
|
||||
});
|
||||
|
||||
//~~ Speed controls
|
||||
|
@ -1618,71 +1716,6 @@ $(function() {
|
|||
//~~ Offline overlay
|
||||
$("#offline_overlay_reconnect").click(function() {dataUpdater.reconnect()});
|
||||
|
||||
//~~ Alert
|
||||
|
||||
/*
|
||||
function displayAlert(text, timeout, type) {
|
||||
var placeholder = $("#alert_placeholder");
|
||||
|
||||
var alertType = "";
|
||||
if (type == "success" || type == "error" || type == "info") {
|
||||
alertType = " alert-" + type;
|
||||
}
|
||||
|
||||
placeholder.append($("<div id='activeAlert' class='alert " + alertType + " fade in' data-alert='alert'><p>" + text + "</p></div>"));
|
||||
placeholder.fadeIn();
|
||||
$("#activeAlert").delay(timeout).fadeOut("slow", function() {$(this).remove(); $("#alert_placeholder").hide();});
|
||||
}
|
||||
*/
|
||||
|
||||
//~~ Login/logout
|
||||
|
||||
$("#login_button").click(function() {
|
||||
var username = $("#login_user").val();
|
||||
var password = $("#login_pass").val();
|
||||
var remember = $("#login_remember").is(":checked");
|
||||
|
||||
$.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"});
|
||||
$("#login_dropdown_text").text("\"" + response.name + "\"");
|
||||
$("#login_dropdown_loggedout").removeClass("dropdown-menu").addClass("hide");
|
||||
$("#login_dropdown_loggedin").removeClass("hide").addClass("dropdown-menu");
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
$.pnotify({title: "Login failed", text: "User unknown or wrong password", type: "error"});
|
||||
}
|
||||
})
|
||||
});
|
||||
$("#logout_button").click(function(){
|
||||
$.ajax({
|
||||
url: AJAX_BASEURL + "logout",
|
||||
type: "POST",
|
||||
success: function(response) {
|
||||
$.pnotify({title: "Logout successful", text: "You are now logged out", type: "success"});
|
||||
$("#login_dropdown_text").text("Login");
|
||||
$("#login_dropdown_loggedin").removeClass("dropdown-menu").addClass("hide");
|
||||
$("#login_dropdown_loggedout").removeClass("hide").addClass("dropdown-menu");
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
$.ajax({
|
||||
url: AJAX_BASEURL + "login",
|
||||
type: "POST",
|
||||
data: {"passive": true},
|
||||
success: function(response) {
|
||||
if (response["name"]) {
|
||||
$("#login_dropdown_text").text("\"" + response.name + "\"");
|
||||
$("#login_dropdown_loggedout").removeClass("dropdown-menu").addClass("hide");
|
||||
$("#login_dropdown_loggedin").removeClass("hide").addClass("dropdown-menu");
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//~~ knockout.js bindings
|
||||
|
||||
ko.bindingHandlers.popover = {
|
||||
|
@ -1717,14 +1750,15 @@ $(function() {
|
|||
|
||||
var timelapseElement = document.getElementById("timelapse");
|
||||
if (timelapseElement) {
|
||||
ko.applyBindings(timelapseViewModel, document.getElementById("timelapse"));
|
||||
ko.applyBindings(timelapseViewModel, timelapseElement);
|
||||
}
|
||||
var gCodeVisualizerElement = document.getElementById("gcode");
|
||||
if(gCodeVisualizerElement){
|
||||
if (gCodeVisualizerElement) {
|
||||
gcodeViewModel.initialize();
|
||||
}
|
||||
//~~ startup commands
|
||||
|
||||
loginStateViewModel.requestData();
|
||||
connectionViewModel.requestData();
|
||||
controlViewModel.requestData();
|
||||
gcodeFilesViewModel.requestData();
|
||||
|
@ -1745,7 +1779,7 @@ $(function() {
|
|||
|
||||
$.pnotify.defaults.history = false;
|
||||
|
||||
// Fix input element click problem
|
||||
// Fix input element click problem on login dialog
|
||||
$('.dropdown input, .dropdown label').click(function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
|
|
@ -33,9 +33,13 @@
|
|||
<a class="brand" href="#"><img src="{{ url_for('static', filename='img/tentacle-20x20.png') }}"> <span data-bind="text: appearance.brand">OctoPrint</span></a>
|
||||
<div class="nav-collapse">
|
||||
<ul class="nav pull-right">
|
||||
<li><a id="navbar_show_settings" class="pull-right" href="#settings_dialog"><i class="icon-wrench"></i> Settings</a></li>
|
||||
<li class="hide" data-bind="css: {hide: !loginState.isAdmin()}">
|
||||
<a id="navbar_show_settings" class="pull-right" href="#settings_dialog">
|
||||
<i class="icon-wrench"></i> Settings
|
||||
</a>
|
||||
</li>
|
||||
{% if enableSystemMenu %}
|
||||
<li class="dropdown">
|
||||
<li class="dropdown hide" data-bind="css: {hide: !loginState.isAdmin()}">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-off"></i> System
|
||||
<b class="caret"></b>
|
||||
|
@ -48,10 +52,10 @@
|
|||
{% if enableAccessControl %}
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-user"></i> <span id="login_dropdown_text">Login</span>
|
||||
<i class="icon-user"></i> <span data-bind="text: loginState.userMenuText">Login</span>
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<div id="login_dropdown_loggedout" class="dropdown-menu" style="padding: 15px">
|
||||
<div id="login_dropdown_loggedout" style="padding: 15px" class="dropdown-menu" data-bind="css: {hide: loginState.loggedIn(), 'dropdown-menu': !loginState.loggedIn()}">
|
||||
<label for="login_user">Username</label>
|
||||
<input type="text" id="login_user" placeholder="Username">
|
||||
<label for="login_pass">Password</label>
|
||||
|
@ -59,12 +63,10 @@
|
|||
<label class="checkbox">
|
||||
<input type="checkbox" id="login_remember"> Remember me
|
||||
</label>
|
||||
<button class="btn btn-block btn-primary" id="login_button">Login</button>
|
||||
<button class="btn btn-block btn-primary" id="login_button" data-bind="click: loginState.login">Login</button>
|
||||
</div>
|
||||
<ul id="login_dropdown_loggedin" class="hide">
|
||||
<li><a href="#">Profile</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="#" id="logout_button">Logout</a></li>
|
||||
<ul id="login_dropdown_loggedin" class="hide" data-bind="css: {hide: !loginState.loggedIn(), 'dropdown-menu': loginState.loggedIn()}">
|
||||
<li><a href="#" id="logout_button" data-bind="click: loginState.logout">Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
@ -82,14 +84,14 @@
|
|||
</div>
|
||||
<div class="accordion-body collapse in" id="connection">
|
||||
<div class="accordion-inner">
|
||||
<label for="connection_ports" data-bind="css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed">Serial Port</label>
|
||||
<select id="connection_ports" data-bind="options: portOptions, optionsCaption: 'AUTO', value: selectedPort, css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed"></select>
|
||||
<label for="connection_baudrates" data-bind="css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed">Baudrate</label>
|
||||
<select id="connection_baudrates" data-bind="options: baudrateOptions, optionsCaption: 'AUTO', value: selectedBaudrate, css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed"></select>
|
||||
<label for="connection_ports" data-bind="css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed && loginState.isUser">Serial Port</label>
|
||||
<select id="connection_ports" data-bind="options: portOptions, optionsCaption: 'AUTO', value: selectedPort, css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed && loginState.isUser"></select>
|
||||
<label for="connection_baudrates" data-bind="css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed && loginState.isUser">Baudrate</label>
|
||||
<select id="connection_baudrates" data-bind="options: baudrateOptions, optionsCaption: 'AUTO', value: selectedBaudrate, css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed && loginState.isUser"></select>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="connection_save" data-bind="checked: saveSettings, css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed"> Save connection settings
|
||||
<input type="checkbox" id="connection_save" data-bind="checked: saveSettings, css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed && loginState.isUser"> Save connection settings
|
||||
</label>
|
||||
<button class="btn btn-block" id="printer_connect" data-bind="click: connect, text: buttonText()">Connect</button>
|
||||
<button class="btn btn-block" id="printer_connect" data-bind="click: connect, text: buttonText(), enable: loginState.isUser">Connect</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -113,9 +115,9 @@
|
|||
</div>
|
||||
|
||||
<div class="row-fluid print-control">
|
||||
<button class="btn btn-primary span4" data-bind="click: print, enable: isOperational() && isReady() && !isPrinting(), css: {'btn-danger': isPaused()}" id="job_print"><i class="icon-white" data-bind="css: {'icon-print': !isPaused(), 'icon-undo': isPaused()}"></i> <span data-bind="text: (isPaused() ? 'Restart' : 'Print')">Print</span></button>
|
||||
<button class="btn span4" id="job_pause" data-bind="click: pause, enable: isPrinting() || isPaused(), css: {active: isPaused()}"><i class="icon-pause"></i> <span>Pause</span></button>
|
||||
<button class="btn span4" id="job_cancel" data-bind="click: cancel, enable: isPrinting() || isPaused()"><i class="icon-stop"></i> Cancel</button>
|
||||
<button class="btn btn-primary span4" data-bind="click: print, enable: isOperational() && isReady() && !isPrinting() && loginState.isUser(), css: {'btn-danger': isPaused()}" id="job_print"><i class="icon-white" data-bind="css: {'icon-print': !isPaused(), 'icon-undo': isPaused()}"></i> <span data-bind="text: (isPaused() ? 'Restart' : 'Print')">Print</span></button>
|
||||
<button class="btn span4" id="job_pause" data-bind="click: pause, enable: isOperational() && (isPrinting() || isPaused()) && loginState.isUser(), css: {active: isPaused()}"><i class="icon-pause"></i> <span>Pause</span></button>
|
||||
<button class="btn span4" id="job_cancel" data-bind="click: cancel, enable: isOperational() && (isPrinting() || isPaused()) && loginState.isUser()"><i class="icon-stop"></i> Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -209,12 +211,12 @@
|
|||
|
||||
<label for="temp_newTemp">New Target</label>
|
||||
<div class="input-append">
|
||||
<input type="text" id="temp_newTemp" data-bind="attr: {placeholder: targetTemp}" class="tempInput">
|
||||
<input type="text" id="temp_newTemp" data-bind="attr: {placeholder: targetTemp}, enable: isOperational() && loginState.isUser()" class="tempInput">
|
||||
<span class="add-on">°C</span>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button type="submit" class="btn" id="temp_newTemp_set">Set</button>
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<button type="submit" class="btn" id="temp_newTemp_set" data-bind="enable: isOperational() && loginState.isUser()">Set</button>
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown" data-bind="enable: isOperational() && loginState.isUser()">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
|
@ -239,12 +241,12 @@
|
|||
|
||||
<label for="temp_newBedTemp">New Target</label>
|
||||
<div class="input-append">
|
||||
<input type="text" id="temp_newBedTemp" data-bind="attr: {placeholder: bedTargetTemp}" class="tempInput">
|
||||
<input type="text" id="temp_newBedTemp" data-bind="attr: {placeholder: bedTargetTemp}, enable: isOperational() && loginState.isUser()" class="tempInput">
|
||||
<span class="add-on">°C</span>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button type="submit" class="btn" id="temp_newBedTemp_set">Set</button>
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<button type="submit" class="btn" id="temp_newBedTemp_set" data-bind="enable: isOperational() && loginState.isUser()">Set</button>
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown" data-bind="enable: isOperational() && loginState.isUser()">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
|
@ -274,37 +276,37 @@
|
|||
<div class="jog-panel">
|
||||
<h1>X/Y</h1>
|
||||
<div>
|
||||
<button class="btn box" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendJogCommand('y',1) }"><i class="icon-arrow-up"></i></button>
|
||||
<button class="btn box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('y',1) }"><i class="icon-arrow-up"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box pull-left" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendJogCommand('x',-1) }"><i class="icon-arrow-left"></i></button>
|
||||
<button class="btn box pull-left" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendHomeCommand('XY') }"><i class="icon-home"></i></button>
|
||||
<button class="btn box pull-left" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendJogCommand('x',1) }"><i class="icon-arrow-right"></i></button>
|
||||
<button class="btn box pull-left" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('x',-1) }"><i class="icon-arrow-left"></i></button>
|
||||
<button class="btn box pull-left" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendHomeCommand('XY') }"><i class="icon-home"></i></button>
|
||||
<button class="btn box pull-left" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('x',1) }"><i class="icon-arrow-right"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendJogCommand('y',-1) }"><i class="icon-arrow-down"></i></button>
|
||||
<button class="btn box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('y',-1) }"><i class="icon-arrow-down"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Z jogging control panel -->
|
||||
<div class="jog-panel">
|
||||
<h1>Z</h1>
|
||||
<div>
|
||||
<button class="btn box" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendJogCommand('z',1) }"><i class="icon-arrow-up"></i></button>
|
||||
<button class="btn box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('z',1) }"><i class="icon-arrow-up"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendHomeCommand('Z') }"><i class="icon-home"></i></button>
|
||||
<button class="btn box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendHomeCommand('Z') }"><i class="icon-home"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendJogCommand('z',-1) }"><i class="icon-arrow-down"></i></button>
|
||||
<button class="btn box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('z',-1) }"><i class="icon-arrow-down"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Jog distance -->
|
||||
<div class="distance">
|
||||
<div class="btn-group" data-toggle="buttons-radio" id="jog_distance">
|
||||
<button type="button" class="btn" data-distance="0.1">0.1</button>
|
||||
<button type="button" class="btn" data-distance="1">1</button>
|
||||
<button type="button" class="btn active" data-distance="10">10</button>
|
||||
<button type="button" class="btn" data-distance="100">100</button>
|
||||
<button type="button" class="btn" data-distance="0.1" data-bind="enable: loginState.isUser()">0.1</button>
|
||||
<button type="button" class="btn" data-distance="1" data-bind="enable: loginState.isUser()">1</button>
|
||||
<button type="button" class="btn active" data-distance="10" data-bind="enable: loginState.isUser()">10</button>
|
||||
<button type="button" class="btn" data-distance="100" data-bind="enable: loginState.isUser()">100</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -313,20 +315,20 @@
|
|||
<h1>E</h1>
|
||||
<div>
|
||||
<div class="input-append control-box">
|
||||
<input type="text" class="input-mini text-right" data-bind="value: extrusionAmount, enable: isOperational() && !isPrinting(), attr: {placeholder: 5}">
|
||||
<input type="text" class="input-mini text-right" data-bind="value: extrusionAmount, enable: isOperational() && !isPrinting() && loginState.isUser(), attr: {placeholder: 5}">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
<button class="btn control-box" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendExtrudeCommand() }">Extrude</button>
|
||||
<button class="btn control-box" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendRetractCommand() }">Retract</button>
|
||||
<button class="btn control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendExtrudeCommand() }">Extrude</button>
|
||||
<button class="btn control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendRetractCommand() }">Retract</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- General control panel -->
|
||||
<div class="jog-panel">
|
||||
<h1>General</h1>
|
||||
<div>
|
||||
<button class="btn control-box" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendCustomCommand({type:'command',command:'M18'}) }"><i class="icon-off"></i> Motors off</button>
|
||||
<button class="btn control-box" data-bind="enable: isOperational(), click: function() { $root.sendCustomCommand({type:'command',command:'M106'}) }">Fans on</button>
|
||||
<button class="btn control-box" data-bind="enable: isOperational(), click: function() { $root.sendCustomCommand({type:'command',command:'M106 S0'}) }">Fans off</button>
|
||||
<button class="btn control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendCustomCommand({type:'command',command:'M18'}) }"><i class="icon-off"></i> Motors off</button>
|
||||
<button class="btn control-box" data-bind="enable: isOperational() && loginState.isUser(), click: function() { $root.sendCustomCommand({type:'command',command:'M106'}) }">Fans on</button>
|
||||
<button class="btn control-box" data-bind="enable: isOperational() && loginState.isUser(), click: function() { $root.sendCustomCommand({type:'command',command:'M106 S0'}) }">Fans off</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -341,7 +343,7 @@
|
|||
</script>
|
||||
<script type="text/html" id="customControls_commandTemplate">
|
||||
<form class="form-inline">
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational(), click: function() { $root.sendCustomCommand($data) }"></button>
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_parametricCommandTemplate">
|
||||
|
@ -350,7 +352,7 @@
|
|||
<label data-bind="text: name"></label>
|
||||
<input type="text" class="input-small" data-bind="attr: {placeholder: name}, value: value">
|
||||
<!-- /ko -->
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational(), click: function() { $root.sendCustomCommand($data) }"></button>
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_emptyTemplate"><div></div></script>
|
||||
|
@ -360,30 +362,30 @@
|
|||
<div class="form-horizontal" style="margin-bottom: 20px">
|
||||
<label for="speed_outerWall">Outer Wall</label>
|
||||
<div class="input-append">
|
||||
<input type="text" id="speed_outerWall" class="input-mini" data-bind="enable: isOperational(), attr: {placeholder: outerWall}">
|
||||
<input type="text" id="speed_outerWall" class="input-mini" data-bind="enable: isOperational() && loginState.isUser(), attr: {placeholder: outerWall}">
|
||||
<span class="add-on">%</span>
|
||||
<button type="submit" class="btn" id="speed_outerWall_set" data-bind="enable: isOperational()">Set</button>
|
||||
<button type="submit" class="btn" id="speed_outerWall_set" data-bind="enable: isOperational() && loginState.isUser()">Set</button>
|
||||
</div>
|
||||
|
||||
<label for="speed_innerWall">Inner Wall</label>
|
||||
<div class="input-append">
|
||||
<input type="text" id="speed_innerWall" class="input-mini" data-bind="enable: isOperational(), attr: {placeholder: innerWall}">
|
||||
<input type="text" id="speed_innerWall" class="input-mini" data-bind="enable: isOperational() && loginState.isUser(), attr: {placeholder: innerWall}">
|
||||
<span class="add-on">%</span>
|
||||
<button type="submit" class="btn" id="speed_innerWall_set" data-bind="enable: isOperational()">Set</button>
|
||||
<button type="submit" class="btn" id="speed_innerWall_set" data-bind="enable: isOperational() && loginState.isUser()">Set</button>
|
||||
</div>
|
||||
|
||||
<label for="speed_fill">Fill</label>
|
||||
<div class="input-append">
|
||||
<input type="text" id="speed_fill" class="input-mini" data-bind="enable: isOperational(), attr: {placeholder: fill}">
|
||||
<input type="text" id="speed_fill" class="input-mini" data-bind="enable: isOperational() && loginState.isUser(), attr: {placeholder: fill}">
|
||||
<span class="add-on">%</span>
|
||||
<button type="submit" class="btn" id="speed_fill_set" data-bind="enable: isOperational()">Set</button>
|
||||
<button type="submit" class="btn" id="speed_fill_set" data-bind="enable: isOperational() && loginState.isUser()">Set</button>
|
||||
</div>
|
||||
|
||||
<label for="speed_support">Support</label>
|
||||
<div class="input-append">
|
||||
<input type="text" id="speed_support" class="input-mini" data-bind="enable: isOperational(), attr: {placeholder: support}">
|
||||
<input type="text" id="speed_support" class="input-mini" data-bind="enable: isOperational() && loginState.isUser(), attr: {placeholder: support}">
|
||||
<span class="add-on">%</span>
|
||||
<button type="submit" class="btn" id="speed_support_set" data-bind="enable: isOperational()">Set</button>
|
||||
<button type="submit" class="btn" id="speed_support_set" data-bind="enable: isOperational() && loginState.isUser()">Set</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -482,7 +484,7 @@
|
|||
|
||||
<div class="input-append">
|
||||
<input type="text" id="terminal-command">
|
||||
<button class="btn" type="button" id="terminal-send">Send</button>
|
||||
<button class="btn" type="button" id="terminal-send" data-bind="enable: isOperational() && loginState.isUser()">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
{% if enableTimelapse %}
|
||||
|
@ -490,7 +492,7 @@
|
|||
<h1>Timelapse Configuration</h1>
|
||||
|
||||
<label for="webcam_timelapse_mode">Timelapse Mode</label>
|
||||
<select id="webcam_timelapse_mode" data-bind="value: timelapseType, enable: isOperational() && !isPrinting()">
|
||||
<select id="webcam_timelapse_mode" data-bind="value: timelapseType, enable: isOperational() && !isPrinting() && loginState.isUser()">
|
||||
<option value="off">Off</option>
|
||||
<option value="zchange">On Z Change</option>
|
||||
<option value="timed">Timed</option>
|
||||
|
@ -505,7 +507,7 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<button class="btn" data-bind="click: save, enable: isOperational() && !isPrinting()">Save Settings</button>
|
||||
<button class="btn" data-bind="click: save, enable: isOperational() && !isPrinting() && loginState.isUser()">Save Settings</button>
|
||||
</div>
|
||||
|
||||
<h1>Finished Timelapses</h1>
|
||||
|
|
|
@ -150,22 +150,34 @@ class UnknownRole(Exception):
|
|||
|
||||
class User(UserMixin):
|
||||
def __init__(self, username, passwordHash, active, roles):
|
||||
self.username = username
|
||||
self.passwordHash = passwordHash
|
||||
self.active = active
|
||||
self.roles = roles
|
||||
self._username = username
|
||||
self._passwordHash = passwordHash
|
||||
self._active = active
|
||||
self._roles = roles
|
||||
|
||||
def check_password(self, passwordHash):
|
||||
return self._passwordHash == passwordHash
|
||||
|
||||
def get_id(self):
|
||||
return self.username
|
||||
return self._username
|
||||
|
||||
def get_name(self):
|
||||
return self._username
|
||||
|
||||
def is_active(self):
|
||||
return self.active
|
||||
return self._active
|
||||
|
||||
def is_user(self):
|
||||
return "user" in self._roles
|
||||
|
||||
def is_admin(self):
|
||||
return "admin" in self._roles
|
||||
|
||||
##~~ DummyUser object to use when accessControl is disabled
|
||||
|
||||
class DummyUser(UserMixin):
|
||||
class DummyUser(User):
|
||||
def __init__(self):
|
||||
self.roles = UserManager.valid_roles
|
||||
User.__init__(self, "dummy", "", True, UserManager.valid_roles)
|
||||
|
||||
def get_id(self):
|
||||
return "dummy"
|
||||
def check_password(self, passwordHash):
|
||||
return True
|
Loading…
Reference in New Issue