User management now working
Also reorganized javascripts a bit (as preparation for some refactoring coming up) and renamed templates from ".html" to ".jinja2".master
parent
244ff25e2f
commit
19dc238f06
|
@ -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/<username>", 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/<username>", 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/<username>", 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/<username>/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():
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
// Register as an anonymous AMD module:
|
||||
define([
|
||||
'jquery',
|
||||
'jquery.ui.widget'
|
||||
'jquery.ui.widget.js'
|
||||
], factory);
|
||||
} else {
|
||||
// Browser globals:
|
|
@ -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
|
||||
|
||||
|
|
|
@ -17,13 +17,15 @@
|
|||
<link href="{{ url_for('static', filename='gcodeviewer/css/style.css') }}" rel="stylesheet" media="screen">
|
||||
|
||||
<script lang="javascript">
|
||||
var AJAX_BASEURL = "/ajax/";
|
||||
var AJAX_BASEURL = "{{ ajaxBaseUrl }}";
|
||||
|
||||
var CONFIG_GCODEFILESPERPAGE = 5;
|
||||
var CONFIG_TIMELAPSEFILESPERPAGE = 10;
|
||||
var CONFIG_USERSPERPAGE = 10;
|
||||
var CONFIG_WEBCAM_STREAM = "{{ webcamStream }}";
|
||||
var CONFIG_ACCESS_CONTROL = {% if enableAccessControl -%} true; {% else %} false; {%- endif %}
|
||||
|
||||
var WEB_SOCKET_SWF_LOCATION = "{{ url_for('static', filename='js/WebSocketMain.swf') }}";
|
||||
var WEB_SOCKET_SWF_LOCATION = "{{ url_for('static', filename='js/socket.io/WebSocketMain.swf') }}";
|
||||
var WEB_SOCKET_DEBUG = true;
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='js/less-1.3.3.min.js') }}" type="text/javascript"></script>
|
||||
|
@ -35,13 +37,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 class="hide" data-bind="css: {hide: !loginState.isAdmin()}">
|
||||
<li style="display: none;" data-bind="visible: 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 hide" data-bind="css: {hide: !loginState.isAdmin()}">
|
||||
<li class="dropdown" style="display: none" data-bind="visible: loginState.isAdmin">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-off"></i> System
|
||||
<b class="caret"></b>
|
||||
|
@ -68,6 +70,7 @@
|
|||
<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" data-bind="css: {hide: !loginState.loggedIn(), 'dropdown-menu': loginState.loggedIn()}">
|
||||
<li><a href="#" id="change_password_button" data-bind="click: function() { users.showChangePasswordDialog(loginState.currentUser()); }">Change Password</a></li>
|
||||
<li><a href="#" id="logout_button" data-bind="click: loginState.logout">Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -80,7 +83,7 @@
|
|||
<div class="container octoprint-container">
|
||||
<div class="row">
|
||||
<div class="accordion span4">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-group" data-bind="visible: loginState.isUser" id="connection_accordion">
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" data-toggle="collapse" href="#connection"><i class="icon-signal"></i> Connection</a>
|
||||
</div>
|
||||
|
@ -97,7 +100,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-group" id="state_accordion">
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" data-toggle="collapse" href="#state"><i class="icon-info-sign"></i> State</a>
|
||||
</div>
|
||||
|
@ -124,8 +127,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading" id="files-heading">
|
||||
<div class="accordion-group" id="files_accordion">
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" data-toggle="collapse" href="#files"><i class="icon-list"></i> Files</a>
|
||||
|
||||
<div class="settings-trigger btn-group">
|
||||
|
@ -156,7 +159,7 @@
|
|||
<td class="gcode_files_name" data-bind="text: name"></td>
|
||||
<td class="gcode_files_size" data-bind="text: size"></td>
|
||||
<td class="gcode_files_action">
|
||||
<a href="#" class="icon-trash" title="Remove" data-bind="click: function() { $root.removeFile($data.name); }"></a> | <a href="#" class="icon-folder-open" title="Load" data-bind="click: function() { if ($root.isLoadActionPossible()) { $root.loadFile($data.name, false); } else { return; } }, css: {disabled: !$root.isLoadActionPossible()}"></a> | <a href="#" class="icon-print" title="Load and Print" data-bind="click: function() { if ($root.isLoadAndPrintActionPossible()) { $root.loadFile($data.name, true); } else { return; } }, css: {disabled: !$root.isLoadAndPrintActionPossible()}"></a>
|
||||
<a href="#" class="icon-trash" title="Remove" data-bind="click: function() { if ($root.loginState.isUser()) { $root.removeFile($data.name); } else { return; } }, css: {disabled: !$root.loginState.isUser()}"></a> | <a href="#" class="icon-folder-open" title="Load" data-bind="click: function() { if ($root.isLoadActionPossible()) { $root.loadFile($data.name, false); } else { return; } }, css: {disabled: !$root.isLoadActionPossible()}"></a> | <a href="#" class="icon-print" title="Load and Print" data-bind="click: function() { if ($root.isLoadAndPrintActionPossible()) { $root.loadFile($data.name, true); } else { return; } }, css: {disabled: !$root.isLoadAndPrintActionPossible()}"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -172,16 +175,18 @@
|
|||
<li data-bind="css: {disabled: listHelper.currentPage() === listHelper.lastPage()}"><a href="#" data-bind="click: listHelper.nextPage">»</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<span class="btn btn-primary btn-block fileinput-button" style="margin-bottom: 10px">
|
||||
<i class="icon-upload icon-white"></i>
|
||||
<span>Upload</span>
|
||||
<input id="gcode_upload" type="file" name="gcode_file" class="fileinput-button" data-url="/ajax/gcodefiles/upload">
|
||||
</span>
|
||||
<div id="gcode_upload_progress" class="progress" style="width: 100%;">
|
||||
<div class="bar" style="width: 0%"></div>
|
||||
</div>
|
||||
<div>
|
||||
<small>Hint: You can also drag and drop files on this page to upload them.</small>
|
||||
<div style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<span class="btn btn-primary btn-block fileinput-button" data-bind="css: {disabled: !$root.loginState.isUser()}" style="margin-bottom: 10px">
|
||||
<i class="icon-upload icon-white"></i>
|
||||
<span>Upload</span>
|
||||
<input id="gcode_upload" type="file" name="gcode_file" class="fileinput-button" data-url="/ajax/gcodefiles/upload" data-bind="enable: loginState.isUser()">
|
||||
</span>
|
||||
<div id="gcode_upload_progress" class="progress" style="width: 100%;">
|
||||
<div class="bar" style="width: 0%"></div>
|
||||
</div>
|
||||
<div>
|
||||
<small>Hint: You can also drag and drop files on this page to upload them.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -193,7 +198,6 @@
|
|||
<li class="active"><a href="#temp" data-toggle="tab">Temperature</a></li>
|
||||
<li><a href="#control" data-toggle="tab">Control</a></li>
|
||||
{% if enableGCodeVisualizer %}<li><a href="#gcode" data-toggle="tab">GCode Viewer</a></li>{% endif %}
|
||||
<!--<li><a href="#speed" data-toggle="tab">Speed</a></li>-->
|
||||
<li><a href="#term" data-toggle="tab">Terminal</a></li>
|
||||
{% if enableTimelapse %}<li><a href="#timelapse" data-toggle="tab">Timelapse</a></li>{% endif %}
|
||||
</ul>
|
||||
|
@ -211,27 +215,29 @@
|
|||
|
||||
<label>Target: <strong data-bind="html: targetTempString"></strong></label>
|
||||
|
||||
<label for="temp_newTemp">New Target</label>
|
||||
<div class="input-append">
|
||||
<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" 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">
|
||||
<!-- ko foreach: temperature_profiles -->
|
||||
<li>
|
||||
<a href="#" data-bind="click: $parent.setTempFromProfile, text: 'Set ' + name + ' (' + extruder + '°C)'"></a>
|
||||
</li>
|
||||
<!-- /ko -->
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="#" data-bind="click: function() { $root.setTemp(0); }">Off</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<label for="temp_newTemp">New Target</label>
|
||||
<div class="input-append">
|
||||
<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" 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">
|
||||
<!-- ko foreach: temperature_profiles -->
|
||||
<li>
|
||||
<a href="#" data-bind="click: $parent.setTempFromProfile, text: 'Set ' + name + ' (' + extruder + '°C)'"></a>
|
||||
</li>
|
||||
<!-- /ko -->
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="#" data-bind="click: function() { $root.setTemp(0); }">Off</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-horizontal span6">
|
||||
|
@ -241,27 +247,29 @@
|
|||
|
||||
<label>Target: <strong data-bind="html: bedTargetTempString"></strong></label>
|
||||
|
||||
<label for="temp_newBedTemp">New Target</label>
|
||||
<div class="input-append">
|
||||
<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" 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">
|
||||
<!-- ko foreach: temperature_profiles -->
|
||||
<li>
|
||||
<a href="#" data-bind="click: $parent.setBedTempFromProfile, text: 'Set ' + name + ' (' + bed + '°C)'"></a>
|
||||
</li>
|
||||
<!-- /ko -->
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="#" data-bind="click: function(){ $root.setBedTemp(0); }">Off</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<label for="temp_newBedTemp">New Target</label>
|
||||
<div class="input-append">
|
||||
<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" 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">
|
||||
<!-- ko foreach: temperature_profiles -->
|
||||
<li>
|
||||
<a href="#" data-bind="click: $parent.setBedTempFromProfile, text: 'Set ' + name + ' (' + bed + '°C)'"></a>
|
||||
</li>
|
||||
<!-- /ko -->
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="#" data-bind="click: function(){ $root.setBedTemp(0); }">Off</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -273,7 +281,7 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="jog-panel">
|
||||
<div class="jog-panel" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<!-- XY jogging control panel -->
|
||||
<div class="jog-panel">
|
||||
<h1>X/Y</h1>
|
||||
|
@ -313,7 +321,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- Extrusion control panel -->
|
||||
<div class="jog-panel">
|
||||
<div class="jog-panel" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<h1>E</h1>
|
||||
<div>
|
||||
<div class="input-append control-box">
|
||||
|
@ -325,7 +333,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- General control panel -->
|
||||
<div class="jog-panel">
|
||||
<div class="jog-panel" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<h1>General</h1>
|
||||
<div>
|
||||
<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>
|
||||
|
@ -335,7 +343,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Container for custom controls -->
|
||||
<div style="clear: both;" data-bind="template: { name: $root.displayMode, foreach: controls }"></div>
|
||||
<div style="clear: both; display: none;" data-bind="visible: loginState.isUser, template: { name: $root.displayMode, foreach: controls }"></div>
|
||||
|
||||
<!-- Templates for custom controls -->
|
||||
<script type="text/html" id="customControls_sectionTemplate">
|
||||
|
@ -360,37 +368,6 @@
|
|||
<script type="text/html" id="customControls_emptyTemplate"><div></div></script>
|
||||
<!-- End of templates for custom controls -->
|
||||
</div>
|
||||
<div class="tab-pane" id="speed">
|
||||
<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() && loginState.isUser(), attr: {placeholder: outerWall}">
|
||||
<span class="add-on">%</span>
|
||||
<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() && loginState.isUser(), attr: {placeholder: innerWall}">
|
||||
<span class="add-on">%</span>
|
||||
<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() && loginState.isUser(), attr: {placeholder: fill}">
|
||||
<span class="add-on">%</span>
|
||||
<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() && loginState.isUser(), attr: {placeholder: support}">
|
||||
<span class="add-on">%</span>
|
||||
<button type="submit" class="btn" id="speed_support_set" data-bind="enable: isOperational() && loginState.isUser()">Set</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="gcode">
|
||||
<canvas id="canvas" width="572" height="588"></canvas>
|
||||
<div id="slider-vertical"></div>
|
||||
|
@ -484,32 +461,34 @@
|
|||
<input type="checkbox" id="terminal-autoscroll" data-bind="checked: autoscrollEnabled"> Autoscroll
|
||||
</label>
|
||||
|
||||
<div class="input-append">
|
||||
<input type="text" id="terminal-command">
|
||||
<div class="input-append" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<input type="text" id="terminal-command" data-bind="enable: isOperational() && loginState.isUser()">
|
||||
<button class="btn" type="button" id="terminal-send" data-bind="enable: isOperational() && loginState.isUser()">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
{% if enableTimelapse %}
|
||||
<div class="tab-pane" id="timelapse">
|
||||
<h1>Timelapse Configuration</h1>
|
||||
<div style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<h1>Timelapse Configuration</h1>
|
||||
|
||||
<label for="webcam_timelapse_mode">Timelapse Mode</label>
|
||||
<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>
|
||||
</select>
|
||||
<label for="webcam_timelapse_mode">Timelapse Mode</label>
|
||||
<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>
|
||||
</select>
|
||||
|
||||
<div id="webcam_timelapse_timedsettings" data-bind="visible: intervalInputEnabled()">
|
||||
<label for="webcam_timelapse_interval">Interval</label>
|
||||
<div class="input-append">
|
||||
<input type="text" class="input-mini" id="webcam_timelapse_interval" data-bind="value: timelapseTimedInterval, enable: isOperational() && !isPrinting()">
|
||||
<span class="add-on">sec</span>
|
||||
<div id="webcam_timelapse_timedsettings" data-bind="visible: intervalInputEnabled()">
|
||||
<label for="webcam_timelapse_interval">Interval</label>
|
||||
<div class="input-append">
|
||||
<input type="text" class="input-mini" id="webcam_timelapse_interval" data-bind="value: timelapseTimedInterval, enable: isOperational() && !isPrinting() && loginState.isUser()">
|
||||
<span class="add-on">sec</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button class="btn" data-bind="click: save, enable: isOperational() && !isPrinting() && loginState.isUser()">Save Settings</button>
|
||||
<div>
|
||||
<button class="btn" data-bind="click: save, enable: isOperational() && !isPrinting() && loginState.isUser()">Save Settings</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Finished Timelapses</h1>
|
||||
|
@ -529,7 +508,7 @@
|
|||
<tr data-bind="attr: {title: name}">
|
||||
<td class="timelapse_files_name" data-bind="text: name"></td>
|
||||
<td class="timelapse_files_size" data-bind="text: size"></td>
|
||||
<td class="timelapse_files_action"><a href="#" class="icon-trash" data-bind="click: $parent.removeFile"></a> | <a href="#" class="icon-download" data-bind="attr: {href: url}"></a></td>
|
||||
<td class="timelapse_files_action"><a href="#" class="icon-trash" data-bind="click: function() { if ($root.loginState.isUser()) { $parent.removeFile(); } else { return; } }, css: {disabled: !$root.loginState.isUser()}"></a> | <a href="#" class="icon-download" data-bind="attr: {href: url}"></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -559,26 +538,27 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'settings.html' %}
|
||||
{% include 'dialogs.html' %}
|
||||
{% include 'settings.jinja2' %}
|
||||
{% include 'dialogs.jinja2' %}
|
||||
|
||||
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/modernizr.custom.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/underscore-min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/knockout-2.2.1.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap-modalmanager.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap-modal.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.core.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.widget.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.mouse.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.slider.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.pnotify.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.flot.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.iframe-transport.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.fileupload.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/socket.io.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/underscore.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/knockout.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap/bootstrap.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap/bootstrap-modalmanager.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap/bootstrap-modal.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery/jquery.ui.core.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery/jquery.ui.widget.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery/jquery.ui.mouse.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery/jquery.ui.slider.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery/jquery.pnotify.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery/jquery.flot.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery/jquery.iframe-transport.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery/jquery.fileupload.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/socket.io/socket.io.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/ui.js') }}"></script>
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/ui.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/gCodeReader.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/renderer.js') }}"></script>
|
|
@ -12,7 +12,7 @@
|
|||
<li><a href="#settings_folder" data-toggle="tab">Folder</a></li>
|
||||
<li><a href="#settings_temperature" data-toggle="tab">Temperature</a></li>
|
||||
<li><a href="#settings_appearance" data-toggle="tab">Appearance</a></li>
|
||||
<li><a href="#settings_users" data-toggle="tab">Users</a></li>
|
||||
{% if enableAccessControl %}<li><a href="#settings_users" data-toggle="tab">Users</a></li>{% endif %}
|
||||
</ul>
|
||||
|
||||
<div class="tab-content span8">
|
||||
|
@ -185,6 +185,8 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if enableAccessControl %}
|
||||
<div class="tab-pane" id="settings_users">
|
||||
<table class="table table-condensed table-hover" id="system_users">
|
||||
<thead>
|
||||
|
@ -327,6 +329,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -65,6 +65,8 @@ class FilebasedUserManager(UserManager):
|
|||
for name in data.keys():
|
||||
attributes = data[name]
|
||||
self._users[name] = User(name, attributes["password"], attributes["active"], attributes["roles"])
|
||||
else:
|
||||
self._users["admin"] = User("admin", "7557160613d5258f883014a7c3c0428de53040fc152b1791f1cc04a62b428c0c2a9c46ed330cdce9689353ab7a5352ba2b2ceb459b96e9c8ed7d0cb0b2c0c076", True, ["user", "admin"])
|
||||
|
||||
def _save(self, force=False):
|
||||
if not self._dirty and not force:
|
||||
|
@ -193,7 +195,8 @@ class User(UserMixin):
|
|||
return {
|
||||
"name": self._username,
|
||||
"active": self.is_active(),
|
||||
"admin": self.is_admin()
|
||||
"admin": self.is_admin(),
|
||||
"user": self.is_user()
|
||||
}
|
||||
|
||||
def check_password(self, passwordHash):
|
||||
|
|
Loading…
Reference in New Issue