Пример #1
0
def in_cache():
    url = request.base_url.replace("/cached.gif", "/")
    path = request.path.replace("/cached.gif", "/")
    base_url = request.url_root

    # select view from plugins and fall back on default view if no plugin will handle it
    ui_plugins = pluginManager.get_implementations(
        octoprint.plugin.UiPlugin, sorting_context="UiPlugin.on_ui_render")
    for plugin in ui_plugins:
        if plugin.will_handle_ui(request):
            ui = plugin._identifier
            key = _cache_key(plugin._identifier,
                             url=url,
                             additional_key_data=plugin.
                             get_ui_additional_key_data_for_cache)
            unless = _preemptive_unless(
                url,
                additional_unless=plugin.
                get_ui_preemptive_caching_additional_unless)
            data = _preemptive_data(
                plugin._identifier,
                path=path,
                base_url=base_url,
                data=plugin.get_ui_data_for_preemptive_caching,
                additional_request_data=plugin.
                get_ui_additional_request_data_for_preemptive_caching)
            break
    else:
        ui = "_default"
        key = _cache_key("_default", url=url)
        unless = _preemptive_unless(url)
        data = _preemptive_data("_default", path=path, base_url=base_url)

    response = make_response(
        bytes(
            base64.b64decode(
                "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")))
    response.headers["Content-Type"] = "image/gif"

    if unless or not preemptiveCache.has_record(data, root=path):
        _logger.info(
            "Preemptive cache not active for path {}, ui {} and data {!r}, signaling as cached"
            .format(path, ui, data))
        return response
    elif util.flask.is_in_cache(key):
        _logger.info(
            "Found path {} in cache (key: {}), signaling as cached".format(
                path, key))
        return response
    elif util.flask.is_cache_bypassed(key):
        _logger.info(
            "Path {} was bypassed from cache (key: {}), signaling as cached".
            format(path, key))
        return response
    else:
        _logger.debug(
            "Path {} not yet cached (key: {}), signaling as missing".format(
                path, key))
        return abort(404)
