~homeworkprod/byceps

ref: 9f9e7abd1a07b6d61a6f083d62fadb3eec563900 byceps/byceps/blueprints/snippet/templating.py -rw-r--r-- 3.8 KiB
9f9e7abd — Jochen Kupperschmidt Rework retrieval of user's orga team to show on public profile 1 year, 11 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""
byceps.blueprints.snippet.templating
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:Copyright: 2006-2020 Jochen Kupperschmidt
:License: Modified BSD, see LICENSE for details.
"""

import sys
import traceback
from typing import Set

from flask import abort, g, render_template, url_for
from jinja2 import TemplateNotFound

from ...services.snippet import mountpoint_service, service as snippet_service
from ...services.snippet.service import SnippetNotFound
from ...services.snippet.transfer.models import Mountpoint, Scope
from ...util.templating import get_variable_value, load_template


def render_snippet_as_page(version):
    """Render the given version of the snippet, or an error page if
    that fails.
    """
    try:
        context = get_snippet_context(version)
        return render_template('snippet/view.html', **context)
    except TemplateNotFound:
        abort(404)
    except Exception as e:
        print('Error in snippet markup:', e, file=sys.stderr)
        traceback.print_exc()
        context = {
            'message': str(e),
        }
        return render_template('snippet/error.html', **context), 500


def get_snippet_context(version):
    """Return the snippet context to insert into the outer template."""
    template = _load_template_with_globals(version.body)

    current_page = get_variable_value(template, 'current_page')
    title = version.title
    head = _render_template(version.head) if version.head else None
    body = template.render()

    return {
        'title': title,
        'current_page': current_page,
        'head': head,
        'body': body,
    }


def render_snippet_as_partial(
    name, *, scope=None, ignore_if_unknown=False, context=None
):
    """Render the latest version of the snippet with the given name and
    return the result.
    """
    if scope is None:
        scope = Scope.for_site(g.site_id)

    current_version = snippet_service.find_current_version_of_snippet_with_name(
        scope, name
    )

    if current_version is None:
        if ignore_if_unknown:
            return ''
        else:
            raise SnippetNotFound(scope, name)

    if context is None:
        context = {}

    return _render_template(current_version.body, context=context)


def _render_template(source, *, context=None):
    template = _load_template_with_globals(source)

    if context is None:
        context = {}

    return template.render(**context)


def _load_template_with_globals(source):
    template_globals = {
        'render_snippet': render_snippet_as_partial,
        'url_for': url_for,
        'url_for_snippet': url_for_snippet,
    }

    return load_template(source, template_globals=template_globals)


def url_for_snippet(endpoint_suffix: str, **kwargs) -> str:
    """Render an URL pointing to the snippet document's mountpoint."""
    mountpoints = _get_mountpoints()

    # Endpoint suffix is unique per site.
    mountpoints_by_endpoint_suffix = {
        mp.endpoint_suffix: mp for mp in mountpoints
    }

    mountpoint = mountpoints_by_endpoint_suffix[endpoint_suffix]

    return url_for(f'snippet.view', url_path=mountpoint.url_path, **kwargs)


def _get_mountpoints() -> Set[Mountpoint]:
    """Return site-specific mountpoints.

    Preferrably from request-local cache, if available. From the
    database if not yet cached.
    """
    site_id = getattr(g, 'site_id', None)
    if site_id is None:
        return set()

    request_context_key = f'snippet_mountpoints_{site_id}'

    mountpoints_from_request_context = g.get(request_context_key)
    if mountpoints_from_request_context:
        return mountpoints_from_request_context
    else:
        mountpoints_from_database = mountpoint_service.get_mountpoints_for_site(
            site_id
        )
        setattr(g, request_context_key, mountpoints_from_database)
        return mountpoints_from_database