M byceps/blueprints/admin/email/forms.py => byceps/blueprints/admin/email/forms.py +8 -1
@@ 6,9 6,10 @@ byceps.blueprints.admin.email.forms
:License: Revised BSD (see `LICENSE` file for details)
"""
-from wtforms import StringField
+from wtforms import SelectField, StringField
from wtforms.validators import InputRequired, Length, Optional
+from ....services.brand import service as brand_service
from ....util.l10n import LocalizedForm
@@ 17,9 18,15 @@ class _BaseForm(LocalizedForm):
sender_name = StringField('Absender-Name', validators=[Optional()])
contact_address = StringField('Kontaktadresse', validators=[Optional()])
+ def set_brand_choices(self):
+ brands = brand_service.get_all_brands()
+ brands.sort(key=lambda brand: brand.title)
+ self.brand_id.choices = [(brand.id, brand.title) for brand in brands]
+
class CreateForm(_BaseForm):
config_id = StringField('ID', validators=[InputRequired()])
+ brand_id = SelectField('Marke', validators=[InputRequired()])
class UpdateForm(_BaseForm):
M byceps/blueprints/admin/email/templates/admin/email/create_form.html => byceps/blueprints/admin/email/templates/admin/email/create_form.html +1 -0
@@ 16,6 16,7 @@
<form action="{{ url_for('.create') }}" method="post">
{% call form_fieldset() %}
{{ form_field(form.config_id, placeholder='superlan', autofocus='autofocus') }}
+ {{ form_field(form.brand_id) }}
{{ form_field(form.sender_address, placeholder='noreply@superlan.example') }}
{{ form_field(form.sender_name, placeholder='SuperLAN') }}
{{ form_field(form.contact_address, placeholder='info@superlan.example') }}
M byceps/blueprints/admin/email/views.py => byceps/blueprints/admin/email/views.py +4 -0
@@ 45,6 45,7 @@ def index():
def create_form(erroneous_form=None):
"""Show form to create an e-mail config."""
form = erroneous_form if erroneous_form else CreateForm()
+ form.set_brand_choices()
return {
'form': form,
@@ 56,17 57,20 @@ def create_form(erroneous_form=None):
def create():
"""Create an e-mail config."""
form = CreateForm(request.form)
+ form.set_brand_choices()
if not form.validate():
return create_form(form)
config_id = form.config_id.data.strip()
+ brand_id = form.brand_id.data
sender_address = form.sender_address.data.strip()
sender_name = form.sender_name.data.strip() or None
contact_address = form.contact_address.data.strip() or None
config = email_service.create_config(
config_id,
+ brand_id,
sender_address,
sender_name=sender_name,
contact_address=contact_address,
M byceps/services/email/models.py => byceps/services/email/models.py +4 -0
@@ 9,6 9,7 @@ byceps.services.email.models
from typing import Optional
from ...database import db
+from ...typing import BrandID
from ...util.instances import ReprBuilder
@@ 18,6 19,7 @@ class EmailConfig(db.Model):
__tablename__ = 'email_configs'
id = db.Column(db.UnicodeText, primary_key=True)
+ brand_id = db.Column(db.UnicodeText, db.ForeignKey('brands.id'), index=True, nullable=False)
sender_address = db.Column(db.UnicodeText, nullable=False)
sender_name = db.Column(db.UnicodeText, nullable=True)
contact_address = db.Column(db.UnicodeText, nullable=True)
@@ 25,12 27,14 @@ class EmailConfig(db.Model):
def __init__(
self,
config_id: str,
+ brand_id: BrandID,
sender_address: str,
*,
sender_name: Optional[str] = None,
contact_address: Optional[str] = None,
) -> None:
self.id = config_id
+ self.brand_id = brand_id
self.sender_address = sender_address
self.sender_name = sender_name
self.contact_address = contact_address
M byceps/services/email/service.py => byceps/services/email/service.py +6 -0
@@ 12,6 12,7 @@ from sqlalchemy.exc import IntegrityError
from ...database import db, upsert
from ... import email
+from ...typing import BrandID
from ...util.jobqueue import enqueue
from .models import EmailConfig as DbEmailConfig
@@ 24,6 25,7 @@ class UnknownEmailConfigId(ValueError):
def create_config(
config_id: str,
+ brand_id: BrandID,
sender_address: str,
*,
sender_name: Optional[str] = None,
@@ 32,6 34,7 @@ def create_config(
"""Create a configuration."""
config = DbEmailConfig(
config_id,
+ brand_id,
sender_address,
sender_name=sender_name,
contact_address=contact_address,
@@ 111,6 114,7 @@ def get_config(config_id: str) -> EmailConfig:
def set_config(
config_id: str,
+ brand_id: BrandID,
sender_address: str,
*,
sender_name: Optional[str] = None,
@@ 120,6 124,7 @@ def set_config(
table = DbEmailConfig.__table__
identifier = {
'id': config_id,
+ 'brand_id': brand_id,
'sender_address': sender_address,
}
replacement = {
@@ 168,6 173,7 @@ def _db_entity_to_config(config: DbEmailConfig) -> EmailConfig:
return EmailConfig(
config.id,
+ config.brand_id,
sender,
config.contact_address,
)
M byceps/services/email/transfer/models.py => byceps/services/email/transfer/models.py +3 -0
@@ 10,6 10,8 @@ from dataclasses import dataclass
from email.utils import formataddr
from typing import List
+from ....typing import BrandID
+
@dataclass(frozen=True)
class Sender:
@@ 25,6 27,7 @@ class Sender:
@dataclass(frozen=True)
class EmailConfig:
id: str
+ brand_id: BrandID
sender: Sender
contact_address: str
M tests/conftest.py => tests/conftest.py +17 -5
@@ 20,6 20,7 @@ from byceps.services.user import (
command_service as user_command_service,
service as user_service,
)
+from byceps.typing import BrandID
from tests.base import create_admin_app, create_site_app
from tests.database import set_up_database, tear_down_database
@@ 190,9 191,12 @@ def deleted_user(make_user):
@pytest.fixture(scope='session')
-def make_email_config(admin_app):
+def make_email_config(admin_app, brand):
+ configs = []
+
def _wrapper(
config_id: str,
+ brand_id: BrandID,
sender_address: str,
*,
sender_name: Optional[str] = None,
@@ 200,20 204,28 @@ def make_email_config(admin_app):
):
email_service.set_config(
config_id,
+ brand_id,
sender_address,
sender_name=sender_name,
contact_address=contact_address,
)
- return email_service.get_config(config_id)
+ config = email_service.get_config(config_id)
+ configs.append(config)
- return _wrapper
+ return config
+
+ yield _wrapper
+
+ for config in configs:
+ email_service.delete_config(config.id)
+# Dependency on `brand` avoids error on clean up.
@pytest.fixture(scope='session')
-def email_config(make_email_config):
+def email_config(make_email_config, brand):
return make_email_config(
- DEFAULT_EMAIL_CONFIG_ID, sender_address='noreply@acmecon.test'
+ DEFAULT_EMAIL_CONFIG_ID, brand.id, sender_address='noreply@acmecon.test'
)
M tests/integration/blueprints/admin/email/test_create_config.py => tests/integration/blueprints/admin/email/test_create_config.py +6 -2
@@ 6,13 6,14 @@
import byceps.services.email.service as email_service
-def test_create_minimal_config(email_admin_client):
+def test_create_minimal_config(email_admin_client, brand):
config_id = 'acme-minimal'
assert email_service.find_config(config_id) is None
url = '/admin/email/configs'
form_data = {
'config_id': config_id,
+ 'brand_id': brand.id,
'sender_address': 'noreply@acme.example',
}
response = email_admin_client.post(url, data=form_data)
@@ 20,6 21,7 @@ def test_create_minimal_config(email_admin_client):
config = email_service.find_config(config_id)
assert config is not None
assert config.id == config_id
+ assert config.brand_id == brand.id
assert config.sender is not None
assert config.sender.address == 'noreply@acme.example'
assert config.sender.name is None
@@ 29,13 31,14 @@ def test_create_minimal_config(email_admin_client):
email_service.delete_config(config_id)
-def test_create_full_config(email_admin_client):
+def test_create_full_config(email_admin_client, brand):
config_id = 'acme-full'
assert email_service.find_config(config_id) is None
url = '/admin/email/configs'
form_data = {
'config_id': config_id,
+ 'brand_id': brand.id,
'sender_address': 'noreply@acme.example',
'sender_name': 'ACME Corp.',
'contact_address': 'info@acme.example',
@@ 45,6 48,7 @@ def test_create_full_config(email_admin_client):
config = email_service.find_config(config_id)
assert config is not None
assert config.id == config_id
+ assert config.brand_id == brand.id
assert config.sender is not None
assert config.sender.address == 'noreply@acme.example'
assert config.sender.name == 'ACME Corp.'
M tests/integration/blueprints/admin/email/test_delete_config.py => tests/integration/blueprints/admin/email/test_delete_config.py +4 -2
@@ 6,10 6,12 @@
import byceps.services.email.service as email_service
-def test_delete_config(email_admin_client):
+def test_delete_config(email_admin_client, brand):
config_id = 'kann-weg'
- assert email_service.create_config(config_id, 'noreply@acme.example')
+ assert email_service.create_config(
+ config_id, brand.id, 'noreply@acme.example'
+ )
assert email_service.find_config(config_id) is not None
url = f'/admin/email/configs/{config_id}'
M tests/integration/blueprints/site/user_message/test_send.py => tests/integration/blueprints/site/user_message/test_send.py +8 -0
@@ 16,9 16,11 @@ from tests.helpers import create_site, http_client, login_user
def site1(brand, make_email_config):
email_config = make_email_config(
'acme-noreply',
+ brand.id,
sender_address='noreply@acmecon.test',
sender_name='ACME Entertainment Convention',
)
+
site = create_site(
'acmecon-website-1',
brand.id,
@@ 26,7 28,9 @@ def site1(brand, make_email_config):
server_name='www1.acmecon.test',
email_config_id=email_config.id,
)
+
yield site
+
site_service.delete_site(site.id)
@@ 34,10 38,12 @@ def site1(brand, make_email_config):
def site2(brand, make_email_config):
email_config = make_email_config(
'acme-noreply-with-contact-address',
+ brand.id,
sender_address='noreply@acmecon.test',
sender_name='ACME Entertainment Convention',
contact_address='help@acmecon.test',
)
+
site = create_site(
'acmecon-website-2',
brand.id,
@@ 45,7 51,9 @@ def site2(brand, make_email_config):
server_name='www2.acmecon.test',
email_config_id=email_config.id,
)
+
yield site
+
site_service.delete_site(site.id)