1
|
|
|
import os |
2
|
|
|
import logging |
3
|
|
|
from flask import Flask, render_template |
4
|
|
|
from docutils.core import publish_parts |
5
|
|
|
|
6
|
|
|
from groundwork.patterns import GwBasePattern |
7
|
|
|
|
8
|
|
|
from groundwork_web.patterns.gw_web_pattern.server import ServerManagerApplication, ServerManagerPlugin |
9
|
|
|
from groundwork_web.patterns.gw_web_pattern.context import ContextManagerApplication, ContextManagerPlugin |
10
|
|
|
from groundwork_web.patterns.gw_web_pattern.route import RouteManagerApplication, RouteManagerPlugin |
11
|
|
|
from groundwork_web.patterns.gw_web_pattern.menu import MenuApplication, MenuPlugin |
12
|
|
|
|
13
|
|
|
|
14
|
|
|
class GwWebPattern(GwBasePattern): |
15
|
|
|
|
16
|
|
|
def __init__(self, *args, **kwargs): |
17
|
|
|
super().__init__(*args, **kwargs) |
18
|
|
|
if not hasattr(self.app, "web"): |
19
|
|
|
self.app.web = WebApplication(self.app) |
20
|
|
|
|
21
|
|
|
#: Instance of :class:`~.WebPlugin`. |
22
|
|
|
#: Provides functions to manage web based objects |
23
|
|
|
self.web = WebPlugin(self) |
24
|
|
|
|
25
|
|
|
|
26
|
|
|
class WebPlugin: |
27
|
|
|
def __init__(self, plugin): |
28
|
|
|
self.plugin = plugin |
29
|
|
|
self.app = plugin.app |
30
|
|
|
self.log = plugin.log |
31
|
|
|
|
32
|
|
|
self.servers = ServerManagerPlugin(plugin) |
33
|
|
|
self.contexts = ContextManagerPlugin(plugin) |
34
|
|
|
self.routes = RouteManagerPlugin(plugin) |
35
|
|
|
self.menus = MenuPlugin(plugin) |
36
|
|
|
|
37
|
|
|
self.plugin.signals.connect(receiver="%s_flask_activation" % self.plugin.name, |
38
|
|
|
signal="plugin_activate_pre", |
39
|
|
|
function=self.__init_flask, |
40
|
|
|
description="Initialised flask during plugin activation of %s" % self.plugin.name, |
41
|
|
|
sender=self.plugin) |
42
|
|
|
|
43
|
|
|
@property |
44
|
|
|
def flask(self): |
45
|
|
|
return self.app.web.flask |
46
|
|
|
|
47
|
|
|
@flask.setter |
48
|
|
|
def flask(self, value): |
49
|
|
|
self.app.web.flask = value |
50
|
|
|
|
51
|
|
|
def __init_flask(self, plugin, *args, **kwargs): |
52
|
|
|
self.app.web.init_flask() |
53
|
|
|
|
54
|
|
|
def render(self, template, plugin=None, **kwargs): |
55
|
|
|
if plugin is None: |
56
|
|
|
plugin = self.plugin |
57
|
|
|
|
58
|
|
|
return self.app.web.render(template, plugin, **kwargs) |
59
|
|
|
|
60
|
|
|
|
61
|
|
|
class WebApplication: |
62
|
|
|
def __init__(self, app): |
63
|
|
|
self.app = app |
64
|
|
|
self.log = logging.getLogger(__name__) |
65
|
|
|
self.flask = None |
66
|
|
|
|
67
|
|
|
self.servers = ServerManagerApplication(app) |
68
|
|
|
self.contexts = ContextManagerApplication(app) |
69
|
|
|
self.routes = RouteManagerApplication(app) |
70
|
|
|
self.menus = MenuApplication(app) |
71
|
|
|
|
72
|
|
|
def init_flask(self): |
73
|
|
|
""" |
74
|
|
|
Initialises and configures flask, is not already done,. |
75
|
|
|
:return: None |
76
|
|
|
""" |
77
|
|
|
if self.flask is None: |
78
|
|
|
self.flask = Flask(self.app.name) |
79
|
|
|
|
80
|
|
|
# Inject send_signal() to jinja templates |
81
|
|
|
# Use it like {{ send_signal("my_signal") }} |
82
|
|
|
self.flask.jinja_env.globals.update(send_signal=self.app.signals.send) |
83
|
|
|
|
84
|
|
|
self.flask.jinja_env.globals.update(app=self.app) |
85
|
|
|
|
86
|
|
|
self.flask.jinja_env.globals.update(get_menu=self.__get_menu) |
87
|
|
|
self.flask.jinja_env.globals.update(get_config=self.app.config.get) |
88
|
|
|
self.flask.jinja_env.globals.update(rst2html=self.__rst2html) |
89
|
|
|
|
90
|
|
|
# Lets set the secret key for flask. This should be set in configuration files, so that |
91
|
|
|
# signed cookies are still valid if the server got restarted. |
92
|
|
|
# If there is no such parameter available, we create a temporary key, which is only |
93
|
|
|
# available during server runtime. |
94
|
|
|
self.flask.secret_key = self.app.config.get("FLASK_SECRET_KEY", os.urandom(24)) |
95
|
|
|
|
96
|
|
|
self.flask.config["SERVER_NAME"] = self.app.config.get("FLASK_SERVER_NAME", "localhost") |
97
|
|
|
|
98
|
|
|
def __get_menu(self, cluster="base"): |
99
|
|
|
return self.menus.get(cluster=cluster) |
100
|
|
|
|
101
|
|
|
def __rst2html(self, document, part="body"): |
102
|
|
|
if document is not None and type(document) == str: |
103
|
|
|
doc_rendered = publish_parts(document, writer_name="html") |
104
|
|
|
if part not in doc_rendered.keys(): |
105
|
|
|
raise KeyError("%s is not a valid key for part parameter of rst2html.\nValid options: " % |
106
|
|
|
(part, ",".join(doc_rendered.keys()))) |
107
|
|
|
return doc_rendered[part] |
108
|
|
|
return document |
109
|
|
|
|
110
|
|
|
def render(self, template, plugin=None, **kwargs): |
111
|
|
|
""" |
112
|
|
|
Renders a template and returns a strings, which represents the rendered data. |
113
|
|
|
|
114
|
|
|
Internally render_template() from flask is used. |
115
|
|
|
|
116
|
|
|
:param template: Name of the template |
117
|
|
|
:param kwargs: Optional key-word arguments, which get passed to the template engine. |
118
|
|
|
:return: Rendered template as string |
119
|
|
|
""" |
120
|
|
|
|
121
|
|
|
return render_template(template, app=self.app, plugin=plugin, **kwargs) |
122
|
|
|
|
123
|
|
|
|