Пример #2
0
def in_cache():
	url = request.base_url.replace("/cached.gif", "/")
	path = request.path.replace("/cached.gif", "/")
	base_url = request.url_root

	# select view from plugins and fall back on default view if no plugin will handle it
	ui_plugins = pluginManager.get_implementations(octoprint.plugin.UiPlugin,
	                                               sorting_context="UiPlugin.on_ui_render")
	for plugin in ui_plugins:
		try:
			if plugin.will_handle_ui(request):
				ui = plugin._identifier
				key = _cache_key(plugin._identifier,
				                 url=url,
				                 additional_key_data=plugin.get_ui_additional_key_data_for_cache)
				unless = _preemptive_unless(url, additional_unless=plugin.get_ui_preemptive_caching_additional_unless)
				data = _preemptive_data(plugin._identifier,
				                        path=path,
				                        base_url=base_url,
				                        data=plugin.get_ui_data_for_preemptive_caching,
				                        additional_request_data=plugin.get_ui_additional_request_data_for_preemptive_caching)
				break
		except Exception:
			_logger.exception("Error while calling plugin {}, skipping it".format(plugin._identifier),
			                  extra=dict(plugin=plugin._identifier))
	else:
		ui = "_default"
		key = _cache_key("_default", url=url)
		unless = _preemptive_unless(url)
		data = _preemptive_data("_default", path=path, base_url=base_url)

	response = make_response(bytes(base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")))
	response.headers["Content-Type"] = "image/gif"

	if unless or not preemptiveCache.has_record(data, root=path):
		_logger.info("Preemptive cache not active for path {}, ui {} and data {!r}, signaling as cached".format(path, ui, data))
		return response
	elif util.flask.is_in_cache(key):
		_logger.info("Found path {} in cache (key: {}), signaling as cached".format(path, key))
		return response
	elif util.flask.is_cache_bypassed(key):
		_logger.info("Path {} was bypassed from cache (key: {}), signaling as cached".format(path, key))
		return response
	else:
		_logger.debug("Path {} not yet cached (key: {}), signaling as missing".format(path, key))
		return abort(404)
Пример #3
0
def index():
	global _templates, _plugin_names, _plugin_vars

	preemptive_cache_enabled = settings().getBoolean(["devel", "cache", "preemptive"])

	locale = g.locale.language if g.locale else "en"

	# helper to check if wizards are active
	def wizard_active(templates):
		return templates is not None and bool(templates["wizard"]["order"])

	# we force a refresh if the client forces one or if we have wizards cached
	force_refresh = util.flask.cache_check_headers() or "_refresh" in request.values or wizard_active(_templates.get(locale))

	# if we need to refresh our template cache or it's not yet set, process it
	if force_refresh or _templates.get(locale) is None or _plugin_names is None or _plugin_vars is None:
		_templates[locale], _plugin_names, _plugin_vars = _process_templates()

	now = datetime.datetime.utcnow()

	enable_accesscontrol = userManager.enabled
	enable_gcodeviewer = settings().getBoolean(["gcodeViewer", "enabled"])
	enable_timelapse = bool(settings().get(["webcam", "snapshot"]) and settings().get(["webcam", "ffmpeg"]))

	def default_template_filter(template_type, template_key):
		if template_type == "navbar":
			return template_key != "login" or enable_accesscontrol
		elif template_type == "tab":
			return (template_key != "gcodeviewer" or enable_gcodeviewer) and \
			       (template_key != "timelapse" or enable_timelapse)
		elif template_type == "settings":
			return template_key != "accesscontrol" or enable_accesscontrol
		elif template_type == "usersettings":
			return enable_accesscontrol
		else:
			return True

	default_additional_etag = [enable_accesscontrol,
	                           enable_gcodeviewer,
	                           enable_timelapse]

	def get_preemptively_cached_view(key, view, data=None, additional_request_data=None, additional_unless=None):
		if (data is None and additional_request_data is None) or g.locale is None:
			return view

		d = _preemptive_data(key, data=data, additional_request_data=additional_request_data)

		def unless():
			return _preemptive_unless(base_url=request.url_root, additional_unless=additional_unless)

		# finally decorate our view
		return util.flask.preemptively_cached(cache=preemptiveCache,
		                                      data=d,
		                                      unless=unless)(view)

	def get_cached_view(key, view, additional_key_data=None, additional_files=None, additional_etag=None, custom_files=None, custom_etag=None, custom_lastmodified=None):
		if additional_etag is None:
			additional_etag = []

		def cache_key():
			return _cache_key(key, additional_key_data=additional_key_data)

		def check_etag_and_lastmodified():
			files = collect_files()
			lastmodified = compute_lastmodified(files)
			lastmodified_ok = util.flask.check_lastmodified(lastmodified)
			etag_ok = util.flask.check_etag(compute_etag(files=files,
			                                             lastmodified=lastmodified,
			                                             additional=[cache_key()] + additional_etag))
			return lastmodified_ok and etag_ok

		def validate_cache(cached):
			etag_different = compute_etag(additional=[cache_key()] + additional_etag) != cached.get_etag()[0]
			return force_refresh or etag_different

		def collect_files():
			if callable(custom_files):
				try:
					files = custom_files()
					if files:
						return files
				except:
					_logger.exception("Error while trying to retrieve tracked files for plugin {}".format(key))

			templates = _get_all_templates()
			assets = _get_all_assets()
			translations = _get_all_translationfiles(g.locale.language if g.locale else "en",
			                                         "messages")

			files = templates + assets + translations

			if callable(additional_files):
				try:
					af = additional_files()
					if af:
						files += af
				except:
					_logger.exception("Error while trying to retrieve additional tracked files for plugin {}".format(key))

			return sorted(set(files))

		def compute_lastmodified(files=None):
			if callable(custom_lastmodified):
				try:
					lastmodified = custom_lastmodified()
					if lastmodified:
						return lastmodified
				except:
					_logger.exception("Error while trying to retrieve custom LastModified value for plugin {}".format(key))

			if files is None:
				files = collect_files()
			return _compute_date(files)

		def compute_etag(files=None, lastmodified=None, additional=None):
			if callable(custom_etag):
				try:
					etag = custom_etag()
					if etag:
						return etag
				except:
					_logger.exception("Error while trying to retrieve custom ETag value for plugin {}".format(key))

			if files is None:
				files = collect_files()
			if lastmodified is None:
				lastmodified = compute_lastmodified(files)
			if lastmodified and not isinstance(lastmodified, basestring):
				from werkzeug.http import http_date
				lastmodified = http_date(lastmodified)
			if additional is None:
				additional = []

			import hashlib
			hash = hashlib.sha1()
			hash.update(octoprint.__version__)
			hash.update(octoprint.server.UI_API_KEY)
			hash.update(",".join(sorted(files)))
			if lastmodified:
				hash.update(lastmodified)
			for add in additional:
				hash.update(str(add))
			return hash.hexdigest()

		decorated_view = view
		decorated_view = util.flask.lastmodified(lambda _: compute_lastmodified())(decorated_view)
		decorated_view = util.flask.etagged(lambda _: compute_etag(additional=[cache_key()] + additional_etag))(decorated_view)
		decorated_view = util.flask.cached(timeout=-1,
		                                   refreshif=validate_cache,
		                                   key=cache_key,
		                                   unless_response=lambda response: util.flask.cache_check_response_headers(response) or util.flask.cache_check_status_code(response, _valid_status_for_cache))(decorated_view)
		decorated_view = util.flask.conditional(check_etag_and_lastmodified, NOT_MODIFIED)(decorated_view)
		return decorated_view

	def plugin_view(p):
		cached = get_cached_view(p._identifier,
		                         p.on_ui_render,
		                         additional_key_data=p.get_ui_additional_key_data_for_cache,
		                         additional_files=p.get_ui_additional_tracked_files,
		                         custom_files=p.get_ui_custom_tracked_files,
		                         custom_etag=p.get_ui_custom_etag,
		                         custom_lastmodified=p.get_ui_custom_lastmodified,
		                         additional_etag=p.get_ui_additional_etag(default_additional_etag))

		if preemptive_cache_enabled and p.get_ui_preemptive_caching_enabled():
			view = get_preemptively_cached_view(p._identifier,
			                                    cached,
			                                    p.get_ui_data_for_preemptive_caching,
			                                    p.get_ui_additional_request_data_for_preemptive_caching,
			                                    p.get_ui_preemptive_caching_additional_unless)
		else:
			view = cached

		template_filter = p.get_ui_custom_template_filter(default_template_filter)
		if template_filter is not None and callable(template_filter):
			filtered_templates = _filter_templates(_templates[locale], template_filter)
		else:
			filtered_templates = _templates[locale]

		render_kwargs = _get_render_kwargs(filtered_templates,
		                                   _plugin_names,
		                                   _plugin_vars,
		                                   now)

		return view(now, request, render_kwargs)

	def default_view():
		filtered_templates = _filter_templates(_templates[locale], default_template_filter)

		wizard = wizard_active(filtered_templates)
		accesscontrol_active = enable_accesscontrol and userManager.hasBeenCustomized()

		render_kwargs = _get_render_kwargs(filtered_templates,
		                                   _plugin_names,
		                                   _plugin_vars,
		                                   now)

		render_kwargs.update(dict(
			webcamStream=settings().get(["webcam", "stream"]),
			enableTemperatureGraph=settings().get(["feature", "temperatureGraph"]),
			enableAccessControl=enable_accesscontrol,
			accessControlActive=accesscontrol_active,
			enableSdSupport=settings().get(["feature", "sdSupport"]),
			gcodeMobileThreshold=settings().get(["gcodeViewer", "mobileSizeThreshold"]),
			gcodeThreshold=settings().get(["gcodeViewer", "sizeThreshold"]),
			wizard=wizard,
			now=now,
		))

		# no plugin took an interest, we'll use the default UI
		def make_default_ui():
			r = make_response(render_template("index.jinja2", **render_kwargs))
			if wizard:
				# if we have active wizard dialogs, set non caching headers
				r = util.flask.add_non_caching_response_headers(r)
			return r

		cached = get_cached_view("_default",
		                         make_default_ui,
		                         additional_etag=default_additional_etag)
		preemptively_cached = get_preemptively_cached_view("_default",
		                                                   cached,
		                                                   dict(),
		                                                   dict())
		return preemptively_cached()

	response = None

	forced_view = request.headers.get("X-Force-View", None)

	if forced_view:
		# we have view forced by the preemptive cache
		_logger.debug("Forcing rendering of view {}".format(forced_view))
		if forced_view != "_default":
			plugin = pluginManager.get_plugin_info(forced_view, require_enabled=True)
			if plugin is not None and isinstance(plugin.implementation, octoprint.plugin.UiPlugin):
				response = plugin_view(plugin.implementation)
		else:
			response = default_view()

	else:
		# select view from plugins and fall back on default view if no plugin will handle it
		ui_plugins = pluginManager.get_implementations(octoprint.plugin.UiPlugin, sorting_context="UiPlugin.on_ui_render")
		for plugin in ui_plugins:
			if plugin.will_handle_ui(request):
				# plugin claims responsibility, let it render the UI
				response = plugin_view(plugin)
				if response is not None:
					break
				else:
					_logger.warn("UiPlugin {} returned an empty response".format(plugin._identifier))
		else:
			response = default_view()

	if response is None:
		return abort(404)
	return response
Пример #4
0
def index():
	global _templates, _plugin_names, _plugin_vars

	preemptive_cache_enabled = settings().getBoolean(["devel", "cache", "preemptive"])

	locale = g.locale.language if g.locale else "en"

	# helper to check if wizards are active
	def wizard_active(templates):
		return templates is not None and bool(templates["wizard"]["order"])

	# we force a refresh if the client forces one or if we have wizards cached
	force_refresh = util.flask.cache_check_headers() or "_refresh" in request.values or wizard_active(_templates.get(locale))

	# if we need to refresh our template cache or it's not yet set, process it
	if force_refresh or _templates.get(locale) is None or _plugin_names is None or _plugin_vars is None:
		_templates[locale], _plugin_names, _plugin_vars = _process_templates()

	now = datetime.datetime.utcnow()

	enable_accesscontrol = userManager.enabled
	enable_gcodeviewer = settings().getBoolean(["gcodeViewer", "enabled"])
	enable_timelapse = bool(settings().get(["webcam", "snapshot"]) and settings().get(["webcam", "ffmpeg"]))

	def default_template_filter(template_type, template_key):
		if template_type == "navbar":
			return template_key != "login" or enable_accesscontrol
		elif template_type == "tab":
			return (template_key != "gcodeviewer" or enable_gcodeviewer) and \
			       (template_key != "timelapse" or enable_timelapse)
		elif template_type == "settings":
			return template_key != "accesscontrol" or enable_accesscontrol
		elif template_type == "usersettings":
			return enable_accesscontrol
		else:
			return True

	default_additional_etag = [enable_accesscontrol,
	                           enable_gcodeviewer,
	                           enable_timelapse]

	def get_preemptively_cached_view(key, view, data=None, additional_request_data=None, additional_unless=None):
		if (data is None and additional_request_data is None) or g.locale is None:
			return view

		d = _preemptive_data(key, data=data, additional_request_data=additional_request_data)

		def unless():
			return _preemptive_unless(base_url=request.url_root, additional_unless=additional_unless)

		# finally decorate our view
		return util.flask.preemptively_cached(cache=preemptiveCache,
		                                      data=d,
		                                      unless=unless)(view)

	def get_cached_view(key, view, additional_key_data=None, additional_files=None, additional_etag=None, custom_files=None, custom_etag=None, custom_lastmodified=None):
		if additional_etag is None:
			additional_etag = []

		def cache_key():
			return _cache_key(key, additional_key_data=additional_key_data)

		def check_etag_and_lastmodified():
			files = collect_files()
			lastmodified = compute_lastmodified(files)
			lastmodified_ok = util.flask.check_lastmodified(lastmodified)
			etag_ok = util.flask.check_etag(compute_etag(files=files,
			                                             lastmodified=lastmodified,
			                                             additional=[cache_key()] + additional_etag))
			return lastmodified_ok and etag_ok

		def validate_cache(cached):
			etag_different = compute_etag(additional=[cache_key()] + additional_etag) != cached.get_etag()[0]
			return force_refresh or etag_different

		def collect_files():
			if callable(custom_files):
				try:
					files = custom_files()
					if files:
						return files
				except:
					_logger.exception("Error while trying to retrieve tracked files for plugin {}".format(key))

			templates = _get_all_templates()
			assets = _get_all_assets()
			translations = _get_all_translationfiles(g.locale.language if g.locale else "en",
			                                         "messages")

			files = templates + assets + translations

			if callable(additional_files):
				try:
					af = additional_files()
					if af:
						files += af
				except:
					_logger.exception("Error while trying to retrieve additional tracked files for plugin {}".format(key))

			return sorted(set(files))

		def compute_lastmodified(files=None):
			if callable(custom_lastmodified):
				try:
					lastmodified = custom_lastmodified()
					if lastmodified:
						return lastmodified
				except:
					_logger.exception("Error while trying to retrieve custom LastModified value for plugin {}".format(key))

			if files is None:
				files = collect_files()
			return _compute_date(files)

		def compute_etag(files=None, lastmodified=None, additional=None):
			if callable(custom_etag):
				try:
					etag = custom_etag()
					if etag:
						return etag
				except:
					_logger.exception("Error while trying to retrieve custom ETag value for plugin {}".format(key))

			if files is None:
				files = collect_files()
			if lastmodified is None:
				lastmodified = compute_lastmodified(files)
			if lastmodified and not isinstance(lastmodified, basestring):
				from werkzeug.http import http_date
				lastmodified = http_date(lastmodified)
			if additional is None:
				additional = []

			import hashlib
			hash = hashlib.sha1()
			hash.update(octoprint.__version__)
			hash.update(octoprint.server.UI_API_KEY)
			hash.update(",".join(sorted(files)))
			if lastmodified:
				hash.update(lastmodified)
			for add in additional:
				hash.update(str(add))
			return hash.hexdigest()

		decorated_view = view
		decorated_view = util.flask.lastmodified(lambda _: compute_lastmodified())(decorated_view)
		decorated_view = util.flask.etagged(lambda _: compute_etag(additional=[cache_key()] + additional_etag))(decorated_view)
		decorated_view = util.flask.cached(timeout=-1,
		                                   refreshif=validate_cache,
		                                   key=cache_key,
		                                   unless_response=lambda response: util.flask.cache_check_response_headers(response) or util.flask.cache_check_status_code(response, _valid_status_for_cache))(decorated_view)
		decorated_view = util.flask.conditional(check_etag_and_lastmodified, NOT_MODIFIED)(decorated_view)
		return decorated_view

	def plugin_view(p):
		cached = get_cached_view(p._identifier,
		                         p.on_ui_render,
		                         additional_key_data=p.get_ui_additional_key_data_for_cache,
		                         additional_files=p.get_ui_additional_tracked_files,
		                         custom_files=p.get_ui_custom_tracked_files,
		                         custom_etag=p.get_ui_custom_etag,
		                         custom_lastmodified=p.get_ui_custom_lastmodified,
		                         additional_etag=p.get_ui_additional_etag(default_additional_etag))

		if preemptive_cache_enabled and p.get_ui_preemptive_caching_enabled():
			view = get_preemptively_cached_view(p._identifier,
			                                    cached,
			                                    p.get_ui_data_for_preemptive_caching,
			                                    p.get_ui_additional_request_data_for_preemptive_caching,
			                                    p.get_ui_preemptive_caching_additional_unless)
		else:
			view = cached

		template_filter = p.get_ui_custom_template_filter(default_template_filter)
		if template_filter is not None and callable(template_filter):
			filtered_templates = _filter_templates(_templates[locale], template_filter)
		else:
			filtered_templates = _templates[locale]

		render_kwargs = _get_render_kwargs(filtered_templates,
		                                   _plugin_names,
		                                   _plugin_vars,
		                                   now)

		return view(now, request, render_kwargs)

	def default_view():
		filtered_templates = _filter_templates(_templates[locale], default_template_filter)

		wizard = wizard_active(filtered_templates)
		accesscontrol_active = enable_accesscontrol and userManager.hasBeenCustomized()

		render_kwargs = _get_render_kwargs(filtered_templates,
		                                   _plugin_names,
		                                   _plugin_vars,
		                                   now)

		render_kwargs.update(dict(
			webcamStream=settings().get(["webcam", "stream"]),
			enableTemperatureGraph=settings().get(["feature", "temperatureGraph"]),
			enableAccessControl=enable_accesscontrol,
			accessControlActive=accesscontrol_active,
			enableSdSupport=settings().get(["feature", "sdSupport"]),
			gcodeMobileThreshold=settings().get(["gcodeViewer", "mobileSizeThreshold"]),
			gcodeThreshold=settings().get(["gcodeViewer", "sizeThreshold"]),
			wizard=wizard,
			now=now,
		))

		# no plugin took an interest, we'll use the default UI
		def make_default_ui():
			r = make_response(render_template("index.jinja2", **render_kwargs))
			if wizard:
				# if we have active wizard dialogs, set non caching headers
				r = util.flask.add_non_caching_response_headers(r)
			return r

		cached = get_cached_view("_default",
		                         make_default_ui,
		                         additional_etag=default_additional_etag)
		preemptively_cached = get_preemptively_cached_view("_default",
		                                                   cached,
		                                                   dict(),
		                                                   dict())
		return preemptively_cached()

	response = None

	forced_view = request.headers.get("X-Force-View", None)

	if forced_view:
		# we have view forced by the preemptive cache
		_logger.debug("Forcing rendering of view {}".format(forced_view))
		if forced_view != "_default":
			plugin = pluginManager.get_plugin_info(forced_view, require_enabled=True)
			if plugin is not None and isinstance(plugin.implementation, octoprint.plugin.UiPlugin):
				response = plugin_view(plugin.implementation)
		else:
			response = default_view()

	else:
		# select view from plugins and fall back on default view if no plugin will handle it
		ui_plugins = pluginManager.get_implementations(octoprint.plugin.UiPlugin, sorting_context="UiPlugin.on_ui_render")
		for plugin in ui_plugins:
			if plugin.will_handle_ui(request):
				# plugin claims responsibility, let it render the UI
				response = plugin_view(plugin)
				if response is not None:
					break
				else:
					_logger.warn("UiPlugin {} returned an empty response".format(plugin._identifier))
		else:
			response = default_view()

	if response is None:
		return abort(404)
	return response