Webassets are now written to ~/.octoprint/generated

Same holds true for cache files, so there should be no problem anymore with installs where the static folder is not writable.

Also introduced two new devel config vars to disable merging and minifying of the assets, solved the empty-less-bundle problem and made sure babel knows about the jinja extension.
This commit is contained in:
Gina Häußge 2015-06-03 18:27:30 +02:00
parent 55650cd416
commit b63f842210
7 changed files with 78 additions and 53 deletions

View file

@ -1,7 +1,7 @@
[python: src/octoprint/**.py] [python: src/octoprint/**.py]
[jinja2: src/octoprint/templates/**.jinja2] [jinja2: src/octoprint/templates/**.jinja2]
[jinja2: src/octoprint/plugins/**.jinja2] [jinja2: src/octoprint/plugins/**.jinja2]
extensions=jinja2.ext.autoescape, jinja2.ext.with_ extensions=jinja2.ext.autoescape, jinja2.ext.with_, webassets.ext.jinja2.AssetsExtension
[javascript: src/octoprint/static/js/app/**.js] [javascript: src/octoprint/static/js/app/**.js]
[javascript: src/octoprint/plugins/**.js] [javascript: src/octoprint/plugins/**.js]

View file

@ -290,11 +290,6 @@ class Server():
# register API blueprint # register API blueprint
self._setup_blueprints() self._setup_blueprints()
def blueprint_enabled(name, plugin):
if plugin.implementation is None or not isinstance(plugin.implementation, octoprint.plugin.BlueprintPlugin):
return
self._register_blueprint_plugin(plugin.implementation)
pluginLifecycleManager.add_callback(["enabled"], blueprint_enabled)
## Tornado initialization starts here ## Tornado initialization starts here
@ -313,7 +308,7 @@ class Server():
# camera snapshot # camera snapshot
(r"/downloads/camera/current", util.tornado.UrlForwardHandler, dict(url=s.get(["webcam", "snapshot"]), as_attachment=True, access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.user_validator))), (r"/downloads/camera/current", util.tornado.UrlForwardHandler, dict(url=s.get(["webcam", "snapshot"]), as_attachment=True, access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.user_validator))),
# generated webassets # generated webassets
(r"/static/webassets/(.*)", util.tornado.LargeResponseHandler, dict(path=s.getBaseFolder("webassets"))) (r"/static/webassets/(.*)", util.tornado.LargeResponseHandler, dict(path=os.path.join(s.getBaseFolder("generated"), "webassets")))
] ]
for name, hook in pluginManager.get_hooks("octoprint.server.http.routes").items(): for name, hook in pluginManager.get_hooks("octoprint.server.http.routes").items():
try: try:
@ -667,12 +662,28 @@ class Server():
global assets global assets
global pluginManager global pluginManager
base_folder = settings().getBaseFolder("generated")
AdjustedEnvironment = type(Environment)(Environment.__name__, (Environment,), dict( AdjustedEnvironment = type(Environment)(Environment.__name__, (Environment,), dict(
resolver_class=util.flask.PluginAssetResolver resolver_class=util.flask.PluginAssetResolver
)) ))
assets = AdjustedEnvironment(app) class CustomDirectoryEnvironment(AdjustedEnvironment):
@property
def directory(self):
return base_folder
dynamic_assets = util.flask.collect_plugin_assets() assets = CustomDirectoryEnvironment(app)
assets.debug = not settings().getBoolean(["devel", "webassets", "bundle"])
enable_gcodeviewer = settings().getBoolean(["gcodeViewer", "enabled"])
enable_timelapse = (settings().get(["webcam", "snapshot"]) and settings().get(["webcam", "ffmpeg"]))
preferred_stylesheet = settings().get(["devel", "stylesheet"])
dynamic_assets = util.flask.collect_plugin_assets(
enable_gcodeviewer=enable_gcodeviewer,
enable_timelapse=enable_timelapse,
preferred_stylesheet=preferred_stylesheet
)
js_libs = [ js_libs = [
"js/lib/jquery/jquery.min.js", "js/lib/jquery/jquery.min.js",
@ -696,16 +707,15 @@ class Server():
"js/lib/jquery/jquery.fileupload.js", "js/lib/jquery/jquery.fileupload.js",
"js/lib/jquery/jquery.slimscroll.min.js", "js/lib/jquery/jquery.slimscroll.min.js",
"js/lib/jquery/jquery.qrcode.min.js", "js/lib/jquery/jquery.qrcode.min.js",
"js/lib/sockjs-0.3.4.min.js",
"js/lib/moment-with-locales.min.js", "js/lib/moment-with-locales.min.js",
"js/lib/pusher.color.min.js", "js/lib/pusher.color.min.js",
"js/lib/detectmobilebrowser.js", "js/lib/detectmobilebrowser.js",
"js/lib/md5.min.js", "js/lib/md5.min.js",
"js/lib/pnotify.min.js", "js/lib/pnotify.min.js",
"js/lib/bootstrap-slider-knockout-binding.js", "js/lib/bootstrap-slider-knockout-binding.js",
"js/lib/loglevel.min.js" "js/lib/loglevel.min.js",
"js/lib/sockjs-0.3.4.min.js"
] ]
js_app = dynamic_assets["js"] + [ js_app = dynamic_assets["js"] + [
"js/app/dataupdater.js", "js/app/dataupdater.js",
"js/app/helpers.js", "js/app/helpers.js",
@ -721,29 +731,24 @@ class Server():
"css/jquery.fileupload-ui.css", "css/jquery.fileupload-ui.css",
"css/pnotify.min.css" "css/pnotify.min.css"
] ]
css_app = ["empty"] + dynamic_assets["css"]
css_app = [] less_app = ["empty"] + dynamic_assets["less"]
less_app = []
for sheet, path in dynamic_assets["stylesheets"]:
if sheet == "css":
css_app.append(path)
elif sheet == "less":
less_app.append(path)
js_libs_bundle = Bundle(*js_libs, output="webassets/packed_libs.js") js_libs_bundle = Bundle(*js_libs, output="webassets/packed_libs.js")
js_app_bundle = Bundle(*js_app, output="webassets/package_app.js") if settings().getBoolean(["devel", "webassets", "minify"]):
js_app_bundle = Bundle(*js_app, output="webassets/package_app.js", filters="rjsmin")
else:
js_app_bundle = Bundle(*js_app, output="webassets/package_app.js")
all_js_bundle = Bundle(js_libs_bundle, js_app_bundle, output="webassets/packed.js")
css_libs_bundle = Bundle(*css_libs, output="webassets/packed_libs.css") css_libs_bundle = Bundle(*css_libs, output="webassets/packed_libs.css")
css_app_bundle = Bundle(*css_app, output="webassets/packed_app.css")
all_css_bundle = Bundle(css_libs_bundle, css_app_bundle, output="webassets/packed.css")
all_less_bundle = Bundle(*less_app, output="webassets/packed_app.less")
assets.register("js_libs", js_libs_bundle) assets.register("all_js", all_js_bundle)
assets.register("js_app", js_app_bundle) assets.register("all_css", all_css_bundle)
assets.register("css_libs", css_libs_bundle) assets.register("less_app", all_less_bundle)
if len(css_app):
css_app_bundle = Bundle(*css_app, output="webassets/packed_app.css")
assets.register("css_app", css_app_bundle)
if len(less_app):
less_app_bundle = Bundle(*less_app, output="webassets/packed_app.less")
assets.register("less_app", less_app_bundle)
class LifecycleManager(object): class LifecycleManager(object):

View file

@ -431,10 +431,34 @@ class PluginAssetResolver(flask.ext.assets.FlaskResolver):
return flask.ext.assets.FlaskResolver.split_prefix(self, item) return flask.ext.assets.FlaskResolver.split_prefix(self, item)
def resolve_output_to_path(self, target, bundle): def resolve_output_to_path(self, target, bundle):
if target.startswith("webassets/"): import os
import os return os.path.normpath(os.path.join(self.env.directory, target))
return os.path.normpath(os.path.join(settings().getBaseFolder("webassets"), target[len("webassets/"):]))
return flask.ext.assets.FlaskResolver.resolve_output_to_path(self, target, bundle) def resolve_source_to_url(self, filepath, item):
if item.startswith("plugin/"):
try:
prefix, plugin, name = item.split('/', 2)
blueprint = prefix + "." + plugin
self.env._app.blueprints[blueprint] # keyerror if no module
endpoint = '%s.static' % blueprint
filename = name
except (ValueError, KeyError):
endpoint = 'static'
filename = item
ctx = None
if not flask._request_ctx_stack.top:
ctx = self.env._app.test_request_context()
ctx.push()
try:
return flask.url_for(endpoint, filename=filename)
finally:
if ctx:
ctx.pop()
return flask.ext.assets.FlaskResolver.resolve_source_to_url(self, filepath, item)
##~~ plugin assets collector ##~~ plugin assets collector
@ -442,7 +466,8 @@ def collect_plugin_assets(enable_gcodeviewer=True, enable_timelapse=True, prefer
supported_stylesheets = ("css", "less") supported_stylesheets = ("css", "less")
assets = dict( assets = dict(
js=[], js=[],
stylesheets=[] css=[],
less=[]
) )
assets["js"] = [ assets["js"] = [
'js/app/viewmodels/appearance.js', 'js/app/viewmodels/appearance.js',
@ -473,9 +498,9 @@ def collect_plugin_assets(enable_gcodeviewer=True, enable_timelapse=True, prefer
assets["js"].append('js/app/viewmodels/timelapse.js') assets["js"].append('js/app/viewmodels/timelapse.js')
if preferred_stylesheet == "less": if preferred_stylesheet == "less":
assets["stylesheets"].append(("less", 'less/octoprint.less')) assets["less"].append('less/octoprint.less')
elif preferred_stylesheet == "css": elif preferred_stylesheet == "css":
assets["stylesheets"].append(("css", 'css/octoprint.css')) assets["css"].append('css/octoprint.css')
asset_plugins = octoprint.plugin.plugin_manager().get_implementations(octoprint.plugin.AssetPlugin) asset_plugins = octoprint.plugin.plugin_manager().get_implementations(octoprint.plugin.AssetPlugin)
for implementation in asset_plugins: for implementation in asset_plugins:
@ -488,14 +513,14 @@ def collect_plugin_assets(enable_gcodeviewer=True, enable_timelapse=True, prefer
if preferred_stylesheet in all_assets: if preferred_stylesheet in all_assets:
for asset in all_assets[preferred_stylesheet]: for asset in all_assets[preferred_stylesheet]:
assets["stylesheets"].append((preferred_stylesheet, 'plugin/{name}/{asset}'.format(**locals()))) assets[preferred_stylesheet].append('plugin/{name}/{asset}'.format(**locals()))
else: else:
for stylesheet in supported_stylesheets: for stylesheet in supported_stylesheets:
if not stylesheet in all_assets: if not stylesheet in all_assets:
continue continue
for asset in all_assets[stylesheet]: for asset in all_assets[stylesheet]:
assets["stylesheets"].append((stylesheet, 'plugin/{name}/{asset}'.format(**locals()))) assets[stylesheet].append('plugin/{name}/{asset}'.format(**locals()))
break break
return assets return assets

View file

@ -156,7 +156,7 @@ default_settings = {
"printerProfiles": None, "printerProfiles": None,
"scripts": None, "scripts": None,
"translations": None, "translations": None,
"webassets": None "generated": None
}, },
"temperature": { "temperature": {
"profiles": [ "profiles": [
@ -249,6 +249,10 @@ default_settings = {
"cache": { "cache": {
"enabled": True "enabled": True
}, },
"webassets": {
"minify": True,
"bundle": True
},
"virtualPrinter": { "virtualPrinter": {
"enabled": False, "enabled": False,
"okAfterResend": False, "okAfterResend": False,

View file

View file

@ -1,9 +1,4 @@
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/moment-with-locales.min.js') }}"></script> {% assets "all_js" %}
{% assets "js_libs" %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
{% assets "js_app" %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %} {% endassets %}

View file

@ -1,13 +1,9 @@
{% assets "css_libs" %} {% assets "all_css" %}
<link href="{{ ASSET_URL }}" rel="stylesheet" media="screen"> <link href="{{ ASSET_URL }}" rel="stylesheet" media="screen">
{% endassets %} {% endassets %}
{% assets "css_app" %}
<link href="{{ ASSET_URL }}" rel="stylesheet" type="text/css" media="screen">
{% endassets %}
{% assets "less_app" %} {% assets "less_app" %}
<link href="{{ url }}" rel="stylesheet/less" type="text/css" media="screen"> <link href="{{ ASSET_URL }}" rel="stylesheet/less" type="text/css" media="screen">
{% endassets %} {% endassets %}
<script src="{{ url_for('static', filename='js/lib/less.min.js') }}" type="text/javascript"></script> <script src="{{ url_for('static', filename='js/lib/less.min.js') }}" type="text/javascript"></script>