summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas2017-08-12 19:06:52 +0200
committerNiklas2017-08-12 19:06:52 +0200
commitec78392b29cd0be677362ae081c7b62f3c06ecb1 (patch)
tree961e4ba59d542823170c9c6773959ec21461a427
parent5ec8a605f9ffc1d37ee2c858f76954110a5aac89 (diff)
downloadaur-ec78392b29cd0be677362ae081c7b62f3c06ecb1.tar.gz
update to 1.3.4
-rw-r--r--.SRCINFO55
-rw-r--r--PKGBUILD111
-rw-r--r--octoprint-deps.patch641
-rw-r--r--octoprint-jinja29.patch148
-rwxr-xr-xoctoprint.install4
-rw-r--r--octoprint.run5
-rw-r--r--octoprint.service5
7 files changed, 865 insertions, 104 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 048b6f8ceca1..6ed4cd3d3172 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,51 +1,54 @@
-# Generated by mksrcinfo v8
-# Thu Oct 20 16:25:19 UTC 2016
pkgbase = octoprint
- pkgdesc = OctoPrint provides a responsive web interface for controlling a 3D printer (RepRap, Ultimaker, ...)
- pkgver = 1.2.16
+ pkgdesc = Responsive web interface for controlling a 3D printer (RepRap, Ultimaker, ...)
+ pkgver = 1.3.4
pkgrel = 1
- url = https://github.com/foosel/OctoPrint
+ url = http://octoprint.org/
install = octoprint.install
arch = any
- license = GPL
- makedepends = git
+ license = AGPL3
+ depends = python2-awesome-slugify
depends = python2-blinker
+ depends = python2-chainmap
+ depends = python2-click
+ depends = python2-dateutil
+ depends = python2-feedparser
depends = python2-flask
+ depends = python2-flask-assets
depends = python2-flask-babel
depends = python2-flask-login
depends = python2-flask-principal
+ depends = python2-future
+ depends = python2-markdown
depends = python2-netaddr
- depends = python2-numpy
+ depends = python2-netifaces
depends = python2-pkginfo
- depends = python2-pylru-git
+ depends = python2-psutil
+ depends = python2-pylru
depends = python2-pyserial
+ depends = python2-requests
depends = python2-rsa
- depends = python2-sarge-git
+ depends = python2-sarge
+ depends = python2-scandir
+ depends = python2-semanticversion
+ depends = python2-sockjs-tornado
depends = python2-tornado
- depends = python2-tornadio2
- depends = python2-sockjs-tornado-git
- depends = python2-webassets
depends = python2-watchdog
+ depends = python2-websocket-client
depends = python2-yaml
- depends = python2-flask-assets
- depends = python2-psutil
- depends = python2-requests
- depends = polkit
- depends = python2-netifaces
- depends = python2-awesome-slugify
optdepends = ffmpeg: timelapse support
optdepends = mjpg-streamer: stream images from webcam
optdepends = v4l-mjpg-stream: stream images from a Video4Linux capable camera
provides = octoprint
- conflicts = octoprint
- source = octoprint-1.2.16.tar.gz::https://github.com/foosel/OctoPrint/archive/1.2.16.tar.gz
+ source = https://github.com/foosel/OctoPrint/archive/1.3.4.tar.gz
source = octoprint.run
source = octoprint.service
- source = octoprint-tornado401.patch
- md5sums = SKIP
- md5sums = 3bee9901c9eabed94b7f9236f83bf053
- md5sums = ec5e51f876bb5fb223801bf28850908a
- md5sums = 88d6fb3a61a4ab990fb6cca39c830a6c
+ source = octoprint-deps.patch
+ source = octoprint-jinja29.patch
+ sha512sums = df22247796b46f270f7525777194e983a12be3c5f719ffc4ed864d6012d2050b813df9e8f9ee95bdc7687838c96f7c102327a7b802d42abd84481ddd77c8ebea
+ sha512sums = 5c22c76e4089958ff42e2627f29c360fa0f9a73b849f1fe5092cdd9ae800323263b75ac7e23d7189badac9baa7daa850183bf2491680a0ac01c605531728da62
+ sha512sums = 89a8703cbc8b8802eb6a360359f92904357a99bf5b1174c85d4555f88a816d4c008d077fe4f3fed4db0b23e8a662359bcfafaf7750a2ed50a81104f04d04c056
+ sha512sums = cb4190c0a24499fdac2d48e89ba20229ac7d11df0d6bb56b8930fadde3595e70387868374d93c49aa0230b276f7bc061969ca64d92de995aea403150d156969f
+ sha512sums = 41e03bcc2111d1000ca0d62634009dc2030bae52b6623cd58a595f570e0f99e524613d52785b50098fe044fe5355b26221ce7a286148d9bdd95fb3f220bb5568
pkgname = octoprint
diff --git a/PKGBUILD b/PKGBUILD
index 1acbcf3a6549..a7343012f02d 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,112 +1,81 @@
-# Maintainer: Yunhui Fu <yhfudev@gmail.com>
+# Maintainer: Niklas <dev@n1klas.net>
+# Contributor: Yunhui Fu <yhfudev@gmail.com>
# Contributor: Doug Richardson <dougie.richardson@gmail.com>
# Contributor: feilen <feilen1000@gmail.com>
# Contributor: Thermionix <thermionix@gmail.com>
pkgname=octoprint
-pkgver=1.2.16
+_reponame=OctoPrint
+pkgver=1.3.4
pkgrel=1
-pkgdesc="OctoPrint provides a responsive web interface for controlling a 3D printer (RepRap, Ultimaker, ...)"
+pkgdesc="Responsive web interface for controlling a 3D printer (RepRap, Ultimaker, ...)"
arch=(any)
-url="https://github.com/foosel/OctoPrint"
-license=('GPL')
+url="http://octoprint.org/"
+license=('AGPL3')
depends=(
+ 'python2-awesome-slugify'
'python2-blinker'
+ 'python2-chainmap'
+ 'python2-click'
+ 'python2-dateutil'
+ 'python2-feedparser'
'python2-flask'
+ 'python2-flask-assets'
'python2-flask-babel'
'python2-flask-login'
- #'python2-flask-markdown'
'python2-flask-principal'
+ 'python2-future'
+ 'python2-markdown'
'python2-netaddr'
- 'python2-numpy'
+ 'python2-netifaces'
'python2-pkginfo'
- 'python2-pylru-git' # 'python2-pylru'
+ 'python2-psutil'
+ 'python2-pylru'
'python2-pyserial'
+ 'python2-requests'
'python2-rsa'
- 'python2-sarge-git' # 'python2-sarge'
+ 'python2-sarge'
+ 'python2-scandir'
+ 'python2-semanticversion'
+ 'python2-sockjs-tornado'
'python2-tornado'
- 'python2-tornadio2'
- 'python2-sockjs-tornado-git'
- 'python2-webassets'
'python2-watchdog'
+ 'python2-websocket-client'
'python2-yaml'
- 'python2-flask-assets'
- 'python2-psutil'
- 'python2-requests'
- 'polkit'
- 'python2-netifaces'
- 'python2-awesome-slugify'
- )
-makedepends=( 'git' )
-optdepends=(
+)
+optdepends=(
'ffmpeg: timelapse support'
'mjpg-streamer: stream images from webcam'
'v4l-mjpg-stream: stream images from a Video4Linux capable camera'
)
-provides=( 'octoprint' )
-conflicts=( 'octoprint' )
+provides=('octoprint')
install="octoprint.install"
source=(
- #"${pkgname}::git+https://github.com/foosel/OctoPrint.git#commit=${pkgver}"
- "${pkgname}-${pkgver}.tar.gz::https://github.com/foosel/OctoPrint/archive/${pkgver}.tar.gz"
+ "https://github.com/foosel/OctoPrint/archive/${pkgver}.tar.gz"
octoprint.run
octoprint.service
- octoprint-tornado401.patch
- )
-md5sums=('SKIP'
- '3bee9901c9eabed94b7f9236f83bf053'
- 'ec5e51f876bb5fb223801bf28850908a'
- '88d6fb3a61a4ab990fb6cca39c830a6c'
- )
-
-pkgver_git() {
- cd "${srcdir}/${pkgname}"
- #local ver="$(git show | grep commit | awk '{print $2}' )"
- #printf "r%s" "${ver//[[:alpha:]]}"
- echo "r$(git rev-list --count HEAD).$(git rev-parse --short HEAD)"
-}
-
-pkgver_svn() {
- cd "${srcdir}/${pkgname}"
- local ver="$(svn info | grep Revision | awk '{print $2}' )"
- #printf "r%s" "${ver//[[:alpha:]]}"
- echo ${ver:0:7}
-}
+ octoprint-deps.patch
+ octoprint-jinja29.patch
+)
+sha512sums=('df22247796b46f270f7525777194e983a12be3c5f719ffc4ed864d6012d2050b813df9e8f9ee95bdc7687838c96f7c102327a7b802d42abd84481ddd77c8ebea'
+ '5c22c76e4089958ff42e2627f29c360fa0f9a73b849f1fe5092cdd9ae800323263b75ac7e23d7189badac9baa7daa850183bf2491680a0ac01c605531728da62'
+ '89a8703cbc8b8802eb6a360359f92904357a99bf5b1174c85d4555f88a816d4c008d077fe4f3fed4db0b23e8a662359bcfafaf7750a2ed50a81104f04d04c056'
+ 'cb4190c0a24499fdac2d48e89ba20229ac7d11df0d6bb56b8930fadde3595e70387868374d93c49aa0230b276f7bc061969ca64d92de995aea403150d156969f'
+ '41e03bcc2111d1000ca0d62634009dc2030bae52b6623cd58a595f570e0f99e524613d52785b50098fe044fe5355b26221ce7a286148d9bdd95fb3f220bb5568')
-#pkgver() {
-# pkgver_git
-#}
prepare() {
- cd ${srcdir}/OctoPrint-${pkgver}
+ cd ${srcdir}/${_reponame}-${pkgver}
- # check the version of python2-tornado required by the OctoPrint
- # if it not python2-tornado 4.0.1, we need to switch back to new version!
- # If use the latest tornado, you don't need to patch the octoprint.
- VER=$(grep -Hrn '"tornado==' setup.py | awk -F= '{print $3}' | awk -F\" '{print $1}')
- VAL=$(echo $VER | awk -F. '{print 10000 * $1 + 100 * $2 + $3}')
- if (( $VAL <= 40001 )) ; then
- patch -p1 < "$srcdir/octoprint-tornado401.patch"
- else
- echo "The required version of tornado is now changed." 1>&2
- fi
+ patch -p1 < "$srcdir/octoprint-deps.patch"
+ patch -p1 < "$srcdir/octoprint-jinja29.patch"
}
package() {
- #cd ${srcdir}/${pkgname}
- cd ${srcdir}/OctoPrint-${pkgver}
+ cd ${srcdir}/${_reponame}-${pkgver}
python2 setup.py install --root="$pkgdir/" --optimize=1
- mkdir -p ${pkgdir}/usr/share/octoprint/
- cp -a * ${pkgdir}/usr/share/octoprint/
- #install -D -m755 run ${pkgdir}/usr/share/octoprint/run
- rm -rf ${pkgdir}/usr/share/octoprint/src/
- rm -rf ${pkgdir}/usr/share/octoprint/build/
- rm -rf ${pkgdir}/usr/share/octoprint/docs/
- rm -rf ${pkgdir}/usr/share/octoprint/tests/
- rm -rf ${pkgdir}/usr/share/octoprint/translations/
-
install -D -m755 ${srcdir}/octoprint.run ${pkgdir}/usr/bin/octoprint
install -D -m644 ${srcdir}/octoprint.service ${pkgdir}/usr/lib/systemd/system/octoprint.service
}
diff --git a/octoprint-deps.patch b/octoprint-deps.patch
new file mode 100644
index 000000000000..73e2fde4e003
--- /dev/null
+++ b/octoprint-deps.patch
@@ -0,0 +1,641 @@
+diff --git a/setup.py b/setup.py
+index c6eabbd0..24ed0f8b 100644
+--- a/setup.py
++++ b/setup.py
+@@ -14,34 +14,34 @@ import octoprint_setuptools
+
+ # Requirements for our application
+ INSTALL_REQUIRES = [
+- "flask>=0.9,<0.11",
++ "flask>=0.11,<0.12",
+ "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
+ # 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
++ "werkzeug>=0.11.1,<0.12",
++ "tornado>=4.4.2,<4.5", # 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",
+- "PyYAML>=3.10,<3.11",
+- "Flask-Login>=0.2.2,<0.3",
+- "Flask-Principal>=0.3.5,<0.4",
+- "Flask-Babel>=0.9,<0.10",
+- "Flask-Assets>=0.10,<0.11",
++ "PyYAML>=3.12,<3.13",
++ "Flask-Login>=0.3,<0.4",
++ "Flask-Principal>=0.4,<0.5",
++ "Flask-Babel>=0.11,<0.12",
++ "Flask-Assets>=0.12,<0.13",
+ "markdown>=2.6.4,<2.7",
+- "pyserial>=2.7,<2.8",
++ "pyserial>=3.1.1,<3.2",
+ "netaddr>=0.7.17,<0.8",
+ "watchdog>=0.8.3,<0.9",
+ "sarge>=0.1.4,<0.2",
+ "netifaces>=0.10,<0.11",
+ "pylru>=1.0.9,<1.1",
+- "rsa>=3.2,<3.3",
+- "pkginfo>=1.2.1,<1.3",
+- "requests>=2.7,<2.8",
+- "semantic_version>=2.4.2,<2.5",
+- "psutil>=3.2.1,<3.3",
+- "Click>=6.2,<6.3",
++ "rsa>=3.4,<3.5",
++ "pkginfo>=1.3.2,<1.4",
++ "requests>=2.11.1,<2.12",
++ "semantic_version>=2.6.0,<2.7",
++ "psutil>=4.3.1,<4.4",
++ "Click>=6.6,<6.7",
+ "awesome-slugify>=1.6.5,<1.7",
+ "feedparser>=5.2.1,<5.3",
+ "chainmap>=1.0.2,<1.1",
+diff --git a/src/octoprint/plugin/types.py b/src/octoprint/plugin/types.py
+index 098688f6..be69dbc6 100644
+--- a/src/octoprint/plugin/types.py
++++ b/src/octoprint/plugin/types.py
+@@ -1417,7 +1417,7 @@ class SettingsPlugin(OctoPrintPlugin):
+
+ :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
+
+ data = copy.deepcopy(self._settings.get_all_data(merged=True))
+@@ -1462,8 +1462,8 @@ class SettingsPlugin(OctoPrintPlugin):
+ else:
+ node[key] = None
+
+- 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(),
++ 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,
+ never=lambda: False)
+
+ for level, condition in conditions.items():
+@@ -1937,4 +1937,3 @@ class AppPlugin(OctoPrintPlugin):
+
+ def get_additional_apps(self):
+ return []
+-
+diff --git a/src/octoprint/plugins/announcements/__init__.py b/src/octoprint/plugins/announcements/__init__.py
+index 74bc192a..7f73efb2 100644
+--- a/src/octoprint/plugins/announcements/__init__.py
++++ b/src/octoprint/plugins/announcements/__init__.py
+@@ -22,7 +22,7 @@ from collections import OrderedDict
+
+ from octoprint.server import admin_permission
+ 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,
+ octoprint.plugin.SettingsPlugin,
+diff --git a/src/octoprint/plugins/corewizard/__init__.py b/src/octoprint/plugins/corewizard/__init__.py
+index d9c61f67..77c89a05 100644
+--- a/src/octoprint/plugins/corewizard/__init__.py
++++ b/src/octoprint/plugins/corewizard/__init__.py
+@@ -9,7 +9,7 @@ __copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms
+ import octoprint.plugin
+
+
+-from flask.ext.babel import gettext
++from flask_babel import gettext
+
+
+ class CoreWizardPlugin(octoprint.plugin.AssetPlugin,
+diff --git a/src/octoprint/plugins/discovery/__init__.py b/src/octoprint/plugins/discovery/__init__.py
+index 988d4840..7d5b5f44 100644
+--- a/src/octoprint/plugins/discovery/__init__.py
++++ b/src/octoprint/plugins/discovery/__init__.py
+@@ -12,7 +12,7 @@ The SSDP/UPNP implementations has been largely inspired by https://gist.github.c
+ import logging
+ import os
+ import flask
+-from flask.ext.babel import gettext
++from flask_babel import gettext
+ from builtins import range
+
+ import octoprint.plugin
+diff --git a/src/octoprint/plugins/pluginmanager/__init__.py b/src/octoprint/plugins/pluginmanager/__init__.py
+index 47365b2e..03b81a40 100644
+--- a/src/octoprint/plugins/pluginmanager/__init__.py
++++ b/src/octoprint/plugins/pluginmanager/__init__.py
+@@ -15,7 +15,7 @@ from octoprint.server import admin_permission, VERSION
+ from octoprint.util.pip import LocalPipCaller, UnknownPip
+
+ from flask import jsonify, make_response
+-from flask.ext.babel import gettext
++from flask_babel import gettext
+ from collections import OrderedDict
+
+ import logging
+diff --git a/src/octoprint/plugins/softwareupdate/__init__.py b/src/octoprint/plugins/softwareupdate/__init__.py
+index 507c5593..bd46a054 100644
+--- a/src/octoprint/plugins/softwareupdate/__init__.py
++++ b/src/octoprint/plugins/softwareupdate/__init__.py
+@@ -18,7 +18,7 @@ import hashlib
+
+ from . import version_checks, updaters, exceptions, util, cli
+
+-from flask.ext.babel import gettext
++from flask_babel import gettext
+
+ from octoprint.server.util.flask import restricted_access, with_revalidation_checking, check_etag
+ from octoprint.server import admin_permission, VERSION, REVISION, BRANCH
+@@ -549,7 +549,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
+ ##~~ TemplatePlugin API
+
+ def get_template_configs(self):
+- from flask.ext.babel import gettext
++ from flask_babel import gettext
+ return [
+ dict(type="settings", name=gettext("Software Update"))
+ ]
+@@ -882,7 +882,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
+ result = dict(check)
+
+ if target == "octoprint":
+- from flask.ext.babel import gettext
++ from flask_babel import gettext
+
+ result["displayName"] = to_unicode(check.get("displayName"), errors="replace")
+ if result["displayName"] is None:
+@@ -1074,5 +1074,3 @@ def __plugin_load__():
+ __plugin_hooks__ = {
+ "octoprint.cli.commands": cli.commands
+ }
+-
+-
+diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py
+index 94b9bab5..e418d594 100644
+--- a/src/octoprint/server/__init__.py
++++ b/src/octoprint/server/__init__.py
+@@ -8,10 +8,10 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
+ import uuid
+ from sockjs.tornado import SockJSRouter
+ from flask import Flask, g, request, session, Blueprint, Request, Response
+-from flask.ext.login import LoginManager, current_user
+-from flask.ext.principal import Principal, Permission, RoleNeed, identity_loaded, UserNeed
+-from flask.ext.babel import Babel, gettext, ngettext
+-from flask.ext.assets import Environment, Bundle
++from flask_login import LoginManager, current_user
++from flask_principal import Principal, Permission, RoleNeed, identity_loaded, UserNeed
++from flask_babel import Babel, gettext, ngettext
++from flask_assets import Environment, Bundle
+ from babel import Locale
+ from watchdog.observers import Observer
+ from watchdog.observers.polling import PollingObserver
+@@ -89,9 +89,9 @@ def on_identity_loaded(sender, identity):
+ return
+
+ identity.provides.add(UserNeed(user.get_id()))
+- if user.is_user():
++ if user.is_user:
+ identity.provides.add(RoleNeed("user"))
+- if user.is_admin():
++ if user.is_admin:
+ identity.provides.add(RoleNeed("admin"))
+
+ def load_user(id):
+@@ -170,8 +170,7 @@ class Server(object):
+ self._logger = logging.getLogger(__name__)
+ pluginManager = self._plugin_manager
+
+- # monkey patch a bunch of stuff
+- util.tornado.fix_ioloop_scheduling()
++ # monkey patch some stuff
+ util.flask.enable_additional_translations(additional_folders=[self._settings.getBaseFolder("translations")])
+
+ # setup app
+diff --git a/src/octoprint/server/api/__init__.py b/src/octoprint/server/api/__init__.py
+index 35401e45..6d056fe0 100644
+--- a/src/octoprint/server/api/__init__.py
++++ b/src/octoprint/server/api/__init__.py
+@@ -10,8 +10,8 @@ import netaddr
+ import sarge
+
+ 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.ext.principal import Identity, identity_changed, AnonymousIdentity
++from flask_login import login_user, logout_user, current_user
++from flask_principal import Identity, identity_changed, AnonymousIdentity
+
+ import octoprint.util as util
+ 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)
+
+ 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)
+
+ response = api_plugin.on_api_get(request)
+@@ -89,7 +89,7 @@ def pluginCommand(name):
+ if valid_commands is None:
+ 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)
+
+ command, data, response = get_json_command_from_request(request, valid_commands)
+diff --git a/src/octoprint/server/api/languages.py b/src/octoprint/server/api/languages.py
+index e0749658..3d5f1f2c 100644
+--- a/src/octoprint/server/api/languages.py
++++ b/src/octoprint/server/api/languages.py
+@@ -26,7 +26,7 @@ from octoprint.server.util.flask import restricted_access
+
+ from octoprint.plugin import plugin_manager
+
+-from flask.ext.babel import Locale
++from flask_babel import Locale
+
+ @api.route("/languages", methods=["GET"])
+ @restricted_access
+diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py
+index 02880093..b53e889f 100644
+--- a/src/octoprint/server/api/settings.py
++++ b/src/octoprint/server/api/settings.py
+@@ -8,7 +8,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
+ import logging
+
+ 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 octoprint.events import eventManager, Events
+@@ -39,7 +39,7 @@ def _etag(lm=None):
+ for key in sorted(plugin_settings.keys()):
+ 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)
+ else:
+ roles = []
+diff --git a/src/octoprint/server/api/system.py b/src/octoprint/server/api/system.py
+index 058476d3..14a0e084 100644
+--- a/src/octoprint/server/api/system.py
++++ b/src/octoprint/server/api/system.py
+@@ -9,7 +9,7 @@ import logging
+ import sarge
+
+ 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
+
+diff --git a/src/octoprint/server/api/users.py b/src/octoprint/server/api/users.py
+index 73233f5e..fbee5c16 100644
+--- a/src/octoprint/server/api/users.py
++++ b/src/octoprint/server/api/users.py
+@@ -7,7 +7,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
+
+ from flask import request, jsonify, abort, make_response
+ from werkzeug.exceptions import BadRequest
+-from flask.ext.login import current_user
++from flask_login import current_user
+
+ import octoprint.users as users
+
+@@ -72,7 +72,7 @@ def getUser(username):
+ if not userManager.enabled:
+ 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)
+ if user is not None:
+ return jsonify(user.asDict())
+@@ -133,7 +133,7 @@ def changePasswordForUser(username):
+ if not userManager.enabled:
+ 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"]:
+ return make_response("Expected content-type JSON", 400)
+
+@@ -161,7 +161,7 @@ def getSettingsForUser(username):
+ if not userManager.enabled:
+ 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)
+
+ try:
+@@ -175,7 +175,7 @@ def changeSettingsForUser(username):
+ if not userManager.enabled:
+ 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)
+
+ try:
+@@ -195,7 +195,7 @@ def deleteApikeyForUser(username):
+ if not userManager.enabled:
+ 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:
+ userManager.deleteApikey(username)
+ except users.UnknownUser:
+@@ -211,7 +211,7 @@ def generateApikeyForUser(username):
+ if not userManager.enabled:
+ 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:
+ apikey = userManager.generateApiKey(username)
+ except users.UnknownUser:
+diff --git a/src/octoprint/server/util/__init__.py b/src/octoprint/server/util/__init__.py
+index 5d202d16..0a084325 100644
+--- a/src/octoprint/server/util/__init__.py
++++ b/src/octoprint/server/util/__init__.py
+@@ -13,6 +13,7 @@ from octoprint.users import ApiUser
+ from octoprint.util import deprecated
+
+ import flask as _flask
++import flask_login
+
+ from . import flask
+ from . import sockjs
+@@ -58,7 +59,7 @@ def loginFromApiKeyRequestHandler():
+
+ if apikey and apikey != octoprint.server.UI_API_KEY and not octoprint.server.appSessionManager.validate(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 flask_login.login_user(user, remember=False):
+ _flask.ext.principal.identity_changed.send(_flask.current_app._get_current_object(),
+ identity=_flask.ext.principal.Identity(user.get_id()))
+ else:
+diff --git a/src/octoprint/server/util/flask.py b/src/octoprint/server/util/flask.py
+index 5f41d661..9fc0defd 100644
+--- a/src/octoprint/server/util/flask.py
++++ b/src/octoprint/server/util/flask.py
+@@ -8,9 +8,9 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
+
+ import tornado.web
+ import flask
+-import flask.ext.login
+-import flask.ext.principal
+-import flask.ext.assets
++import flask_login
++import flask_principal
++import flask_assets
+ import webassets.updater
+ import webassets.utils
+ import functools
+@@ -40,7 +40,7 @@ def enable_additional_translations(default_locale="en", additional_folders=None)
+ import os
+ from flask import _request_ctx_stack
+ from babel import support, Locale
+- import flask.ext.babel
++ import flask_babel
+
+ if additional_folders is None:
+ additional_folders = []
+@@ -91,7 +91,7 @@ def enable_additional_translations(default_locale="en", additional_folders=None)
+ return None
+ translations = getattr(ctx, 'babel_translations', None)
+ if translations is None:
+- locale = flask.ext.babel.get_locale()
++ locale = flask_babel.get_locale()
+ translations = support.Translations()
+
+ if str(locale) != default_locale:
+@@ -129,8 +129,8 @@ def enable_additional_translations(default_locale="en", additional_folders=None)
+ ctx.babel_translations = translations
+ return translations
+
+- flask.ext.babel.Babel.list_translations = fixed_list_translations
+- flask.ext.babel.get_translations = fixed_get_translations
++ flask_babel.Babel.list_translations = fixed_list_translations
++ flask_babel.get_translations = fixed_get_translations
+
+ def fix_webassets_cache():
+ from webassets import cache
+@@ -478,12 +478,12 @@ class OctoPrintFlaskResponse(flask.Response):
+
+ def passive_login():
+ 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:
+- user = flask.ext.login.current_user
++ user = flask_login.current_user
+
+- 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()))
++ if user is not None and not user.is_anonymous:
++ flask_principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask_principal.Identity(user.get_id()))
+ if hasattr(user, "get_session"):
+ flask.session["usersession.id"] = user.get_session()
+ flask.g.user = user
+@@ -505,8 +505,8 @@ def passive_login():
+ user = octoprint.server.userManager.login_user(user)
+ flask.session["usersession.id"] = user.get_session()
+ flask.g.user = user
+- flask.ext.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_login.login_user(user)
++ flask_principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask_principal.Identity(user.get_id()))
+ return flask.jsonify(user.asDict())
+ except:
+ logger = logging.getLogger(__name__)
+@@ -1023,7 +1023,7 @@ def admin_validator(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)
+
+
+@@ -1038,7 +1038,7 @@ def user_validator(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)
+
+
+@@ -1051,14 +1051,14 @@ def _get_flask_user_from_request(request):
+ :return: the user or None if no user could be determined
+ """
+ import octoprint.server.util
+- import flask.ext.login
++ import flask_login
+ from octoprint.settings import settings
+
+ apikey = octoprint.server.util.get_api_key(request)
+ if settings().getBoolean(["api", "enabled"]) and apikey is not None:
+ user = octoprint.server.util.get_user_for_apikey(apikey)
+ else:
+- user = flask.ext.login.current_user
++ user = flask_login.current_user
+
+ return user
+
+@@ -1103,7 +1103,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()):
+ 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
+
+@@ -1201,7 +1201,7 @@ def get_json_command_from_request(request, valid_commands):
+
+ ##~~ Flask-Assets resolver with plugin asset support
+
+-class PluginAssetResolver(flask.ext.assets.FlaskResolver):
++class PluginAssetResolver(flask_assets.FlaskResolver):
+
+ def split_prefix(self, ctx, item):
+ app = ctx.environment._app
+@@ -1210,14 +1210,14 @@ class PluginAssetResolver(flask.ext.assets.FlaskResolver):
+ prefix, plugin, name = item.split("/", 2)
+ blueprint = prefix + "." + plugin
+
+- directory = flask.ext.assets.get_static_folder(app.blueprints[blueprint])
++ directory = flask_assets.get_static_folder(app.blueprints[blueprint])
+ item = name
+ endpoint = blueprint + ".static"
+ return directory, item, endpoint
+ except (ValueError, KeyError):
+ 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):
+ import os
+diff --git a/src/octoprint/server/util/tornado.py b/src/octoprint/server/util/tornado.py
+index 12ba3b9b..8325bcaf 100644
+--- a/src/octoprint/server/util/tornado.py
++++ b/src/octoprint/server/util/tornado.py
+@@ -29,36 +29,6 @@ import tornado.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
+
+
+@@ -672,8 +642,10 @@ 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.
+ """
++ def __init__(self, *args, **kwargs):
++ pass
+
+- def __init__(self, request_callback, no_keep_alive=False, io_loop=None,
++ def initialize(self, request_callback, no_keep_alive=False, io_loop=None,
+ xheaders=False, ssl_options=None, protocol=None,
+ decompress_request=False,
+ chunk_size=None, max_header_size=None,
+diff --git a/src/octoprint/users.py b/src/octoprint/users.py
+index cec5a48f..a99e0582 100644
+--- a/src/octoprint/users.py
++++ b/src/octoprint/users.py
+@@ -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'
+ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License"
+
+-from flask.ext.login import UserMixin
+-from flask.ext.principal import Identity
++from flask_login import UserMixin
++from flask_principal import Identity
+ from werkzeug.local import LocalProxy
+ import hashlib
+ import os
+@@ -431,9 +431,9 @@ class User(UserMixin):
+ def asDict(self):
+ return {
+ "name": self._username,
+- "active": self.is_active(),
+- "admin": self.is_admin(),
+- "user": self.is_user(),
++ "active": self.is_active,
++ "admin": self.is_admin,
++ "user": self.is_user,
+ "apikey": self._apikey,
+ "settings": self._settings
+ }
+@@ -447,12 +447,15 @@ class User(UserMixin):
+ def get_name(self):
+ return self._username
+
++ @property
+ def is_active(self):
+ return self._active
+
++ @property
+ def is_user(self):
+ return "user" in self._roles
+
++ @property
+ def is_admin(self):
+ return "admin" in self._roles
+
+@@ -503,7 +506,7 @@ class User(UserMixin):
+ return True
+
+ 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(), self.is_active, self.is_user, self.is_admin)
+
+ class SessionUser(User):
+ def __init__(self, user):
+@@ -535,7 +538,7 @@ class SessionUser(User):
+ self._user = user
+
+ 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(), self.is_active, self.is_user, self.is_admin, self._session, self._created)
+
+ ##~~ DummyUser object to use when accessControl is disabled
+
diff --git a/octoprint-jinja29.patch b/octoprint-jinja29.patch
new file mode 100644
index 000000000000..26b6d21a6361
--- /dev/null
+++ b/octoprint-jinja29.patch
@@ -0,0 +1,148 @@
+diff --git a/src/octoprint/templates/dialogs/about.jinja2 b/src/octoprint/templates/dialogs/about.jinja2
+index dc94bb2c..0d484c99 100644
+--- a/src/octoprint/templates/dialogs/about.jinja2
++++ b/src/octoprint/templates/dialogs/about.jinja2
+@@ -8,7 +8,7 @@
+ <div class="tabbable row-fluid">
+ <div class="span3 scrollable" id="about_dialog_menu">
+ <ul class="nav nav-list" id="about_dialog_tabs">
+- {% set mark_active = True %}
++ {% set mark_active = [True] %}
+ {% for key in templates.about.order %}
+ {% set entry, data = templates.about.entries[key] %}
+ {% if data is none %}
+@@ -17,7 +17,7 @@
+ {% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
+ <li id="{{ data._div }}_link"
+ {% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
+- class="{% if mark_active %}active{% set mark_active = False %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
++ class="{% if mark_active[0] %}active{% do mark_active.pop() %}{% do mark_active.append(False) %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
+ {% if "styles_link" in data %} style="{{ data.styles_link|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}" {% endif %}
+ >
+ <a href="#{{ data._div }}" data-toggle="tab">{{ entry }}</a>
+@@ -28,14 +28,14 @@
+ </ul>
+ </div>
+ <div class="tab-content span9 scrollable" id="about_dialog_content">
+- {% set mark_active = True %}
++ {% set mark_active = [True] %}
+ {% for key in templates.about.order %}
+ {% set entry, data = templates.about.entries[key] %}
+ {% if data is not none %}
+ {% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
+ <div id="{{ data._div }}"
+ {% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
+- class="tab-pane {% if mark_active %}active{% set mark_active = False %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
++ class="tab-pane {% if mark_active[0] %}active{% do mark_active.pop() %}{% do mark_active.append(False) %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
+ {% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif styles in data %} style="{{ data.styles|join(', ') }}" {% endif %}
+ >
+ {% try "There was an error with the template {filename} at line number {lineno}: {exception}" %}{% include data.template ignore missing %}{% endtry %}
+diff --git a/src/octoprint/templates/dialogs/settings.jinja2 b/src/octoprint/templates/dialogs/settings.jinja2
+index 62790a0a..e86a1367 100644
+--- a/src/octoprint/templates/dialogs/settings.jinja2
++++ b/src/octoprint/templates/dialogs/settings.jinja2
+@@ -8,7 +8,7 @@
+ <div class="tabbable row-fluid">
+ <div class="span3 scrollable" id="settings_dialog_menu">
+ <ul class="nav nav-list" id="settingsTabs">
+- {% set mark_active = True %}
++ {% set mark_active = [True] %}
+ {% for key in templates.settings.order %}
+ {% set entry, data = templates.settings.entries[key] %}
+ {% if data is none %}
+@@ -17,7 +17,7 @@
+ {% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
+ <li id="{{ data._div }}_link"
+ {% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
+- class="{% if mark_active %}active{% set mark_active = False %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
++ class="{% if mark_active[0] %}active{% do mark_active.pop() %}{% do mark_active.append(False) %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
+ {% if "styles_link" in data %} style="{{ data.styles_link|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}" {% endif %}
+ >
+ <a href="#{{ data._div }}" data-toggle="tab">{{ entry|e }}</a>
+@@ -28,7 +28,7 @@
+ </ul>
+ </div>
+ <div class="tab-content span9 scrollable" id="settings_dialog_content">
+- {% set mark_active = True %}
++ {% set mark_active = [True] %}
+ {% for key in templates.settings.order %}
+ {% set entry, data = templates.settings.entries[key] %}
+ {% if data is not none %}
+diff --git a/src/octoprint/templates/dialogs/usersettings.jinja2 b/src/octoprint/templates/dialogs/usersettings.jinja2
+index d34da7b4..770c173b 100644
+--- a/src/octoprint/templates/dialogs/usersettings.jinja2
++++ b/src/octoprint/templates/dialogs/usersettings.jinja2
+@@ -5,7 +5,7 @@
+ </div>
+ <div class="modal-body">
+ <ul class="nav nav-pills">
+- {% set mark_active = True %}
++ {% set mark_active = [True] %}
+ {% for key in templates.usersettings.order %}
+ {% set entry, data = templates.usersettings.entries[key] %}
+ {% if data is none %}
+@@ -14,7 +14,7 @@
+ {% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
+ <li id="{{ data._div }}_link"
+ {% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
+- class="{% if mark_active %}active{% set mark_active = False %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
++ class="{% if mark_active[0] %}active{% do mark_active.pop() %}{% do mark_active.append(False) %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
+ {% if "styles_link" in data %} style="{{ data.styles_link|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}" {% endif %}
+ >
+ <a href="#{{ data._div }}" data-toggle="tab">{{ entry|e }}</a>
+@@ -25,14 +25,14 @@
+ </ul>
+
+ <div class="tab-content">
+- {% set mark_active = True %}
++ {% set mark_active = [True] %}
+ {% for key in templates.usersettings.order %}
+ {% set entry, data = templates.usersettings.entries[key] %}
+ {% if data is not none %}
+ {% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
+ <div id="{{ data._div }}"
+ {% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
+- class="tab-pane {% if mark_active %}active{% set mark_active = False %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
++ class="tab-pane {% if mark_active[0] %}active{% do mark_active.pop() %}{% do mark_active.append(False) %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
+ {% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif styles in data %} style="{{ data.styles|join(', ') }}" {% endif %}
+ >
+ {% try "There was an error with the template {filename} at line number {lineno}: {exception}" %}{% include data.template ignore missing %}{% endtry %}
+diff --git a/src/octoprint/templates/dialogs/wizard.jinja2 b/src/octoprint/templates/dialogs/wizard.jinja2
+index c0023a28..41fcc6bd 100644
+--- a/src/octoprint/templates/dialogs/wizard.jinja2
++++ b/src/octoprint/templates/dialogs/wizard.jinja2
+@@ -7,7 +7,7 @@
+ <div class="tabbable row-fluid">
+ <div class="span3 scrollable" id="wizard_dialog_menu">
+ <ul class="nav nav-list" id="wizardTabs">
+- {% set mark_active = True %}
++ {% set mark_active = [True] %}
+ {% for key in templates.wizard.order %}
+ {% set entry, data = templates.wizard.entries[key] %}
+ {% if data is none %}
+@@ -16,7 +16,7 @@
+ {% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
+ <li id="{{ data._div }}_link"
+ {% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
+- class="{% if mark_active %}active{% set mark_active = False %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
++ class="{% if mark_active[0] %}active{% do mark_active.pop() %}{% do mark_active.append(False) %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
+ {% if "styles_link" in data %} style="{{ data.styles_link|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}" {% endif %}
+ >
+ <a href="#{{ data._div }}" data-toggle="tab">{% if "mandatory" in data and data.mandatory %}<strong>{{ entry }}</strong>{% else %}{{ entry }}{% endif %}</a>
+@@ -27,14 +27,14 @@
+ </ul>
+ </div>
+ <div class="tab-content span9 scrollable" id="wizard_dialog_content">
+- {% set mark_active = True %}
++ {% set mark_active = [True] %}
+ {% for key in templates.wizard.order %}
+ {% set entry, data = templates.wizard.entries[key] %}
+ {% if data is not none %}
+ {% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
+ <div id="{{ data._div }}"
+ {% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
+- class="tab-pane {% if mark_active %}active{% set mark_active = False %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
++ class="tab-pane {% if mark_active %}active{% do mark_active.pop() %}{% do mark_active.append(False) %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
+ {% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif styles in data %} style="{{ data.styles|join(', ') }}" {% endif %}
+ >
+ {% if "mandatory" in data and data.mandatory %}
diff --git a/octoprint.install b/octoprint.install
index cdb6d888c8b7..bf46ee24163c 100755
--- a/octoprint.install
+++ b/octoprint.install
@@ -1,7 +1,7 @@
post_install() {
local HOMEDIR=/var/lib/octoprint
- getent group octoprint > /dev/null || groupadd octoprint
- getent passwd octoprint > /dev/null || useradd -d $HOMEDIR -g octoprint -s /usr/bin/nologin octoprint
+ getent group octoprint > /dev/null || groupadd -r octoprint
+ getent passwd octoprint > /dev/null || useradd -d $HOMEDIR -r -g octoprint -s /usr/bin/nologin octoprint
usermod -a -G octoprint,network,uucp,tty octoprint
mkdir -p $HOMEDIR
chmod 700 $HOMEDIR
diff --git a/octoprint.run b/octoprint.run
index eebe74ef7cc4..f5bc4b64b86d 100644
--- a/octoprint.run
+++ b/octoprint.run
@@ -1,3 +1,4 @@
-#!/usr/bin/env sh
+#!/usr/bin/env python2
-exec /usr/bin/env python2 /usr/share/octoprint/run $@
+import octoprint
+octoprint.main()
diff --git a/octoprint.service b/octoprint.service
index b064adab9fcd..b358aecc54ec 100644
--- a/octoprint.service
+++ b/octoprint.service
@@ -1,13 +1,12 @@
[Unit]
-Description=Octoprint 3d Printing Web Server
+Description=Responsive web interface for controlling a 3D printer
After=network.target
[Service]
User=octoprint
Group=octoprint
Type=simple
-ExecStart=/usr/bin/octoprint
-Restart=on-failure
+ExecStart=/usr/bin/octoprint serve
[Install]
WantedBy=multi-user.target