Completed
Push — master ( f10ede...2cdad2 )
by Jochen
04:07
created

byceps.application._get_blueprints()   A

Complexity

Conditions 5

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 13
nop 1
dl 0
loc 19
rs 9.2833
c 0
b 0
f 0
1
"""
2
byceps.application
3
~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2006-2019 Jochen Kupperschmidt
6
:License: Modified BSD, see LICENSE for details.
7
"""
8
9
from importlib import import_module
10
from pathlib import Path
11
12
from flask import Flask, redirect
13
import jinja2
14
15
from .blueprints.snippet.init import add_routes_for_snippets
16
from . import config, config_defaults
17
from .database import db
18
from . import email
19
from .redis import redis
20
from .util.framework.blueprint import register_blueprint
21
from .util.l10n import set_locale
22
from .util import templatefilters
23
24
25
def create_app(config_filename, config_overrides=None):
26
    """Create the actual Flask application."""
27
    app = Flask(__name__)
28
29
    app.config.from_object(config_defaults)
30
    app.config.from_pyfile(str(config_filename))
31
    if config_overrides is not None:
32
        app.config.from_mapping(config_overrides)
33
34
    # Throw an exception when an undefined name is referenced in a template.
35
    app.jinja_env.undefined = jinja2.StrictUndefined
36
37
    # Set the locale.
38
    set_locale(app.config['LOCALE'])  # Fail if not configured.
39
40
    # Initialize database.
41
    db.init_app(app)
42
43
    # Initialize Redis connection.
44
    redis.init_app(app)
45
46
    email.init_app(app)
47
48
    config.init_app(app)
49
50
    _register_blueprints(app)
51
52
    templatefilters.register(app)
53
54
    _add_static_file_url_rules(app)
55
56
    return app
57
58
59
def _register_blueprints(app):
60
    """Register blueprints depending on the configuration."""
61
    for name, url_prefix in _get_blueprints(app):
62
        register_blueprint(app, name, url_prefix)
63
64
65
def _get_blueprints(app):
66
    """Yield blueprints to register on the application."""
67
    yield from _get_blueprints_common()
68
69
    current_mode = config.get_site_mode(app)
70
    if current_mode.is_public():
71
        yield from _get_blueprints_site()
72
    elif current_mode.is_admin():
73
        yield from _get_blueprints_admin()
74
75
    yield from _get_blueprints_api()
76
77
    yield from _get_blueprints_health()
78
79
    if app.config['METRICS_ENABLED']:
80
        yield from _get_blueprints_metrics()
81
82
    if app.debug:
83
        yield from _get_blueprints_debug()
84
85
86
def _get_blueprints_common():
87
    return [
88
        ('authentication',          '/authentication'           ),
89
        ('authorization',           None                        ),
90
        ('core',                    '/core'                     ),
91
        ('user',                    None                        ),
92
        ('user.avatar',             '/users'                    ),
93
        ('user.creation',           '/users'                    ),
94
        ('user.current',            '/users'                    ),
95
        ('user.email_address',      '/users/email_address'      ),
96
    ]
97
98
99
def _get_blueprints_site():
100
    return [
101
        ('attendance',              '/attendance'               ),
102
        ('board',                   '/board'                    ),
103
        ('consent',                 '/consent'                  ),
104
        ('news',                    '/news'                     ),
105
        ('newsletter',              '/newsletter'               ),
106
        ('orga_team',               '/orgas'                    ),
107
        ('party',                   None                        ),
108
        ('seating',                 '/seating'                  ),
109
        ('shop.order',              '/shop'                     ),
110
        ('shop.orders',             '/shop/orders'              ),
111
        ('snippet',                 '/snippets'                 ),
112
        ('terms',                   '/terms'                    ),
113
        ('ticketing',               '/tickets'                  ),
114
        ('user.profile',            '/users'                    ),
115
        ('user_badge',              '/user_badges'              ),
116
        ('user_group',              '/user_groups'              ),
117
        ('user_message',            '/user_messages'            ),
118
    ]
