1
|
|
|
""" |
2
|
|
|
byceps.blueprints.snippet.templating |
3
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
4
|
|
|
|
5
|
|
|
:Copyright: 2006-2019 Jochen Kupperschmidt |
6
|
|
|
:License: Modified BSD, see LICENSE for details. |
7
|
|
|
""" |
8
|
|
|
|
9
|
|
|
import sys |
10
|
|
|
import traceback |
11
|
|
|
from typing import Set |
12
|
|
|
|
13
|
|
|
from flask import abort, g, render_template, url_for |
14
|
|
|
from jinja2 import TemplateNotFound |
15
|
|
|
|
16
|
|
|
from ...services.snippet import mountpoint_service, service as snippet_service |
17
|
|
|
from ...services.snippet.service import SnippetNotFound |
18
|
|
|
from ...services.snippet.transfer.models import Mountpoint, Scope |
19
|
|
|
from ...util.templating import get_variable_value, load_template |
20
|
|
|
|
21
|
|
|
|
22
|
|
|
def render_snippet_as_page(version): |
23
|
|
|
"""Render the given version of the snippet, or an error page if |
24
|
|
|
that fails. |
25
|
|
|
""" |
26
|
|
|
try: |
27
|
|
|
context = get_snippet_context(version) |
28
|
|
|
return render_template('snippet/view.html', **context) |
29
|
|
|
except TemplateNotFound: |
30
|
|
|
abort(404) |
31
|
|
|
except Exception as e: |
32
|
|
|
print('Error in snippet markup:', e, file=sys.stderr) |
33
|
|
|
traceback.print_exc() |
34
|
|
|
context = { |
35
|
|
|
'message': str(e), |
36
|
|
|
} |
37
|
|
|
return render_template('snippet/error.html', **context), 500 |
38
|
|
|
|
39
|
|
|
|
40
|
|
|
def get_snippet_context(version): |
41
|
|
|
"""Return the snippet context to insert into the outer template.""" |
42
|
|
|
template = _load_template_with_globals(version.body) |
43
|
|
|
|
44
|
|
|
current_page = get_variable_value(template, 'current_page') |
45
|
|
|
title = version.title |
46
|
|
|
head = _render_template(version.head) if version.head else None |
47
|
|
|
body = template.render() |
48
|
|
|
|
49
|
|
|
return { |
50
|
|
|
'title': title, |
51
|
|
|
'current_page': current_page, |
52
|
|
|
'head': head, |
53
|
|
|
'body': body, |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
|
57
|
|
|
def render_snippet_as_partial( |
58
|
|
|
name, *, scope=None, ignore_if_unknown=False, context=None |
59
|
|
|
): |
60
|
|
|
"""Render the latest version of the snippet with the given name and |
61
|
|
|
return the result. |
62
|
|
|
""" |
63
|
|
|
if scope is None: |
64
|
|
|
scope = Scope.for_site(g.site_id) |
65
|
|
|
|
66
|
|
|
current_version = snippet_service.find_current_version_of_snippet_with_name( |
67
|
|
|
scope, name |
68
|
|
|
) |
69
|
|
|
|
70
|
|
|
if current_version is None: |
71
|
|
|
if ignore_if_unknown: |
72
|
|
|
return '' |
73
|
|
|
else: |
74
|
|
|
raise SnippetNotFound(scope, name) |
75
|
|
|
|
76
|
|
|
if context is None: |
77
|
|
|
context = {} |
78
|
|
|
|
79
|
|
|
return _render_template(current_version.body, context=context) |
80
|
|
|
|
81
|
|
|
|
82
|
|
|
def _render_template(source, *, context=None): |
83
|
|
|
template = _load_template_with_globals(source) |
84
|
|
|
|
85
|
|
|
if context is None: |
86
|
|
|
context = {} |
87
|
|
|
|
88
|
|
|
return template.render(**context) |
89
|
|
|
|
90
|
|
|
|
91
|
|
|
def _load_template_with_globals(source): |
92
|
|
|
template_globals = { |
93
|
|
|
'render_snippet': render_snippet_as_partial, |
94
|
|
|
'url_for': url_for, |
95
|
|
|
'url_for_snippet': url_for_snippet, |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
return load_template(source, template_globals=template_globals) |
99
|
|
|
|
100
|
|
|
|
101
|
|
|
def url_for_snippet(endpoint_suffix: str, **kwargs) -> str: |
102
|
|
|
"""Render an URL pointing to the snippet document's mountpoint.""" |
103
|
|
|
mountpoints = _get_mountpoints() |
104
|
|
|
|
105
|
|
|
# Endpoint suffix is unique per site. |
106
|
|
|
mountpoints_by_endpoint_suffix = { |
107
|
|
|
mp.endpoint_suffix: mp for mp in mountpoints |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
mountpoint = mountpoints_by_endpoint_suffix[endpoint_suffix] |
111
|
|
|
|
112
|
|
|
return url_for(f'snippet.view', url_path=mountpoint.url_path, **kwargs) |
113
|
|
|
|
114
|
|
|
|
115
|
|
|
def _get_mountpoints() -> Set[Mountpoint]: |
116
|
|
|
"""Return site-specific mountpoints. |
117
|
|
|
|
118
|
|
|
Preferrably from request-local cache, if available. From the |
119
|
|
|
database if not yet cached. |
120
|
|
|
""" |
121
|
|
|
request_context_key = 'snippet_mountpoints' |
122
|
|
|
|
123
|
|
|
mountpoints_from_request_context = g.get(request_context_key) |
124
|
|
|
if mountpoints_from_request_context: |
125
|
|
|
return mountpoints_from_request_context |
126
|
|
|
else: |
127
|
|
|
mountpoints_from_database = mountpoint_service.get_mountpoints_for_site(g.site_id) |
128
|
|
|
setattr(g, request_context_key, mountpoints_from_database) |
129
|
|
|
return mountpoints_from_database |
130
|
|
|
|