aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fietsboek/__init__.py38
-rw-r--r--fietsboek/config.py11
-rw-r--r--fietsboek/email.py50
-rw-r--r--fietsboek/jinja2.py2
-rw-r--r--fietsboek/templates/layout.jinja22
-rw-r--r--fietsboek/views/account.py13
-rw-r--r--fietsboek/views/default.py9
-rw-r--r--fietsboek/views/tileproxy.py54
8 files changed, 78 insertions, 101 deletions
diff --git a/fietsboek/__init__.py b/fietsboek/__init__.py
index 5e342c5..e208a61 100644
--- a/fietsboek/__init__.py
+++ b/fietsboek/__init__.py
@@ -9,7 +9,6 @@ import redis
from pyramid.config import Configurator
from pyramid.session import SignedCookieSessionFactory
from pyramid.csrf import CookieCSRFStoragePolicy
-from pyramid.settings import asbool, aslist
from pyramid.i18n import default_locale_negotiator
from .security import SecurityPolicy
@@ -40,7 +39,7 @@ def locale_negotiator(request):
if locale:
return locale
- installed_locales = request.registry.settings['available_locales']
+ installed_locales = request.config.available_locales
sentinel = object()
negotiated = request.accept_language.lookup(installed_locales, default=sentinel)
if negotiated is sentinel:
@@ -48,55 +47,30 @@ def locale_negotiator(request):
return negotiated
-def main(global_config, **settings):
+def main(_global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
- # pylint: disable=unused-argument, import-outside-toplevel, cyclic-import
- from .views import tileproxy
-
parsed_config = mod_config.parse(settings)
- if settings.get('session_key', '<EDIT THIS>') == '<EDIT THIS>':
- raise ValueError("Please set a session signing key (session_key) in your settings!")
-
- if 'fietsboek.data_dir' not in settings:
- raise ValueError("Please set a data directory (fietsboek.data_dir) in your settings!")
-
def data_manager(request):
- data_dir = request.registry.settings["fietsboek.data_dir"]
- return DataManager(Path(data_dir))
+ return DataManager(Path(request.config.data_dir))
def redis_(request):
- return redis.from_url(request.registry.settings["redis.url"])
+ return redis.from_url(request.config.redis_url)
def config_(_request):
return parsed_config
- settings['enable_account_registration'] = asbool(
- settings.get('enable_account_registration', 'false'))
- settings['available_locales'] = aslist(
- settings.get('available_locales', 'en'))
- settings['fietsboek.pages'] = aslist(
- settings.get('fietsboek.pages', ''))
- settings['fietsboek.tile_proxy.disable'] = asbool(
- settings.get('fietsboek.tile_proxy.disable', 'false'))
- settings['thunderforest.maps'] = aslist(
- settings.get('thunderforest.maps', ''))
- settings['fietsboek.default_tile_layers'] = aslist(
- settings.get('fietsboek.default_tile_layers',
- 'osm satellite osmde opentopo topplusopen opensea cycling hiking'))
- settings['fietsboek.tile_layers'] = tileproxy.extract_tile_layers(settings)
-
# Load the pages
page_manager = Pages()
- for path in settings['fietsboek.pages']:
+ for path in parsed_config.pages:
path = Path(path)
if path.is_dir():
page_manager.load_directory(path)
elif path.is_file():
page_manager.load_file(path)
- def pages(request):
+ def pages(_request):
return page_manager
my_session_factory = SignedCookieSessionFactory(settings['session_key'])
diff --git a/fietsboek/config.py b/fietsboek/config.py
index c6da878..e90deda 100644
--- a/fietsboek/config.py
+++ b/fietsboek/config.py
@@ -183,7 +183,7 @@ class Config(BaseModel):
"""Tile layers."""
@validator("session_key")
- def good_session_key(cls, value):
+ def _good_session_key(cls, value):
"""Ensures that the session key has been changed from its default
value.
"""
@@ -191,7 +191,7 @@ class Config(BaseModel):
raise ValueError("You need to edit the default session key!")
@validator("email_smtp_url")
- def known_smtp_url(cls, value):
+ def _known_smtp_url(cls, value):
"""Ensures that the SMTP URL is valid."""
parsed = urllib.parse.urlparse(value)
if parsed.scheme not in {'debug', 'smtp', 'smtp+ssl', 'smtp+starttls'}:
@@ -212,17 +212,16 @@ def parse(config):
errors = []
# First, we try to extract the tile layers.
tile_layers = []
- for key in config.keys():
+ for key, value in config.items():
match = re.match("^fietsboek\\.tile_layer\\.([A-Za-z0-9_-]+)$", key)
if not match:
continue
provider_id = match.group(1)
- name = config[key]
- prefix = f'{key}.'
+ prefix = f'{value}.'
inner = {k[len(prefix):]: v for (k, v) in config.items() if k.startswith(prefix)}
inner['layer_id'] = provider_id
- inner['name'] = name
+ inner['name'] = value
try:
layer_config = TileLayerConfig.parse_obj(inner)
tile_layers.append(layer_config)
diff --git a/fietsboek/email.py b/fietsboek/email.py
index c07c97d..497debe 100644
--- a/fietsboek/email.py
+++ b/fietsboek/email.py
@@ -1,5 +1,6 @@
"""Utility functions for email sending."""
import logging
+import re
import smtplib
import sys
@@ -9,15 +10,14 @@ from email.message import EmailMessage
LOGGER = logging.getLogger(__name__)
-def prepare_message(settings, addr_to, subject):
+def prepare_message(sender, addr_to, subject):
"""Prepares an email message with the right headers.
The body of the message can be set by using
:meth:`~email.message.EmailMessage.set_content` on the returned message.
- :param settings: The application settings, used to access the email
- specific settings.
- :type settings: dict
+ :param sender: The email sender.
+ :type sender: str
:param addr_to: Address of the recipient.
:type addr_to: str
:param subject: Subject of the message.
@@ -25,44 +25,44 @@ def prepare_message(settings, addr_to, subject):
:return: A prepared message.
:rtype: email.message.EmailMessage
"""
- from_address = settings.get('email.from')
- if not from_address:
- LOGGER.warning("`email.from` is not set, make sure to check your configuration!")
message = EmailMessage()
message['To'] = addr_to
- message['From'] = f'Fietsboek <{from_address}>'
+ if '<' not in sender and '>' not in sender:
+ message['From'] = f'Fietsboek <{sender}>'
+ else:
+ message['From'] = sender
message['Subject'] = subject
return message
-def send_message(settings, message):
+def send_message(server_url, username, password, message):
"""Sends an email message using the STMP server configured in the settings.
The recipient is taken from the 'To'-header of the message.
- :parm settings: The application settings.
- :type settings: dict
+ :param server_url: The URL of the server for mail sending.
+ :type server_url: str
+ :param username: The username to authenticate, can be ``None`` or empty.
+ :type username str:
+ :param password: The password to authenticate, can be ``None`` or empty.
+ :type password: str
:param message: The message to send.
:type message: email.message.EmailMessage
"""
- smtp_server = settings.get('email.smtp_url')
- if not smtp_server:
- LOGGER.warning("`email.smtp_url` not set, no email can be sent!")
- return
- smtp_url = urlparse(smtp_server)
- if smtp_url.scheme == 'debug':
+ parsed_url = urlparse(server_url)
+ if parsed_url.scheme == 'debug':
print(message, file=sys.stderr)
return
try:
- if smtp_url.scheme == 'smtp':
- client = smtplib.SMTP(smtp_url.hostname, smtp_url.port)
- elif smtp_url.scheme == 'smtp+ssl':
- client = smtplib.SMTP_SSL(smtp_url.hostname, smtp_url.port)
- elif smtp_url.scheme == 'smtp+starttls':
- client = smtplib.SMTP(smtp_url.hostname, smtp_url.port)
+ if parsed_url.scheme == 'smtp':
+ client = smtplib.SMTP(parsed_url.hostname, parsed_url.port)
+ elif parsed_url.scheme == 'smtp+ssl':
+ client = smtplib.SMTP_SSL(parsed_url.hostname, parsed_url.port)
+ elif parsed_url.scheme == 'smtp+starttls':
+ client = smtplib.SMTP(parsed_url.hostname, parsed_url.port)
client.starttls()
- if 'email.smtp_user' in settings and 'email.smtp_password' in settings:
- client.login(settings['email.smtp_user'], settings['email.smtp_password'])
+ if username and password:
+ client.login(username, password)
client.send_message(message)
client.quit()
except smtplib.SMTPException:
diff --git a/fietsboek/jinja2.py b/fietsboek/jinja2.py
index a0b9457..e7ef522 100644
--- a/fietsboek/jinja2.py
+++ b/fietsboek/jinja2.py
@@ -95,7 +95,7 @@ def global_embed_tile_layers(request):
from .views import tileproxy
tile_sources = tileproxy.sources_for(request)
- if request.registry.settings.get("fietsboek.tile_proxy.disable"):
+ if request.config.disable_tile_proxy:
def _url(source):
return source.url_template
else:
diff --git a/fietsboek/templates/layout.jinja2 b/fietsboek/templates/layout.jinja2
index 9236b36..80981d1 100644
--- a/fietsboek/templates/layout.jinja2
+++ b/fietsboek/templates/layout.jinja2
@@ -81,7 +81,7 @@ const Bestaetigung = false;
<li>
<a class="dropdown-item" href="{{ request.route_url('login') }}">{{ _("page.navbar.login") }}</a>
</li>
- {% if request.registry.settings.get('enable_account_registration') %}
+ {% if request.config.enable_account_registration %}
<li>
<a class="dropdown-item" href="{{ request.route_url('create-account') }}">{{ _("page.navbar.create_account") }}</a>
</li>
diff --git a/fietsboek/views/account.py b/fietsboek/views/account.py
index 2816325..f9f48e9 100644
--- a/fietsboek/views/account.py
+++ b/fietsboek/views/account.py
@@ -18,7 +18,7 @@ def create_account(request):
:rtype: pyramid.response.Response
"""
# pylint: disable=unused-argument
- if not request.registry.settings['enable_account_registration']:
+ if not request.config.enable_account_registration:
return HTTPForbidden()
return {}
@@ -34,7 +34,7 @@ def do_create_account(request):
:rtype: pyramid.response.Response
"""
# pylint: disable=duplicate-code
- if not request.registry.settings['enable_account_registration']:
+ if not request.config.enable_account_registration:
return HTTPForbidden()
password = request.params["password"]
try:
@@ -61,13 +61,18 @@ def do_create_account(request):
request.dbsession.add(token)
message = email.prepare_message(
- request.registry.settings,
+ request.config.email_from,
user.email,
request.localizer.translate(_('email.verify_mail.subject')),
)
message.set_content(request.localizer.translate(_('email.verify.text'))
.format(request.route_url('use-token', uuid=token.uuid)))
- email.send_message(request.registry.settings, message)
+ email.send_message(
+ request.config.email_smtp_url,
+ request.config.email_username,
+ request.config.email_password.get_secret_value(),
+ message,
+ )
request.session.flash(request.localizer.translate(_("flash.a_confirmation_link_has_been_sent")))
return HTTPFound(request.route_url('login'))
diff --git a/fietsboek/views/default.py b/fietsboek/views/default.py
index a36e4c3..f4aaa8f 100644
--- a/fietsboek/views/default.py
+++ b/fietsboek/views/default.py
@@ -166,7 +166,7 @@ def do_password_reset(request):
request.session.flash(request.localizer.translate(_("flash.password_token_generated")))
mail = email.prepare_message(
- request.registry.settings,
+ request.config.email_from,
user.email,
request.localizer.translate(_("page.password_reset.email.subject")),
)
@@ -175,7 +175,12 @@ def do_password_reset(request):
.translate(_("page.password_reset.email.body"))
.format(request.route_url('use-token', uuid=token.uuid))
)
- email.send_message(request.registry.settings, mail)
+ email.send_message(
+ request.config.email_smtp_url,
+ request.config.email_username,
+ request.config.email_password.get_secret_value(),
+ mail,
+ )
return HTTPFound(request.route_url('password-reset'))
diff --git a/fietsboek/views/tileproxy.py b/fietsboek/views/tileproxy.py
index 631982e..3e2abc1 100644
--- a/fietsboek/views/tileproxy.py
+++ b/fietsboek/views/tileproxy.py
@@ -9,7 +9,6 @@ Additionally, this protects the users' IP, as only fietsboek can see it.
import datetime
import random
import logging
-import re
from typing import NamedTuple
from itertools import chain
@@ -177,7 +176,7 @@ def tile_proxy(request):
:return: The HTTP response.
:rtype: pyramid.response.Response
"""
- if request.registry.settings.get("fietsboek.tile_proxy.disable"):
+ if request.config.disable_tile_proxy:
raise HTTPBadRequest("Tile proxying is disabled")
provider = request.matchdict['provider']
@@ -205,7 +204,7 @@ def tile_proxy(request):
headers = {
"user-agent": f"Fietsboek-Tile-Proxy/{__VERSION__}",
}
- from_mail = request.registry.settings.get('email.from')
+ from_mail = request.config.email_from
if from_mail:
headers["from"] = from_mail
@@ -235,42 +234,41 @@ def sources_for(request):
:return: A list of tile sources.
:rtype: list[TileSource]
"""
- settings = request.registry.settings
return [
source for source in chain(
(default_layer for default_layer in DEFAULT_TILE_LAYERS
- if default_layer.key in settings["fietsboek.default_tile_layers"]),
- settings["fietsboek.tile_layers"]
+ if default_layer.key in request.config.default_tile_layers),
+ extract_tile_layers(request.config),
)
if source.access == LayerAccess.PUBLIC or request.identity is not None
]
-def extract_tile_layers(settings):
+def extract_tile_layers(config):
"""Extract all defined tile layers from the settings.
- :param settings: The application settings.
- :type settings: dict
+ :param config: The fietsboek config.
+ :type config: fietsboek.config.Config
:return: A list of extracted tile sources.
:rtype: list[TileSource]
"""
layers = []
- layers.extend(_extract_thunderforest(settings))
- layers.extend(_extract_user_layers(settings))
+ layers.extend(_extract_thunderforest(config))
+ layers.extend(_extract_user_layers(config))
return layers
-def _extract_thunderforest(settings):
+def _extract_thunderforest(config):
# Thunderforest Shortcut!
- tf_api_key = settings.get("thunderforest.api_key")
+ tf_api_key = config.thunderforest_key.get_secret_value()
if tf_api_key:
- tf_access = LayerAccess(settings.get("thunderforest.access", "restricted"))
+ tf_access = config.thunderforest_access
tf_attribution = ' | '.join([
_jb_copy,
_href("https://www.thunderforest.com/", "Thunderforest"),
_href("https://www.openstreetmap.org/", "OpenStreetMap"),
])
- for tf_map in settings["thunderforest.maps"]:
+ for tf_map in config.thunderforest_maps:
url = (f"https://tile.thunderforest.com/{tf_map}/"
f"{{z}}/{{x}}/{{y}}.png?apikey={tf_api_key}")
yield TileSource(
@@ -279,19 +277,15 @@ def _extract_thunderforest(settings):
)
-def _extract_user_layers(settings):
+def _extract_user_layers(config):
# Any other custom maps
- for key in settings.keys():
- match = re.match("^fietsboek\\.tile_layer\\.([A-Za-z0-9_-]+)$", key)
- if not match:
- continue
-
- provider_id = match.group(1)
- name = settings[key]
- url = settings[f"{key}.url"]
- layer_type = LayerType(settings.get(f"{key}.type", "base"))
- zoom = int(settings.get(f"{key}.zoom", 22))
- attribution = settings.get(f"{key}.attribution", _jb_copy)
- access = LayerAccess(settings.get(f"{key}.access", "public"))
-
- yield TileSource(provider_id, name, url, layer_type, zoom, access, attribution)
+ for layer in config.tile_layers:
+ yield TileSource(
+ layer.layer_id,
+ layer.name,
+ layer.url,
+ layer.layer_type,
+ layer.zoom,
+ layer.access,
+ layer.attribution
+ )