119
120
121
def _get_blueprints_admin():
122
    return [
123
        ('admin.authorization',     '/admin/authorization'      ),
124
        ('admin.board',             '/admin/board'              ),
125
        ('admin.brand',             '/admin/brands'             ),
126
        ('admin.consent',           '/admin/consent'            ),
127
        ('admin.core',              None                        ),
128
        ('admin.dashboard',         '/admin/dashboard'          ),
129
        ('admin.email',             '/admin/email'              ),
130
        ('admin.news',              '/admin/news'               ),
131
        ('admin.newsletter',        '/admin/newsletter'         ),
132
        ('admin.jobs',              '/admin/jobs'               ),
133
        ('admin.orga',              '/admin/orgas'              ),
134
        ('admin.orga_presence',     '/admin/presence'           ),
135
        ('admin.orga_team',         '/admin/orga_teams'         ),
136
        ('admin.party',             '/admin/parties'            ),
137
        ('admin.seating',           '/admin/seating'            ),
138
        ('admin.shop',              None                        ),
139
        ('admin.shop.article',      '/admin/shop/articles'      ),
140
        ('admin.shop.email',        '/admin/shop/email'         ),
141
        ('admin.shop.order',        '/admin/shop/orders'        ),
142
        ('admin.shop.shipping',     '/admin/shop/shipping'      ),
143
        ('admin.shop.shop',         '/admin/shop/shop'          ),
144
        ('admin.site',              '/admin/sites'              ),
145
        ('admin.snippet',           '/admin/snippets'           ),
146
        ('admin.terms',             '/admin/terms'              ),
147
        ('admin.ticketing',         '/admin/ticketing'          ),
148
        ('admin.ticketing.checkin', '/admin/ticketing/checkin'  ),
149
        ('admin.tourney',           '/admin/tourney'            ),
150
        ('admin.user',              '/admin/users'              ),
151
        ('admin.user_badge',        '/admin/user_badges'        ),
152
    ]
153
154
155
def _get_blueprints_api():
156
    return [
157
        ('api.attendance',          '/api/attendances'          ),
158
        ('api.tourney.avatar',      '/api/tourney/avatars'      ),
159
        ('api.tourney.match',       '/api/tourney/matches'      ),
160
        ('api.user',                '/api/users'                ),
161
        ('api.user_badge',          '/api/user_badges'          ),
162
    ]
163
164
165
def _get_blueprints_health():
166
    return [
167
        ('healthcheck',             '/health'                   ),
168
    ]
169
170
171
def _get_blueprints_metrics():
172
    return [
173
        ('metrics',                 '/metrics'                  ),
174
    ]
175
176
177
def _get_blueprints_debug():
178
    return [
179
        ('style_guide',             '/style_guide'              ),
180
    ]
181
182
183
def _add_static_file_url_rules(app):
184
    """Add URL rules to for static files."""
185
    for rule_prefix, endpoint in [
186
        (config.STATIC_URL_PREFIX_GLOBAL, 'global_file'),
187
        (config.STATIC_URL_PREFIX_BRAND, 'brand_file'),
188
        (config.STATIC_URL_PREFIX_PARTY, 'party_file'),
189
        (config.STATIC_URL_PREFIX_SITE, 'site_file'),
190
    ]:
191
        rule = rule_prefix + '/<path:filename>'
192
        app.add_url_rule(
193
            rule, endpoint=endpoint, methods=['GET'], build_only=True
194
        )
195
196
197
def init_app(app):
198
    """Initialize the application after is has been created."""
199
    with app.app_context():
200
        _set_url_root_path(app)
201
202
        site_mode = config.get_site_mode()
203
        if site_mode.is_public():
204
            site_id = config.get_current_site_id()
205
206
            # Mount snippets.
207
            add_routes_for_snippets(site_id)
208
209
            # Incorporate template overrides for the configured site ID.
210
            app.template_folder = str(
211
                Path('..') / 'sites' / site_id / 'template_overrides'
212
            )
213
214
            # Import site-specific code.
215
            _load_site_extension(app, site_id)
216
        elif site_mode.is_admin() and app.config['RQ_DASHBOARD_ENABLED']:
217
            import rq_dashboard
218
219
            app.register_blueprint(
220
                rq_dashboard.blueprint, url_prefix='/admin/rq'
221
            )
222
223
224
def _set_url_root_path(app):
225
    """Set an optional URL path to redirect to from the root URL path (`/`).
226
227
    Important: Don't specify the target with a leading slash unless you
228
    really mean the root of the host.
229
    """
230
    target_url = app.config['ROOT_REDIRECT_TARGET']
231
    if target_url is None:
232
        return
233
234
    status_code = app.config['ROOT_REDIRECT_STATUS_CODE']
235
236
    def _redirect():
237
        return redirect(target_url, status_code)
238
239
    app.add_url_rule('/', endpoint='root', view_func=_redirect)
240
241
242
def _load_site_extension(app, site_id):
243
    """Import and call custom extension code from the site's package, if
244
    available.
245
246
    If a site package contains a module named `extension` and it
247
    contains a top-level callable named `init`, then that callable is
248
    called with the application as its sole argument.
249
250
    The application object can then be used to register, for example, a
251
    context processor.
252
    """
253
    module_name = f'sites.{site_id}.extension'
254
    try:
255
        module = import_module(module_name)
256
    except ModuleNotFoundError:
257
        # No extension module found in site package.
258
        return
259
260
    entry_point = getattr(module, 'init', None)
261
    if entry_point is None:
262
        # Entry point not found in module.
263
        return
264
265
    if not callable(entry_point):
266
        # Entry point object is not callable.
267
        return
268
269
    entry_point(app)
270