Merge pull request #90 from Gallore/f_swupdate_bitbucket
F swupdate bitbucket
This commit is contained in:
commit
2df4da2af0
25 changed files with 355 additions and 209 deletions
|
|
@ -74,6 +74,7 @@ date of first contribution):
|
||||||
* [Mathias Rangel Wulff](https://github.com/mathiasrw)
|
* [Mathias Rangel Wulff](https://github.com/mathiasrw)
|
||||||
* [Clemens Niemeyer](https://github.com/clemniem)
|
* [Clemens Niemeyer](https://github.com/clemniem)
|
||||||
* ["I-am-me"](https://github.com/I-am-me)
|
* ["I-am-me"](https://github.com/I-am-me)
|
||||||
|
* [J-J Heinonen](https://github.com/jammi)
|
||||||
* [Noah Martin](https://github.com/noahsmartin)
|
* [Noah Martin](https://github.com/noahsmartin)
|
||||||
* [Eyal Soha](https://github.com/eyal0)
|
* [Eyal Soha](https://github.com/eyal0)
|
||||||
* [Greg Hulands](https://github.com/ghulands)
|
* [Greg Hulands](https://github.com/ghulands)
|
||||||
|
|
|
||||||
37
setup.py
37
setup.py
|
|
@ -14,34 +14,31 @@ import octoprint_setuptools
|
||||||
|
|
||||||
# Requirements for our application
|
# Requirements for our application
|
||||||
INSTALL_REQUIRES = [
|
INSTALL_REQUIRES = [
|
||||||
"flask>=0.9,<0.11",
|
"flask>=0.12,<0.13",
|
||||||
|
"werkzeug>=0.11.1,<0.12",
|
||||||
|
"tornado>=4.4.2,<4.5",
|
||||||
"Jinja2>=2.8,<2.9", # Jinja 2.9 has breaking changes WRT template scope - we can't
|
"Jinja2>=2.8,<2.9", # Jinja 2.9 has breaking changes WRT template scope - we can't
|
||||||
# guarantee backwards compatibility for plugins and such with that
|
# guarantee backwards compatibility for plugins and such with that
|
||||||
# version, hence we need to pin to a lower version for now. See #1697
|
# version, hence we need to pin to a lower version for now. See #1697
|
||||||
"werkzeug>=0.8.3,<0.9",
|
|
||||||
"tornado==4.0.2", # pinned for now, we need to migrate to a newer tornado, but due
|
|
||||||
# to some voodoo needed to get large streamed uploads and downloads
|
|
||||||
# to work that is probably not completely straightforward and therefore
|
|
||||||
# something for post-1.3.0-stable release
|
|
||||||
"sockjs-tornado>=1.0.2,<1.1",
|
"sockjs-tornado>=1.0.2,<1.1",
|
||||||
"PyYAML>=3.10,<3.11",
|
"PyYAML>=3.12,<3.13",
|
||||||
"Flask-Login>=0.2.2,<0.3",
|
"Flask-Login>=0.4,<0.5",
|
||||||
"Flask-Principal>=0.3.5,<0.4",
|
"Flask-Principal>=0.4,<0.5",
|
||||||
"Flask-Babel>=0.9,<0.10",
|
"Flask-Babel>=0.11,<0.12",
|
||||||
"Flask-Assets>=0.10,<0.11",
|
"Flask-Assets>=0.12,<0.13",
|
||||||
"markdown>=2.6.4,<2.7",
|
"markdown>=2.6.4,<2.7",
|
||||||
"pyserial>=2.7,<2.8",
|
"pyserial>=3.2.1,<3.3",
|
||||||
"netaddr>=0.7.17,<0.8",
|
"netaddr>=0.7.17,<0.8",
|
||||||
"watchdog>=0.8.3,<0.9",
|
"watchdog>=0.8.3,<0.9",
|
||||||
"sarge>=0.1.4,<0.2",
|
"sarge>=0.1.4,<0.2",
|
||||||
"netifaces>=0.10,<0.11",
|
"netifaces>=0.10,<0.11",
|
||||||
"pylru>=1.0.9,<1.1",
|
"pylru>=1.0.9,<1.1",
|
||||||
"rsa>=3.2,<3.3",
|
"rsa>=3.4,<3.5",
|
||||||
"pkginfo>=1.2.1,<1.3",
|
"pkginfo>=1.4.1,<1.5",
|
||||||
"requests>=2.7,<2.8",
|
"requests>=2.13.0,<2.14",
|
||||||
"semantic_version>=2.4.2,<2.5",
|
"semantic_version>=2.6.0,<2.7",
|
||||||
"psutil>=3.2.1,<3.3",
|
"psutil>=5.1.3,<5.2",
|
||||||
"Click>=6.2,<6.3",
|
"Click>=6.7,<6.8",
|
||||||
"awesome-slugify>=1.6.5,<1.7",
|
"awesome-slugify>=1.6.5,<1.7",
|
||||||
"feedparser>=5.2.1,<5.3",
|
"feedparser>=5.2.1,<5.3",
|
||||||
"chainmap>=1.0.2,<1.1",
|
"chainmap>=1.0.2,<1.1",
|
||||||
|
|
@ -58,7 +55,7 @@ EXTRA_REQUIRES = dict(
|
||||||
# Dependencies for developing OctoPrint
|
# Dependencies for developing OctoPrint
|
||||||
develop=[
|
develop=[
|
||||||
# Testing dependencies
|
# Testing dependencies
|
||||||
"mock>=1.0.1,<1.1",
|
"mock>=2.0,<2.1",
|
||||||
"nose>=1.3.0,<1.4",
|
"nose>=1.3.0,<1.4",
|
||||||
"ddt",
|
"ddt",
|
||||||
|
|
||||||
|
|
@ -73,7 +70,7 @@ EXTRA_REQUIRES = dict(
|
||||||
|
|
||||||
# Dependencies for developing OctoPrint plugins
|
# Dependencies for developing OctoPrint plugins
|
||||||
plugins=[
|
plugins=[
|
||||||
"cookiecutter>=1.4,<1.5"
|
"cookiecutter>=1.5,<1.6"
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1409,7 +1409,7 @@ class SettingsPlugin(OctoPrintPlugin):
|
||||||
|
|
||||||
:return: the current settings of the plugin, as a dictionary
|
:return: the current settings of the plugin, as a dictionary
|
||||||
"""
|
"""
|
||||||
from flask.ext.login import current_user
|
from flask_login import current_user
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
data = copy.deepcopy(self._settings.get_all_data(merged=True))
|
data = copy.deepcopy(self._settings.get_all_data(merged=True))
|
||||||
|
|
@ -1454,8 +1454,8 @@ class SettingsPlugin(OctoPrintPlugin):
|
||||||
else:
|
else:
|
||||||
node[key] = None
|
node[key] = None
|
||||||
|
|
||||||
conditions = dict(user=lambda: current_user is not None and not current_user.is_anonymous(),
|
conditions = dict(user=lambda: current_user is not None and not current_user.is_anonymous,
|
||||||
admin=lambda: current_user is not None and not current_user.is_anonymous() and current_user.is_admin(),
|
admin=lambda: current_user is not None and not current_user.is_anonymous and current_user.is_admin,
|
||||||
never=lambda: False)
|
never=lambda: False)
|
||||||
|
|
||||||
for level, condition in conditions.items():
|
for level, condition in conditions.items():
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import flask
|
||||||
|
|
||||||
from octoprint.server import admin_permission
|
from octoprint.server import admin_permission
|
||||||
from octoprint.server.util.flask import restricted_access, with_revalidation_checking, check_etag
|
from octoprint.server.util.flask import restricted_access, with_revalidation_checking, check_etag
|
||||||
from flask.ext.babel import gettext
|
from flask_babel import gettext
|
||||||
|
|
||||||
class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
|
class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
|
||||||
octoprint.plugin.SettingsPlugin,
|
octoprint.plugin.SettingsPlugin,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ __copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms
|
||||||
import octoprint.plugin
|
import octoprint.plugin
|
||||||
|
|
||||||
|
|
||||||
from flask.ext.babel import gettext
|
from flask_babel import gettext
|
||||||
|
|
||||||
|
|
||||||
class CoreWizardPlugin(octoprint.plugin.AssetPlugin,
|
class CoreWizardPlugin(octoprint.plugin.AssetPlugin,
|
||||||
|
|
@ -87,9 +87,9 @@ class CoreWizardPlugin(octoprint.plugin.AssetPlugin,
|
||||||
from flask import request
|
from flask import request
|
||||||
from octoprint.server.api import valid_boolean_trues, NO_CONTENT
|
from octoprint.server.api import valid_boolean_trues, NO_CONTENT
|
||||||
|
|
||||||
data = request.values
|
data = request.get_json()
|
||||||
if hasattr(request, "json") and request.json:
|
if data is None:
|
||||||
data = request.json
|
data = request.values
|
||||||
|
|
||||||
if "ac" in data and data["ac"] in valid_boolean_trues and \
|
if "ac" in data and data["ac"] in valid_boolean_trues and \
|
||||||
"user" in data.keys() and "pass1" in data.keys() and \
|
"user" in data.keys() and "pass1" in data.keys() and \
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ from octoprint.server import admin_permission, VERSION
|
||||||
from octoprint.util.pip import LocalPipCaller, UnknownPip
|
from octoprint.util.pip import LocalPipCaller, UnknownPip
|
||||||
|
|
||||||
from flask import jsonify, make_response
|
from flask import jsonify, make_response
|
||||||
from flask.ext.babel import gettext
|
from flask_babel import gettext
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sarge
|
import sarge
|
||||||
|
|
|
||||||
|
|
@ -501,7 +501,9 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
|
||||||
if not "application/json" in flask.request.headers["Content-Type"]:
|
if not "application/json" in flask.request.headers["Content-Type"]:
|
||||||
return flask.make_response("Expected content-type JSON", 400)
|
return flask.make_response("Expected content-type JSON", 400)
|
||||||
|
|
||||||
json_data = flask.request.json
|
json_data = flask.request.get_json(silent=True)
|
||||||
|
if json_data is None:
|
||||||
|
return flask.make_response("Invalid JSON", 400)
|
||||||
|
|
||||||
if "check" in json_data:
|
if "check" in json_data:
|
||||||
check_targets = map(lambda x: x.strip(), json_data["check"])
|
check_targets = map(lambda x: x.strip(), json_data["check"])
|
||||||
|
|
@ -528,7 +530,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
|
||||||
##~~ TemplatePlugin API
|
##~~ TemplatePlugin API
|
||||||
|
|
||||||
def get_template_configs(self):
|
def get_template_configs(self):
|
||||||
from flask.ext.babel import gettext
|
from flask_babel import gettext
|
||||||
return [
|
return [
|
||||||
dict(type="settings", name=gettext("Software Update"))
|
dict(type="settings", name=gettext("Software Update"))
|
||||||
]
|
]
|
||||||
|
|
@ -861,7 +863,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
|
||||||
result = dict(check)
|
result = dict(check)
|
||||||
|
|
||||||
if target == "octoprint":
|
if target == "octoprint":
|
||||||
from flask.ext.babel import gettext
|
from flask_babel import gettext
|
||||||
|
|
||||||
result["displayName"] = to_unicode(check.get("displayName"), errors="replace")
|
result["displayName"] = to_unicode(check.get("displayName"), errors="replace")
|
||||||
if result["displayName"] is None:
|
if result["displayName"] is None:
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import logging
|
||||||
|
|
||||||
from ..exceptions import ConfigurationInvalid
|
from ..exceptions import ConfigurationInvalid
|
||||||
|
|
||||||
BRANCH_HEAD_URL = "https://api.bitbucket.org/2.0/repositories/{user}/{repo}/commits/{branch}?pagelen=1"
|
BRANCH_HEAD_URL = "https://api.bitbucket.org/2.0/repositories/{user}/{repo}/commit/{branch}"
|
||||||
|
|
||||||
logger = logging.getLogger("octoprint.plugins.softwareupdate.version_checks.bitbucket_commit")
|
logger = logging.getLogger("octoprint.plugins.softwareupdate.version_checks.bitbucket_commit")
|
||||||
|
|
||||||
|
|
@ -22,12 +22,11 @@ def _get_latest_commit(user, repo, branch):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
reference = r.json()
|
reference = r.json()
|
||||||
if not "values" in reference \
|
|
||||||
or len(reference["values"]) < 1 \
|
if not "hash" in reference:
|
||||||
or not 'hash' in reference["values"][0]:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return reference["values"][0]['hash']
|
return reference["hash"]
|
||||||
|
|
||||||
|
|
||||||
def get_latest(target, check):
|
def get_latest(target, check):
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
|
||||||
import uuid
|
import uuid
|
||||||
from sockjs.tornado import SockJSRouter
|
from sockjs.tornado import SockJSRouter
|
||||||
from flask import Flask, g, request, session, Blueprint, Request, Response
|
from flask import Flask, g, request, session, Blueprint, Request, Response
|
||||||
from flask.ext.login import LoginManager, current_user
|
from flask_login import LoginManager, current_user
|
||||||
from flask.ext.principal import Principal, Permission, RoleNeed, identity_loaded, UserNeed
|
from flask_principal import Principal, Permission, RoleNeed, identity_loaded, UserNeed
|
||||||
from flask.ext.babel import Babel, gettext, ngettext
|
from flask_babel import Babel, gettext, ngettext
|
||||||
from flask.ext.assets import Environment, Bundle
|
from flask_assets import Environment, Bundle
|
||||||
from babel import Locale
|
from babel import Locale
|
||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
from watchdog.observers.polling import PollingObserver
|
from watchdog.observers.polling import PollingObserver
|
||||||
|
|
@ -89,9 +89,9 @@ def on_identity_loaded(sender, identity):
|
||||||
return
|
return
|
||||||
|
|
||||||
identity.provides.add(UserNeed(user.get_id()))
|
identity.provides.add(UserNeed(user.get_id()))
|
||||||
if user.is_user():
|
if user.is_user:
|
||||||
identity.provides.add(RoleNeed("user"))
|
identity.provides.add(RoleNeed("user"))
|
||||||
if user.is_admin():
|
if user.is_admin:
|
||||||
identity.provides.add(RoleNeed("admin"))
|
identity.provides.add(RoleNeed("admin"))
|
||||||
|
|
||||||
def load_user(id):
|
def load_user(id):
|
||||||
|
|
@ -170,8 +170,7 @@ class Server(object):
|
||||||
self._logger = logging.getLogger(__name__)
|
self._logger = logging.getLogger(__name__)
|
||||||
pluginManager = self._plugin_manager
|
pluginManager = self._plugin_manager
|
||||||
|
|
||||||
# monkey patch a bunch of stuff
|
# monkey patch some stuff
|
||||||
util.tornado.fix_ioloop_scheduling()
|
|
||||||
util.flask.enable_additional_translations(additional_folders=[self._settings.getBaseFolder("translations")])
|
util.flask.enable_additional_translations(additional_folders=[self._settings.getBaseFolder("translations")])
|
||||||
|
|
||||||
# setup app
|
# setup app
|
||||||
|
|
@ -337,6 +336,7 @@ class Server(object):
|
||||||
loginManager = LoginManager()
|
loginManager = LoginManager()
|
||||||
loginManager.session_protection = "strong"
|
loginManager.session_protection = "strong"
|
||||||
loginManager.user_callback = load_user
|
loginManager.user_callback = load_user
|
||||||
|
loginManager.anonymous_user = users.AnonymousUser # TODO: remove in 1.5.0
|
||||||
if not userManager.enabled:
|
if not userManager.enabled:
|
||||||
loginManager.anonymous_user = users.DummyUser
|
loginManager.anonymous_user = users.DummyUser
|
||||||
principals.identity_loaders.appendleft(users.dummy_identity_loader)
|
principals.identity_loaders.appendleft(users.dummy_identity_loader)
|
||||||
|
|
@ -610,11 +610,14 @@ class Server(object):
|
||||||
return Locale.parse(request.accept_languages.best_match(LANGUAGES))
|
return Locale.parse(request.accept_languages.best_match(LANGUAGES))
|
||||||
|
|
||||||
def _setup_app(self, app):
|
def _setup_app(self, app):
|
||||||
from octoprint.server.util.flask import ReverseProxiedEnvironment, OctoPrintFlaskRequest, OctoPrintFlaskResponse
|
from octoprint.server.util.flask import ReverseProxiedEnvironment, OctoPrintFlaskRequest, OctoPrintFlaskResponse, deprecate_flaskext
|
||||||
|
|
||||||
|
deprecate_flaskext() # TODO: remove in OctoPrint 1.5.0
|
||||||
|
|
||||||
s = settings()
|
s = settings()
|
||||||
|
|
||||||
app.debug = self._debug
|
app.debug = self._debug
|
||||||
|
app.config["TEMPLATES_AUTO_RELOAD"] = True
|
||||||
|
|
||||||
secret_key = s.get(["server", "secretKey"])
|
secret_key = s.get(["server", "secretKey"])
|
||||||
if not secret_key:
|
if not secret_key:
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ import netaddr
|
||||||
import sarge
|
import sarge
|
||||||
|
|
||||||
from flask import Blueprint, request, jsonify, abort, current_app, session, make_response, g
|
from flask import Blueprint, request, jsonify, abort, current_app, session, make_response, g
|
||||||
from flask.ext.login import login_user, logout_user, current_user
|
from flask_login import login_user, logout_user, current_user
|
||||||
from flask.ext.principal import Identity, identity_changed, AnonymousIdentity
|
from flask_principal import Identity, identity_changed, AnonymousIdentity
|
||||||
|
|
||||||
import octoprint.util as util
|
import octoprint.util as util
|
||||||
import octoprint.users
|
import octoprint.users
|
||||||
|
|
@ -62,7 +62,7 @@ def pluginData(name):
|
||||||
return make_response("More than one api provider registered for {name}, can't proceed".format(name=name), 500)
|
return make_response("More than one api provider registered for {name}, can't proceed".format(name=name), 500)
|
||||||
|
|
||||||
api_plugin = api_plugins[0]
|
api_plugin = api_plugins[0]
|
||||||
if api_plugin.is_api_adminonly() and not current_user.is_admin():
|
if api_plugin.is_api_adminonly() and not current_user.is_admin:
|
||||||
return make_response("Forbidden", 403)
|
return make_response("Forbidden", 403)
|
||||||
|
|
||||||
response = api_plugin.on_api_get(request)
|
response = api_plugin.on_api_get(request)
|
||||||
|
|
@ -89,7 +89,7 @@ def pluginCommand(name):
|
||||||
if valid_commands is None:
|
if valid_commands is None:
|
||||||
return make_response("Method not allowed", 405)
|
return make_response("Method not allowed", 405)
|
||||||
|
|
||||||
if api_plugin.is_api_adminonly() and not current_user.is_admin():
|
if api_plugin.is_api_adminonly() and not current_user.is_admin:
|
||||||
return make_response("Forbidden", 403)
|
return make_response("Forbidden", 403)
|
||||||
|
|
||||||
command, data, response = get_json_command_from_request(request, valid_commands)
|
command, data, response = get_json_command_from_request(request, valid_commands)
|
||||||
|
|
@ -134,10 +134,13 @@ def wizardFinish():
|
||||||
|
|
||||||
data = dict()
|
data = dict()
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.get_json()
|
||||||
except:
|
except:
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
abort(400)
|
||||||
|
|
||||||
if not "handled" in data:
|
if not "handled" in data:
|
||||||
abort(400)
|
abort(400)
|
||||||
handled = data["handled"]
|
handled = data["handled"]
|
||||||
|
|
@ -186,9 +189,9 @@ def apiVersion():
|
||||||
|
|
||||||
@api.route("/login", methods=["POST"])
|
@api.route("/login", methods=["POST"])
|
||||||
def login():
|
def login():
|
||||||
data = request.values
|
data = request.get_json()
|
||||||
if hasattr(request, "json") and request.json:
|
if data is None:
|
||||||
data = request.json
|
data = request.values
|
||||||
|
|
||||||
if octoprint.server.userManager.enabled and "user" in data and "pass" in data:
|
if octoprint.server.userManager.enabled and "user" in data and "pass" in data:
|
||||||
username = data["user"]
|
username = data["user"]
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ from octoprint.server.util.flask import restricted_access
|
||||||
|
|
||||||
from octoprint.plugin import plugin_manager
|
from octoprint.plugin import plugin_manager
|
||||||
|
|
||||||
from flask.ext.babel import Locale
|
from flask_babel import Locale
|
||||||
|
|
||||||
@api.route("/languages", methods=["GET"])
|
@api.route("/languages", methods=["GET"])
|
||||||
@restricted_access
|
@restricted_access
|
||||||
|
|
|
||||||
|
|
@ -326,10 +326,13 @@ def printerCommand():
|
||||||
return make_response("Expected content type JSON", 400)
|
return make_response("Expected content type JSON", 400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.get_json()
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return make_response("Malformed JSON body in request", 400)
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
if "command" in data and "commands" in data:
|
if "command" in data and "commands" in data:
|
||||||
return make_response("'command' and 'commands' are mutually exclusive", 400)
|
return make_response("'command' and 'commands' are mutually exclusive", 400)
|
||||||
elif ("command" in data or "commands" in data) and "script" in data:
|
elif ("command" in data or "commands" in data) and "script" in data:
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,13 @@ def printerProfilesAdd():
|
||||||
return make_response("Expected content-type JSON", 400)
|
return make_response("Expected content-type JSON", 400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
json_data = request.json
|
json_data = request.get_json()
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return make_response("Malformed JSON body in request", 400)
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
|
if json_data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
if not "profile" in json_data:
|
if not "profile" in json_data:
|
||||||
return make_response("No profile included in request", 400)
|
return make_response("No profile included in request", 400)
|
||||||
|
|
||||||
|
|
@ -117,10 +120,13 @@ def printerProfilesUpdate(identifier):
|
||||||
return make_response("Expected content-type JSON", 400)
|
return make_response("Expected content-type JSON", 400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
json_data = request.json
|
json_data = request.get_json()
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return make_response("Malformed JSON body in request", 400)
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
|
if json_data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
if not "profile" in json_data:
|
if not "profile" in json_data:
|
||||||
return make_response("No profile included in request", 400)
|
return make_response("No profile included in request", 400)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from flask import request, jsonify, make_response
|
from flask import request, jsonify, make_response
|
||||||
from flask.ext.login import current_user
|
from flask_login import current_user
|
||||||
from werkzeug.exceptions import BadRequest
|
from werkzeug.exceptions import BadRequest
|
||||||
|
|
||||||
from octoprint.events import eventManager, Events
|
from octoprint.events import eventManager, Events
|
||||||
|
|
@ -39,7 +39,7 @@ def _etag(lm=None):
|
||||||
for key in sorted(plugin_settings.keys()):
|
for key in sorted(plugin_settings.keys()):
|
||||||
sorted_plugin_settings[key] = plugin_settings.get(key, dict())
|
sorted_plugin_settings[key] = plugin_settings.get(key, dict())
|
||||||
|
|
||||||
if current_user is not None and not current_user.is_anonymous():
|
if current_user is not None and not current_user.is_anonymous:
|
||||||
roles = sorted(current_user.roles)
|
roles = sorted(current_user.roles)
|
||||||
else:
|
else:
|
||||||
roles = []
|
roles = []
|
||||||
|
|
@ -251,10 +251,13 @@ def setSettings():
|
||||||
return make_response("Expected content-type JSON", 400)
|
return make_response("Expected content-type JSON", 400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.get_json()
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return make_response("Malformed JSON body in request", 400)
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
_saveSettings(data)
|
_saveSettings(data)
|
||||||
return getSettings()
|
return getSettings()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,10 +120,13 @@ def slicingAddSlicerProfile(slicer, name):
|
||||||
return make_response("Expected content-type JSON", 400)
|
return make_response("Expected content-type JSON", 400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
json_data = request.json
|
json_data = request.get_json()
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return make_response("Malformed JSON body in request", 400)
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
|
if json_data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
data = dict()
|
data = dict()
|
||||||
display_name = None
|
display_name = None
|
||||||
description = None
|
description = None
|
||||||
|
|
@ -159,10 +162,13 @@ def slicingPatchSlicerProfile(slicer, name):
|
||||||
return make_response("Profile {name} for slicer {slicer} not found".format(**locals()), 404)
|
return make_response("Profile {name} for slicer {slicer} not found".format(**locals()), 404)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
json_data = request.json
|
json_data = request.get_json()
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return make_response("Malformed JSON body in request", 400)
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
|
if json_data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
data = dict()
|
data = dict()
|
||||||
display_name = None
|
display_name = None
|
||||||
description = None
|
description = None
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import logging
|
||||||
import sarge
|
import sarge
|
||||||
|
|
||||||
from flask import request, make_response, jsonify, url_for
|
from flask import request, make_response, jsonify, url_for
|
||||||
from flask.ext.babel import gettext
|
from flask_babel import gettext
|
||||||
|
|
||||||
from octoprint.settings import settings as s
|
from octoprint.settings import settings as s
|
||||||
|
|
||||||
|
|
@ -24,9 +24,9 @@ from octoprint.server.util.flask import restricted_access, get_remote_address
|
||||||
def performSystemAction():
|
def performSystemAction():
|
||||||
logging.getLogger(__name__).warn("Deprecated API call to /api/system made by {}, should be migrated to use /system/commands/custom/<action>".format(get_remote_address(request)))
|
logging.getLogger(__name__).warn("Deprecated API call to /api/system made by {}, should be migrated to use /system/commands/custom/<action>".format(get_remote_address(request)))
|
||||||
|
|
||||||
data = request.values
|
data = request.get_json(silent=True)
|
||||||
if hasattr(request, "json") and request.json:
|
if data is None:
|
||||||
data = request.json
|
data = request.values
|
||||||
|
|
||||||
if not "action" in data:
|
if not "action" in data:
|
||||||
return make_response("action to perform is not defined", 400)
|
return make_response("action to perform is not defined", 400)
|
||||||
|
|
|
||||||
|
|
@ -162,9 +162,9 @@ def processUnrenderedTimelapseCommand(name):
|
||||||
@api.route("/timelapse", methods=["POST"])
|
@api.route("/timelapse", methods=["POST"])
|
||||||
@restricted_access
|
@restricted_access
|
||||||
def setTimelapseConfig():
|
def setTimelapseConfig():
|
||||||
data = request.values
|
data = request.get_json(silent=True)
|
||||||
if hasattr(request, "json") and request.json:
|
if data is None:
|
||||||
data = request.json
|
data = request.values
|
||||||
|
|
||||||
if "type" in data:
|
if "type" in data:
|
||||||
config = {
|
config = {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
|
||||||
|
|
||||||
from flask import request, jsonify, abort, make_response
|
from flask import request, jsonify, abort, make_response
|
||||||
from werkzeug.exceptions import BadRequest
|
from werkzeug.exceptions import BadRequest
|
||||||
from flask.ext.login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
import octoprint.users as users
|
import octoprint.users as users
|
||||||
|
|
||||||
|
|
@ -40,10 +40,13 @@ def addUser():
|
||||||
return make_response("Expected content-type JSON", 400)
|
return make_response("Expected content-type JSON", 400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.get_json()
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return make_response("Malformed JSON body in request", 400)
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
if not "name" in data:
|
if not "name" in data:
|
||||||
return make_response("Missing mandatory name field", 400)
|
return make_response("Missing mandatory name field", 400)
|
||||||
if not "password" in data:
|
if not "password" in data:
|
||||||
|
|
@ -72,7 +75,7 @@ def getUser(username):
|
||||||
if not userManager.enabled:
|
if not userManager.enabled:
|
||||||
return jsonify(SUCCESS)
|
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 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)
|
user = userManager.findUser(username)
|
||||||
if user is not None:
|
if user is not None:
|
||||||
return jsonify(user.asDict())
|
return jsonify(user.asDict())
|
||||||
|
|
@ -95,10 +98,13 @@ def updateUser(username):
|
||||||
return make_response("Expected content-type JSON", 400)
|
return make_response("Expected content-type JSON", 400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.get_json()
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return make_response("Malformed JSON body in request", 400)
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
# change roles
|
# change roles
|
||||||
roles = ["user"]
|
roles = ["user"]
|
||||||
if "admin" in data and data["admin"] in valid_boolean_trues:
|
if "admin" in data and data["admin"] in valid_boolean_trues:
|
||||||
|
|
@ -133,15 +139,18 @@ def changePasswordForUser(username):
|
||||||
if not userManager.enabled:
|
if not userManager.enabled:
|
||||||
return jsonify(SUCCESS)
|
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 current_user is not None and not current_user.is_anonymous and (current_user.get_name() == username or current_user.is_admin):
|
||||||
if not "application/json" in request.headers["Content-Type"]:
|
if not "application/json" in request.headers["Content-Type"]:
|
||||||
return make_response("Expected content-type JSON", 400)
|
return make_response("Expected content-type JSON", 400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.get_json()
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return make_response("Malformed JSON body in request", 400)
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
if not "password" in data or not data["password"]:
|
if not "password" in data or not data["password"]:
|
||||||
return make_response("password is missing from request", 400)
|
return make_response("password is missing from request", 400)
|
||||||
|
|
||||||
|
|
@ -161,7 +170,7 @@ def getSettingsForUser(username):
|
||||||
if not userManager.enabled:
|
if not userManager.enabled:
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
if current_user is None or current_user.is_anonymous() or (current_user.get_name() != username and not current_user.is_admin()):
|
if current_user is None or current_user.is_anonymous or (current_user.get_name() != username and not current_user.is_admin):
|
||||||
return make_response("Forbidden", 403)
|
return make_response("Forbidden", 403)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -175,14 +184,17 @@ def changeSettingsForUser(username):
|
||||||
if not userManager.enabled:
|
if not userManager.enabled:
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
if current_user is None or current_user.is_anonymous() or (current_user.get_name() != username and not current_user.is_admin()):
|
if current_user is None or current_user.is_anonymous or (current_user.get_name() != username and not current_user.is_admin):
|
||||||
return make_response("Forbidden", 403)
|
return make_response("Forbidden", 403)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.get_json()
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return make_response("Malformed JSON body in request", 400)
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
userManager.changeUserSettings(username, data)
|
userManager.changeUserSettings(username, data)
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
@ -195,7 +207,7 @@ def deleteApikeyForUser(username):
|
||||||
if not userManager.enabled:
|
if not userManager.enabled:
|
||||||
return jsonify(SUCCESS)
|
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 current_user is not None and not current_user.is_anonymous and (current_user.get_name() == username or current_user.is_admin):
|
||||||
try:
|
try:
|
||||||
userManager.deleteApikey(username)
|
userManager.deleteApikey(username)
|
||||||
except users.UnknownUser:
|
except users.UnknownUser:
|
||||||
|
|
@ -211,7 +223,7 @@ def generateApikeyForUser(username):
|
||||||
if not userManager.enabled:
|
if not userManager.enabled:
|
||||||
return jsonify(SUCCESS)
|
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 current_user is not None and not current_user.is_anonymous and (current_user.get_name() == username or current_user.is_admin):
|
||||||
try:
|
try:
|
||||||
apikey = userManager.generateApiKey(username)
|
apikey = userManager.generateApiKey(username)
|
||||||
except users.UnknownUser:
|
except users.UnknownUser:
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,10 @@ def verifySessionKey():
|
||||||
if not "application/json" in request.headers["Content-Type"]:
|
if not "application/json" in request.headers["Content-Type"]:
|
||||||
return None, None, make_response("Expected content-type JSON", 400)
|
return None, None, make_response("Expected content-type JSON", 400)
|
||||||
|
|
||||||
data = request.json
|
data = request.get_json()
|
||||||
|
if data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
for key in ("appid", "key", "_sig"):
|
for key in ("appid", "key", "_sig"):
|
||||||
if not key in data:
|
if not key in data:
|
||||||
return make_response("Missing argument: {key}".format(key=key), 400)
|
return make_response("Missing argument: {key}".format(key=key), 400)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ from octoprint.users import ApiUser
|
||||||
from octoprint.util import deprecated
|
from octoprint.util import deprecated
|
||||||
|
|
||||||
import flask as _flask
|
import flask as _flask
|
||||||
|
import flask_login
|
||||||
|
import flask_principal
|
||||||
|
|
||||||
from . import flask
|
from . import flask
|
||||||
from . import sockjs
|
from . import sockjs
|
||||||
|
|
@ -58,9 +60,9 @@ def loginFromApiKeyRequestHandler():
|
||||||
|
|
||||||
if apikey and apikey != octoprint.server.UI_API_KEY and not octoprint.server.appSessionManager.validate(apikey):
|
if apikey and apikey != octoprint.server.UI_API_KEY and not octoprint.server.appSessionManager.validate(apikey):
|
||||||
user = get_user_for_apikey(apikey)
|
user = get_user_for_apikey(apikey)
|
||||||
if user is not None and _flask.ext.login.login_user(user, remember=False):
|
if user is not None and not user.is_anonymous and flask_login.login_user(user, remember=False):
|
||||||
_flask.ext.principal.identity_changed.send(_flask.current_app._get_current_object(),
|
flask_principal.identity_changed.send(_flask.current_app._get_current_object(),
|
||||||
identity=_flask.ext.principal.Identity(user.get_id()))
|
identity=flask_principal.Identity(user.get_id()))
|
||||||
else:
|
else:
|
||||||
return _flask.make_response("Invalid API key", 401)
|
return _flask.make_response("Invalid API key", 401)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
|
||||||
|
|
||||||
import tornado.web
|
import tornado.web
|
||||||
import flask
|
import flask
|
||||||
import flask.ext.login
|
import flask_login
|
||||||
import flask.ext.principal
|
import flask_principal
|
||||||
import flask.ext.assets
|
import flask_assets
|
||||||
import webassets.updater
|
import webassets.updater
|
||||||
import webassets.utils
|
import webassets.utils
|
||||||
import functools
|
import functools
|
||||||
|
|
@ -40,7 +40,7 @@ def enable_additional_translations(default_locale="en", additional_folders=None)
|
||||||
import os
|
import os
|
||||||
from flask import _request_ctx_stack
|
from flask import _request_ctx_stack
|
||||||
from babel import support, Locale
|
from babel import support, Locale
|
||||||
import flask.ext.babel
|
import flask_babel
|
||||||
|
|
||||||
if additional_folders is None:
|
if additional_folders is None:
|
||||||
additional_folders = []
|
additional_folders = []
|
||||||
|
|
@ -91,7 +91,7 @@ def enable_additional_translations(default_locale="en", additional_folders=None)
|
||||||
return None
|
return None
|
||||||
translations = getattr(ctx, 'babel_translations', None)
|
translations = getattr(ctx, 'babel_translations', None)
|
||||||
if translations is None:
|
if translations is None:
|
||||||
locale = flask.ext.babel.get_locale()
|
locale = flask_babel.get_locale()
|
||||||
translations = support.Translations()
|
translations = support.Translations()
|
||||||
|
|
||||||
if str(locale) != default_locale:
|
if str(locale) != default_locale:
|
||||||
|
|
@ -129,8 +129,8 @@ def enable_additional_translations(default_locale="en", additional_folders=None)
|
||||||
ctx.babel_translations = translations
|
ctx.babel_translations = translations
|
||||||
return translations
|
return translations
|
||||||
|
|
||||||
flask.ext.babel.Babel.list_translations = fixed_list_translations
|
flask_babel.Babel.list_translations = fixed_list_translations
|
||||||
flask.ext.babel.get_translations = fixed_get_translations
|
flask_babel.get_translations = fixed_get_translations
|
||||||
|
|
||||||
def fix_webassets_cache():
|
def fix_webassets_cache():
|
||||||
from webassets import cache
|
from webassets import cache
|
||||||
|
|
@ -229,6 +229,27 @@ def fix_webassets_filtertool():
|
||||||
|
|
||||||
FilterTool._wrap_cache = fixed_wrap_cache
|
FilterTool._wrap_cache = fixed_wrap_cache
|
||||||
|
|
||||||
|
# TODO: Remove compatibility layer in OctoPrint 1.5.0
|
||||||
|
def deprecate_flaskext():
|
||||||
|
import flask
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
class FlaskExtDeprecator(object):
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
old_name = "flask.ext.{}".format(item)
|
||||||
|
new_name = "flask_{}".format(item)
|
||||||
|
module = importlib.import_module(new_name)
|
||||||
|
|
||||||
|
from warnings import warn
|
||||||
|
message = "The {old} import is deprecated in Flask versions >= 0.11, which OctoPrint now uses. " + \
|
||||||
|
"Import {new} instead. This compatibility layer will be removed in OctoPrint 1.5.0."
|
||||||
|
warn(DeprecationWarning(message.format(old=old_name, new=new_name)), stacklevel=2)
|
||||||
|
|
||||||
|
return module
|
||||||
|
|
||||||
|
flask.ext = FlaskExtDeprecator()
|
||||||
|
|
||||||
#~~ WSGI environment wrapper for reverse proxying
|
#~~ WSGI environment wrapper for reverse proxying
|
||||||
|
|
||||||
class ReverseProxiedEnvironment(object):
|
class ReverseProxiedEnvironment(object):
|
||||||
|
|
@ -478,12 +499,12 @@ class OctoPrintFlaskResponse(flask.Response):
|
||||||
|
|
||||||
def passive_login():
|
def passive_login():
|
||||||
if octoprint.server.userManager.enabled:
|
if octoprint.server.userManager.enabled:
|
||||||
user = octoprint.server.userManager.login_user(flask.ext.login.current_user)
|
user = octoprint.server.userManager.login_user(flask_login.current_user)
|
||||||
else:
|
else:
|
||||||
user = flask.ext.login.current_user
|
user = flask_login.current_user
|
||||||
|
|
||||||
if user is not None and not user.is_anonymous():
|
if user is not None and not user.is_anonymous:
|
||||||
flask.ext.principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask.ext.principal.Identity(user.get_id()))
|
flask_principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask_principal.Identity(user.get_id()))
|
||||||
if hasattr(user, "get_session"):
|
if hasattr(user, "get_session"):
|
||||||
flask.session["usersession.id"] = user.get_session()
|
flask.session["usersession.id"] = user.get_session()
|
||||||
flask.g.user = user
|
flask.g.user = user
|
||||||
|
|
@ -505,8 +526,8 @@ def passive_login():
|
||||||
user = octoprint.server.userManager.login_user(user)
|
user = octoprint.server.userManager.login_user(user)
|
||||||
flask.session["usersession.id"] = user.get_session()
|
flask.session["usersession.id"] = user.get_session()
|
||||||
flask.g.user = user
|
flask.g.user = user
|
||||||
flask.ext.login.login_user(user)
|
flask_login.login_user(user)
|
||||||
flask.ext.principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask.ext.principal.Identity(user.get_id()))
|
flask_principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask_principal.Identity(user.get_id()))
|
||||||
return flask.jsonify(user.asDict())
|
return flask.jsonify(user.asDict())
|
||||||
except:
|
except:
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -1023,7 +1044,7 @@ def admin_validator(request):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user = _get_flask_user_from_request(request)
|
user = _get_flask_user_from_request(request)
|
||||||
if user is None or not user.is_authenticated() or not user.is_admin():
|
if user is None or not user.is_authenticated or not user.is_admin:
|
||||||
raise tornado.web.HTTPError(403)
|
raise tornado.web.HTTPError(403)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1038,7 +1059,7 @@ def user_validator(request):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user = _get_flask_user_from_request(request)
|
user = _get_flask_user_from_request(request)
|
||||||
if user is None or not user.is_authenticated():
|
if user is None or not user.is_authenticated:
|
||||||
raise tornado.web.HTTPError(403)
|
raise tornado.web.HTTPError(403)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1051,14 +1072,14 @@ def _get_flask_user_from_request(request):
|
||||||
:return: the user or None if no user could be determined
|
:return: the user or None if no user could be determined
|
||||||
"""
|
"""
|
||||||
import octoprint.server.util
|
import octoprint.server.util
|
||||||
import flask.ext.login
|
import flask_login
|
||||||
from octoprint.settings import settings
|
from octoprint.settings import settings
|
||||||
|
|
||||||
apikey = octoprint.server.util.get_api_key(request)
|
apikey = octoprint.server.util.get_api_key(request)
|
||||||
if settings().getBoolean(["api", "enabled"]) and apikey is not None:
|
if settings().getBoolean(["api", "enabled"]) and apikey is not None:
|
||||||
user = octoprint.server.util.get_user_for_apikey(apikey)
|
user = octoprint.server.util.get_user_for_apikey(apikey)
|
||||||
else:
|
else:
|
||||||
user = flask.ext.login.current_user
|
user = flask_login.current_user
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
@ -1103,7 +1124,7 @@ def restricted_access(func):
|
||||||
if settings().getBoolean(["server", "firstRun"]) and settings().getBoolean(["accessControl", "enabled"]) and (octoprint.server.userManager is None or not octoprint.server.userManager.hasBeenCustomized()):
|
if settings().getBoolean(["server", "firstRun"]) and settings().getBoolean(["accessControl", "enabled"]) and (octoprint.server.userManager is None or not octoprint.server.userManager.hasBeenCustomized()):
|
||||||
return flask.make_response("OctoPrint isn't setup yet", 403)
|
return flask.make_response("OctoPrint isn't setup yet", 403)
|
||||||
|
|
||||||
return flask.ext.login.login_required(func)(*args, **kwargs)
|
return flask_login.login_required(func)(*args, **kwargs)
|
||||||
|
|
||||||
return decorated_view
|
return decorated_view
|
||||||
|
|
||||||
|
|
@ -1188,7 +1209,10 @@ def get_json_command_from_request(request, valid_commands):
|
||||||
if content_type is None or not "application/json" in content_type:
|
if content_type is None or not "application/json" in content_type:
|
||||||
return None, None, make_response("Expected content-type JSON", 400)
|
return None, None, make_response("Expected content-type JSON", 400)
|
||||||
|
|
||||||
data = request.json
|
data = request.get_json()
|
||||||
|
if data is None:
|
||||||
|
return make_response("Malformed JSON body in request", 400)
|
||||||
|
|
||||||
if not "command" in data.keys() or not data["command"] in valid_commands.keys():
|
if not "command" in data.keys() or not data["command"] in valid_commands.keys():
|
||||||
return None, None, make_response("Expected valid command", 400)
|
return None, None, make_response("Expected valid command", 400)
|
||||||
|
|
||||||
|
|
@ -1201,7 +1225,7 @@ def get_json_command_from_request(request, valid_commands):
|
||||||
|
|
||||||
##~~ Flask-Assets resolver with plugin asset support
|
##~~ Flask-Assets resolver with plugin asset support
|
||||||
|
|
||||||
class PluginAssetResolver(flask.ext.assets.FlaskResolver):
|
class PluginAssetResolver(flask_assets.FlaskResolver):
|
||||||
|
|
||||||
def split_prefix(self, ctx, item):
|
def split_prefix(self, ctx, item):
|
||||||
app = ctx.environment._app
|
app = ctx.environment._app
|
||||||
|
|
@ -1210,14 +1234,14 @@ class PluginAssetResolver(flask.ext.assets.FlaskResolver):
|
||||||
prefix, plugin, name = item.split("/", 2)
|
prefix, plugin, name = item.split("/", 2)
|
||||||
blueprint = prefix + "." + plugin
|
blueprint = prefix + "." + plugin
|
||||||
|
|
||||||
directory = flask.ext.assets.get_static_folder(app.blueprints[blueprint])
|
directory = flask_assets.get_static_folder(app.blueprints[blueprint])
|
||||||
item = name
|
item = name
|
||||||
endpoint = blueprint + ".static"
|
endpoint = blueprint + ".static"
|
||||||
return directory, item, endpoint
|
return directory, item, endpoint
|
||||||
except (ValueError, KeyError):
|
except (ValueError, KeyError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return flask.ext.assets.FlaskResolver.split_prefix(self, ctx, item)
|
return flask_assets.FlaskResolver.split_prefix(self, ctx, item)
|
||||||
|
|
||||||
def resolve_output_to_path(self, ctx, target, bundle):
|
def resolve_output_to_path(self, ctx, target, bundle):
|
||||||
import os
|
import os
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import datetime
|
|
||||||
import stat
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import email
|
|
||||||
import time
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import tornado
|
import tornado
|
||||||
|
|
@ -29,36 +25,6 @@ import tornado.util
|
||||||
import octoprint.util
|
import octoprint.util
|
||||||
|
|
||||||
|
|
||||||
#~~ Monkey patching
|
|
||||||
|
|
||||||
|
|
||||||
def fix_ioloop_scheduling():
|
|
||||||
"""
|
|
||||||
This monkey patches tornado's :meth:``tornado.ioloop.PeriodicCallback._schedule_next`` method so it no longer
|
|
||||||
blocks for long times on slow machines (RPi) when the system time happens to change by a large amount (e.g. due to
|
|
||||||
the first ever contact to an NTP server).
|
|
||||||
|
|
||||||
Patch by @nosyjoe on Github. See this PR against tornado: https://github.com/tornadoweb/tornado/pull/1290
|
|
||||||
"""
|
|
||||||
|
|
||||||
import math
|
|
||||||
|
|
||||||
# patched implementation taken from PR
|
|
||||||
def _schedule_next(self):
|
|
||||||
if self._running:
|
|
||||||
current_time = self.io_loop.time()
|
|
||||||
|
|
||||||
if self._next_timeout <= current_time:
|
|
||||||
callback_time_sec = self.callback_time / 1000.0
|
|
||||||
self._next_timeout += (math.floor((current_time - self._next_timeout) / callback_time_sec) + 1) * callback_time_sec
|
|
||||||
|
|
||||||
self._timeout = self.io_loop.add_timeout(self._next_timeout, self._run)
|
|
||||||
|
|
||||||
# replace original implementation with patched version
|
|
||||||
import tornado.ioloop
|
|
||||||
tornado.ioloop.PeriodicCallback._schedule_next = _schedule_next
|
|
||||||
|
|
||||||
|
|
||||||
#~~ WSGI middleware
|
#~~ WSGI middleware
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -560,7 +526,8 @@ class WsgiInputContainer(object):
|
||||||
if not data:
|
if not data:
|
||||||
raise Exception("WSGI app did not call start_response")
|
raise Exception("WSGI app did not call start_response")
|
||||||
|
|
||||||
status_code = int(data["status"].split()[0])
|
status_code, reason = data["status"].split(" ", 1)
|
||||||
|
status_code = int(status_code)
|
||||||
headers = data["headers"]
|
headers = data["headers"]
|
||||||
header_set = set(k.lower() for (k, v) in headers)
|
header_set = set(k.lower() for (k, v) in headers)
|
||||||
body = tornado.escape.utf8(body)
|
body = tornado.escape.utf8(body)
|
||||||
|
|
@ -572,13 +539,12 @@ class WsgiInputContainer(object):
|
||||||
if "server" not in header_set:
|
if "server" not in header_set:
|
||||||
headers.append(("Server", "TornadoServer/%s" % tornado.version))
|
headers.append(("Server", "TornadoServer/%s" % tornado.version))
|
||||||
|
|
||||||
parts = [tornado.escape.utf8("HTTP/1.1 " + data["status"] + "\r\n")]
|
start_line = tornado.httputil.ResponseStartLine("HTTP/1.1", status_code, reason)
|
||||||
|
header_obj = tornado.httputil.HTTPHeaders()
|
||||||
for key, value in headers:
|
for key, value in headers:
|
||||||
parts.append(tornado.escape.utf8(key) + b": " + tornado.escape.utf8(value) + b"\r\n")
|
header_obj.add(key, value)
|
||||||
parts.append(b"\r\n")
|
request.connection.write_headers(start_line, header_obj, chunk=body)
|
||||||
parts.append(body)
|
request.connection.finish()
|
||||||
request.write(b"".join(parts))
|
|
||||||
request.finish()
|
|
||||||
self._log(status_code, request)
|
self._log(status_code, request)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -613,22 +579,22 @@ class WsgiInputContainer(object):
|
||||||
host = request.host
|
host = request.host
|
||||||
port = 443 if request.protocol == "https" else 80
|
port = 443 if request.protocol == "https" else 80
|
||||||
environ = {
|
environ = {
|
||||||
"REQUEST_METHOD": request.method,
|
"REQUEST_METHOD": request.method,
|
||||||
"SCRIPT_NAME": "",
|
"SCRIPT_NAME": "",
|
||||||
"PATH_INFO": to_wsgi_str(tornado.escape.url_unescape(
|
"PATH_INFO": to_wsgi_str(tornado.escape.url_unescape(
|
||||||
request.path, encoding=None, plus=False)),
|
request.path, encoding=None, plus=False)),
|
||||||
"QUERY_STRING": request.query,
|
"QUERY_STRING": request.query,
|
||||||
"REMOTE_ADDR": request.remote_ip,
|
"REMOTE_ADDR": request.remote_ip,
|
||||||
"SERVER_NAME": host,
|
"SERVER_NAME": host,
|
||||||
"SERVER_PORT": str(port),
|
"SERVER_PORT": str(port),
|
||||||
"SERVER_PROTOCOL": request.version,
|
"SERVER_PROTOCOL": request.version,
|
||||||
"wsgi.version": (1, 0),
|
"wsgi.version": (1, 0),
|
||||||
"wsgi.url_scheme": request.protocol,
|
"wsgi.url_scheme": request.protocol,
|
||||||
"wsgi.input": request_body,
|
"wsgi.input": request_body,
|
||||||
"wsgi.errors": sys.stderr,
|
"wsgi.errors": sys.stderr,
|
||||||
"wsgi.multithread": False,
|
"wsgi.multithread": False,
|
||||||
"wsgi.multiprocess": True,
|
"wsgi.multiprocess": True,
|
||||||
"wsgi.run_once": False,
|
"wsgi.run_once": False,
|
||||||
}
|
}
|
||||||
if "Content-Type" in request.headers:
|
if "Content-Type" in request.headers:
|
||||||
environ["CONTENT_TYPE"] = request.headers.pop("Content-Type")
|
environ["CONTENT_TYPE"] = request.headers.pop("Content-Type")
|
||||||
|
|
@ -649,7 +615,7 @@ class WsgiInputContainer(object):
|
||||||
log_method = access_log.error
|
log_method = access_log.error
|
||||||
request_time = 1000.0 * request.request_time()
|
request_time = 1000.0 * request.request_time()
|
||||||
summary = request.method + " " + request.uri + " (" + \
|
summary = request.method + " " + request.uri + " (" + \
|
||||||
request.remote_ip + ")"
|
request.remote_ip + ")"
|
||||||
log_method("%d %s %.2fms", status_code, summary, request_time)
|
log_method("%d %s %.2fms", status_code, summary, request_time)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -672,36 +638,24 @@ class CustomHTTPServer(tornado.httpserver.HTTPServer):
|
||||||
|
|
||||||
``default_max_body_size`` is the default maximum body size to apply if no specific one from ``max_body_sizes`` matches.
|
``default_max_body_size`` is the default maximum body size to apply if no specific one from ``max_body_sizes`` matches.
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
def __init__(self, request_callback, no_keep_alive=False, io_loop=None,
|
def initialize(self, *args, **kwargs):
|
||||||
xheaders=False, ssl_options=None, protocol=None,
|
default_max_body_size = kwargs.pop("default_max_body_size", None)
|
||||||
decompress_request=False,
|
max_body_sizes = kwargs.pop("max_body_sizes", None)
|
||||||
chunk_size=None, max_header_size=None,
|
|
||||||
idle_connection_timeout=None, body_timeout=None,
|
tornado.httpserver.HTTPServer.initialize(self, *args, **kwargs)
|
||||||
max_body_sizes=None, default_max_body_size=None, max_buffer_size=None):
|
|
||||||
self.request_callback = request_callback
|
additional = dict(default_max_body_size=default_max_body_size,
|
||||||
self.no_keep_alive = no_keep_alive
|
max_body_sizes=max_body_sizes)
|
||||||
self.xheaders = xheaders
|
self.conn_params = CustomHTTP1ConnectionParameters.from_stock_params(self.conn_params, **additional)
|
||||||
self.protocol = protocol
|
|
||||||
self.conn_params = CustomHTTP1ConnectionParameters(
|
|
||||||
decompress=decompress_request,
|
|
||||||
chunk_size=chunk_size,
|
|
||||||
max_header_size=max_header_size,
|
|
||||||
header_timeout=idle_connection_timeout or 3600,
|
|
||||||
max_body_sizes=max_body_sizes,
|
|
||||||
default_max_body_size=default_max_body_size,
|
|
||||||
body_timeout=body_timeout)
|
|
||||||
tornado.tcpserver.TCPServer.__init__(self, io_loop=io_loop, ssl_options=ssl_options,
|
|
||||||
max_buffer_size=max_buffer_size,
|
|
||||||
read_chunk_size=chunk_size)
|
|
||||||
self._connections = set()
|
|
||||||
|
|
||||||
|
|
||||||
def handle_stream(self, stream, address):
|
def handle_stream(self, stream, address):
|
||||||
context = tornado.httpserver._HTTPRequestContext(stream, address,
|
context = tornado.httpserver._HTTPRequestContext(stream, address,
|
||||||
self.protocol)
|
self.protocol)
|
||||||
conn = CustomHTTP1ServerConnection(
|
conn = CustomHTTP1ServerConnection(stream, self.conn_params, context)
|
||||||
stream, self.conn_params, context)
|
|
||||||
self._connections.add(conn)
|
self._connections.add(conn)
|
||||||
conn.start_serving(self)
|
conn.start_serving(self)
|
||||||
|
|
||||||
|
|
@ -718,7 +672,7 @@ class CustomHTTP1ServerConnection(tornado.http1connection.HTTP1ServerConnection)
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
conn = CustomHTTP1Connection(self.stream, False,
|
conn = CustomHTTP1Connection(self.stream, False,
|
||||||
self.params, self.context)
|
self.params, self.context)
|
||||||
request_delegate = delegate.start_request(self, conn)
|
request_delegate = delegate.start_request(self, conn)
|
||||||
try:
|
try:
|
||||||
ret = yield conn.read_response(request_delegate)
|
ret = yield conn.read_response(request_delegate)
|
||||||
|
|
@ -762,8 +716,13 @@ class CustomHTTP1Connection(tornado.http1connection.HTTP1Connection):
|
||||||
current request exceeds the individual max content length, the request processing is aborted and an
|
current request exceeds the individual max content length, the request processing is aborted and an
|
||||||
``HTTPInputError`` is raised.
|
``HTTPInputError`` is raised.
|
||||||
"""
|
"""
|
||||||
content_length = headers.get("Content-Length")
|
|
||||||
if "Content-Length" in headers:
|
if "Content-Length" in headers:
|
||||||
|
if "Transfer-Encoding" in headers:
|
||||||
|
# Response cannot contain both Content-Length and
|
||||||
|
# Transfer-Encoding headers.
|
||||||
|
# http://tools.ietf.org/html/rfc7230#section-3.3.3
|
||||||
|
raise tornado.httputil.HTTPInputError(
|
||||||
|
"Response with both Transfer-Encoding and Content-Length")
|
||||||
if "," in headers["Content-Length"]:
|
if "," in headers["Content-Length"]:
|
||||||
# Proxies sometimes cause Content-Length headers to get
|
# Proxies sometimes cause Content-Length headers to get
|
||||||
# duplicated. If all the values are identical then we can
|
# duplicated. If all the values are identical then we can
|
||||||
|
|
@ -774,9 +733,14 @@ class CustomHTTP1Connection(tornado.http1connection.HTTP1Connection):
|
||||||
"Multiple unequal Content-Lengths: %r" %
|
"Multiple unequal Content-Lengths: %r" %
|
||||||
headers["Content-Length"])
|
headers["Content-Length"])
|
||||||
headers["Content-Length"] = pieces[0]
|
headers["Content-Length"] = pieces[0]
|
||||||
content_length = int(headers["Content-Length"])
|
|
||||||
|
|
||||||
content_length = int(content_length)
|
try:
|
||||||
|
content_length = int(headers["Content-Length"])
|
||||||
|
except ValueError:
|
||||||
|
# Handles non-integer Content-Length value.
|
||||||
|
raise tornado.httputil.HTTPInputError(
|
||||||
|
"Only integer Content-Length is allowed: %s" % headers["Content-Length"])
|
||||||
|
|
||||||
max_content_length = self._get_max_content_length(self._request_start_line.method, self._request_start_line.path)
|
max_content_length = self._get_max_content_length(self._request_start_line.method, self._request_start_line.path)
|
||||||
if max_content_length is not None and 0 <= max_content_length < content_length:
|
if max_content_length is not None and 0 <= max_content_length < content_length:
|
||||||
raise tornado.httputil.HTTPInputError("Content-Length too long")
|
raise tornado.httputil.HTTPInputError("Content-Length too long")
|
||||||
|
|
@ -828,9 +792,20 @@ class CustomHTTP1ConnectionParameters(tornado.http1connection.HTTP1ConnectionPar
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
tornado.http1connection.HTTP1ConnectionParameters.__init__(self, args, kwargs)
|
max_body_sizes = kwargs.pop("max_body_sizes", list())
|
||||||
self.max_body_sizes = kwargs["max_body_sizes"] if "max_body_sizes" in kwargs else list()
|
default_max_body_size = kwargs.pop("default_max_body_size", None)
|
||||||
self.default_max_body_size = kwargs["default_max_body_size"] if "default_max_body_size" in kwargs else None
|
|
||||||
|
tornado.http1connection.HTTP1ConnectionParameters.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
self.max_body_sizes = max_body_sizes
|
||||||
|
self.default_max_body_size = default_max_body_size
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_stock_params(cls, other, **additional):
|
||||||
|
kwargs = dict(other.__dict__)
|
||||||
|
for key, value in additional.items():
|
||||||
|
kwargs[key] = value
|
||||||
|
return cls(**kwargs)
|
||||||
|
|
||||||
#~~ customized large response handler
|
#~~ customized large response handler
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -482,6 +482,17 @@ $(function() {
|
||||||
// reload overlay
|
// reload overlay
|
||||||
$("#reloadui_overlay_reload").click(function() { location.reload(); });
|
$("#reloadui_overlay_reload").click(function() { location.reload(); });
|
||||||
|
|
||||||
|
var changeTab = function()
|
||||||
|
{
|
||||||
|
var hashtag = window.location.hash;
|
||||||
|
var tab = $('#tabs a[href="' + hashtag + '"]');
|
||||||
|
if (tab.length)
|
||||||
|
{
|
||||||
|
tab.tab("show");
|
||||||
|
onTabChange(hashtag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//~~ view model binding
|
//~~ view model binding
|
||||||
|
|
||||||
var bindViewModels = function() {
|
var bindViewModels = function() {
|
||||||
|
|
@ -570,6 +581,15 @@ $(function() {
|
||||||
callViewModels(allViewModels, "onBrowserTabVisibilityChange", [status]);
|
callViewModels(allViewModels, "onBrowserTabVisibilityChange", [status]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(window).on("hashchange", function() {
|
||||||
|
changeTab();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (window.location.hash != "")
|
||||||
|
{
|
||||||
|
changeTab();
|
||||||
|
}
|
||||||
|
|
||||||
log.info("Application startup complete");
|
log.info("Application startup complete");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ __author__ = "Gina Häußge <osd@foosel.net>"
|
||||||
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
||||||
__copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License"
|
__copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License"
|
||||||
|
|
||||||
from flask.ext.login import UserMixin
|
from flask_login import UserMixin, AnonymousUserMixin
|
||||||
from flask.ext.principal import Identity
|
from flask_principal import Identity
|
||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
|
|
@ -416,6 +416,65 @@ class UnknownRole(Exception):
|
||||||
|
|
||||||
##~~ User object
|
##~~ User object
|
||||||
|
|
||||||
|
class MethodReplacedByBooleanProperty(object):
|
||||||
|
|
||||||
|
def __init__(self, name, message, getter):
|
||||||
|
self._name = name
|
||||||
|
self._message = message
|
||||||
|
self._getter = getter
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _attr(self):
|
||||||
|
return self._getter()
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
from warnings import warn
|
||||||
|
warn(DeprecationWarning(self._message.format(name=self._name)), stacklevel=2)
|
||||||
|
return self._attr
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self._attr == other
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return self._attr != other
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
# Python 3
|
||||||
|
return self._attr
|
||||||
|
|
||||||
|
def __nonzero__(self):
|
||||||
|
# Python 2
|
||||||
|
return self._attr
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self._attr)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "MethodReplacedByProperty({}, {}, {})".format(self._name, self._message, self._getter)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self._attr)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Remove compatibility layer in OctoPrint 1.5.0
|
||||||
|
class FlaskLoginMethodReplacedByBooleanProperty(MethodReplacedByBooleanProperty):
|
||||||
|
|
||||||
|
def __init__(self, name, getter):
|
||||||
|
message = "{name} is now a property in Flask-Login versions >= 0.3.0, which OctoPrint now uses. " + \
|
||||||
|
"Use {name} instead of {name}(). This compatibility layer will be removed in OctoPrint 1.5.0."
|
||||||
|
MethodReplacedByBooleanProperty.__init__(self, name, message, getter)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Remove compatibility layer in OctoPrint 1.5.0
|
||||||
|
class OctoPrintUserMethodReplacedByBooleanProperty(MethodReplacedByBooleanProperty):
|
||||||
|
|
||||||
|
def __init__(self, name, getter):
|
||||||
|
message = "{name} is now a property for consistency reasons with Flask-Login versions >= 0.3.0, which " + \
|
||||||
|
"OctoPrint now uses. Use {name} instead of {name}(). This compatibility layer will be removed " + \
|
||||||
|
"in OctoPrint 1.5.0."
|
||||||
|
MethodReplacedByBooleanProperty.__init__(self, name, message, getter)
|
||||||
|
|
||||||
|
|
||||||
class User(UserMixin):
|
class User(UserMixin):
|
||||||
def __init__(self, username, passwordHash, active, roles, apikey=None, settings=None):
|
def __init__(self, username, passwordHash, active, roles, apikey=None, settings=None):
|
||||||
self._username = username
|
self._username = username
|
||||||
|
|
@ -431,9 +490,9 @@ class User(UserMixin):
|
||||||
def asDict(self):
|
def asDict(self):
|
||||||
return {
|
return {
|
||||||
"name": self._username,
|
"name": self._username,
|
||||||
"active": self.is_active(),
|
"active": bool(self.is_active),
|
||||||
"admin": self.is_admin(),
|
"admin": bool(self.is_admin),
|
||||||
"user": self.is_user(),
|
"user": bool(self.is_user),
|
||||||
"apikey": self._apikey,
|
"apikey": self._apikey,
|
||||||
"settings": self._settings
|
"settings": self._settings
|
||||||
}
|
}
|
||||||
|
|
@ -447,14 +506,25 @@ class User(UserMixin):
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self._username
|
return self._username
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_anonymous(self):
|
||||||
|
return FlaskLoginMethodReplacedByBooleanProperty("is_anonymous", lambda: False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_authenticated(self):
|
||||||
|
return FlaskLoginMethodReplacedByBooleanProperty("is_authenticated", lambda: True)
|
||||||
|
|
||||||
|
@property
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
return self._active
|
return FlaskLoginMethodReplacedByBooleanProperty("is_active", lambda: self._active)
|
||||||
|
|
||||||
|
@property
|
||||||
def is_user(self):
|
def is_user(self):
|
||||||
return "user" in self._roles
|
return OctoPrintUserMethodReplacedByBooleanProperty("is_user", lambda: "user" in self._roles)
|
||||||
|
|
||||||
|
@property
|
||||||
def is_admin(self):
|
def is_admin(self):
|
||||||
return "admin" in self._roles
|
return OctoPrintUserMethodReplacedByBooleanProperty("is_admin", lambda: "admin" in self._roles)
|
||||||
|
|
||||||
def get_all_settings(self):
|
def get_all_settings(self):
|
||||||
return self._settings
|
return self._settings
|
||||||
|
|
@ -503,7 +573,23 @@ class User(UserMixin):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "User(id=%s,name=%s,active=%r,user=%r,admin=%r)" % (self.get_id(), self.get_name(), self.is_active(), self.is_user(), self.is_admin())
|
return "User(id=%s,name=%s,active=%r,user=%r,admin=%r)" % (self.get_id(), self.get_name(), bool(self.is_active), bool(self.is_user), bool(self.is_admin))
|
||||||
|
|
||||||
|
|
||||||
|
class AnonymousUser(AnonymousUserMixin):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_anonymous(self):
|
||||||
|
return FlaskLoginMethodReplacedByBooleanProperty("is_anonymous", lambda: True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_authenticated(self):
|
||||||
|
return FlaskLoginMethodReplacedByBooleanProperty("is_authenticated", lambda: False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_active(self):
|
||||||
|
return FlaskLoginMethodReplacedByBooleanProperty("is_active", lambda: False)
|
||||||
|
|
||||||
|
|
||||||
class SessionUser(User):
|
class SessionUser(User):
|
||||||
def __init__(self, user):
|
def __init__(self, user):
|
||||||
|
|
@ -535,7 +621,7 @@ class SessionUser(User):
|
||||||
self._user = user
|
self._user = user
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "SessionUser(id=%s,name=%s,active=%r,user=%r,admin=%r,session=%s,created=%s)" % (self.get_id(), self.get_name(), self.is_active(), self.is_user(), self.is_admin(), self._session, self._created)
|
return "SessionUser(id=%s,name=%s,active=%r,user=%r,admin=%r,session=%s,created=%s)" % (self.get_id(), self.get_name(), bool(self.is_active), bool(self.is_user), bool(self.is_admin), self._session, self._created)
|
||||||
|
|
||||||
##~~ DummyUser object to use when accessControl is disabled
|
##~~ DummyUser object to use when accessControl is disabled
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ def serialList():
|
||||||
+ glob.glob("/dev/tty.usb*") \
|
+ glob.glob("/dev/tty.usb*") \
|
||||||
+ glob.glob("/dev/cu.*") \
|
+ glob.glob("/dev/cu.*") \
|
||||||
+ glob.glob("/dev/cuaU*") \
|
+ glob.glob("/dev/cuaU*") \
|
||||||
|
+ glob.glob("/dev/ttyS*") \
|
||||||
+ glob.glob("/dev/rfcomm*")
|
+ glob.glob("/dev/rfcomm*")
|
||||||
|
|
||||||
additionalPorts = settings().get(["serial", "additionalPorts"])
|
additionalPorts = settings().get(["serial", "additionalPorts"])
|
||||||
|
|
@ -1569,7 +1570,7 @@ class MachineCom(object):
|
||||||
|
|
||||||
def _poll_temperature(self):
|
def _poll_temperature(self):
|
||||||
"""
|
"""
|
||||||
Polls the temperature after the temperature timeout, re-enqueues itself.
|
Polls the temperature.
|
||||||
|
|
||||||
If the printer is not operational, closing the connection, not printing from sd, busy with a long running
|
If the printer is not operational, closing the connection, not printing from sd, busy with a long running
|
||||||
command or heating, no poll will be done.
|
command or heating, no poll will be done.
|
||||||
|
|
@ -1580,7 +1581,7 @@ class MachineCom(object):
|
||||||
|
|
||||||
def _poll_sd_status(self):
|
def _poll_sd_status(self):
|
||||||
"""
|
"""
|
||||||
Polls the sd printing status after the sd status timeout, re-enqueues itself.
|
Polls the sd printing status.
|
||||||
|
|
||||||
If the printer is not operational, closing the connection, not printing from sd, busy with a long running
|
If the printer is not operational, closing the connection, not printing from sd, busy with a long running
|
||||||
command or heating, no poll will be done.
|
command or heating, no poll will be done.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue