Source code for feriennet.forms.userprofile
import string
from onegov.feriennet import _
from onegov.feriennet.utils import encode_name, decode_name
from onegov.form import Form
from onegov.form.validators import Stdnum
from wtforms.fields import RadioField
from wtforms.fields import StringField
from wtforms.fields import TextAreaField
from wtforms.fields import URLField
from wtforms.validators import Optional, URL, ValidationError, InputRequired
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from onegov.feriennet.request import FeriennetRequest
from onegov.user import User
[docs]
class UserProfileForm(Form):
""" Custom userprofile form for feriennet """
[docs]
extra_fields = (
'salutation',
'organisation',
'address',
'zip_code',
'place',
'email',
'phone',
'website',
'emergency',
'ticket_statistics',
'bank_account',
'bank_beneficiary',
'political_municipality'
)
[docs]
ticket_statistics = RadioField(
label=_('Send a periodic status e-mail.'),
fieldset=_('General'),
default='weekly',
validators=[InputRequired()],
choices=(
('daily', _(
'Daily (exluding the weekend)')),
('weekly', _(
'Weekly (on mondays)')),
('monthly', _(
'Monthly (on first monday of the month)')),
('never', _(
'Never')),
)
)
[docs]
salutation = RadioField(
label=_('Salutation'),
fieldset=_('Personal'),
choices=[
('mr', _('Mr.')),
('ms', _('Ms.')),
],
validators=[InputRequired()]
)
[docs]
first_name = StringField(
label=_('First Name'),
fieldset=_('Personal'),
validators=[InputRequired()]
)
[docs]
last_name = StringField(
label=_('Last Name'),
fieldset=_('Personal'),
validators=[InputRequired()]
)
[docs]
address = TextAreaField(
label=_('Address'),
fieldset=_('Personal'),
render_kw={'rows': 4},
validators=[InputRequired()]
)
[docs]
zip_code = StringField(
label=_('Zip Code'),
fieldset=_('Personal'),
validators=[InputRequired()]
)
[docs]
place = StringField(
label=_('Place'),
fieldset=_('Personal'),
validators=[InputRequired()]
)
[docs]
political_municipality = StringField(
label=_('Political Municipality'),
fieldset=_('Personal'),
validators=[InputRequired()]
)
[docs]
email = StringField(
label=_('Public E-Mail Address'),
fieldset=_('Personal'),
description=_('If different than username')
)
[docs]
emergency = StringField(
label=_('Emergency Contact'),
fieldset=_('Personal'),
description=_('012 345 67 89 (Peter Muster)'),
validators=[InputRequired()]
)
[docs]
website = URLField(
label=_('Website'),
fieldset=_('Personal'),
description=_('Website address including http:// or https://'),
validators=[Optional(), URL()]
)
[docs]
bank_account = StringField(
label=_('Bank Account (IBAN)'),
fieldset=_('Personal'),
validators=[Stdnum(format='iban')]
)
@property
[docs]
def name(self) -> str:
assert self.first_name.data is not None
assert self.last_name.data is not None
return encode_name(
self.first_name.data.strip(),
self.last_name.data.strip()
)
@name.setter
def name(self, value: str) -> None:
self.first_name.data, self.last_name.data = decode_name(value)
[docs]
def on_request(self) -> None:
self.toggle_political_municipality()
self.toggle_ticket_statistics()
@property
[docs]
def show_political_municipality(self) -> bool | None:
return self.request.app.org.meta.get('show_political_municipality')
[docs]
def toggle_political_municipality(self) -> None:
if not self.show_political_municipality:
self.delete_field('political_municipality')
@property
[docs]
def show_ticket_statistics(self) -> bool:
roles = self.request.app.settings.org.status_mail_roles
return self.request.current_role in roles
[docs]
def toggle_ticket_statistics(self) -> None:
if not self.show_ticket_statistics:
self.delete_field('ticket_statistics')
[docs]
def validate_emergency(self, field: StringField) -> None:
if field.data:
characters = tuple(c for c in field.data if c.strip())
numbers = sum(1 for c in characters if c in string.digits)
chars = sum(1 for c in characters if c in string.ascii_letters)
if numbers < 9 or chars < 5:
raise ValidationError(
_('Please enter both a phone number and a name'))
[docs]
def ensure_beneificary_if_bank_account(self) -> bool | None:
if self.bank_account.data and not self.bank_beneficiary.data:
assert isinstance(self.bank_beneficiary.errors, list)
self.bank_beneficiary.errors.append(_(
'A beneficiary is required if a bank account is given.'
))
return False
return None
[docs]
def should_skip_key(self, key: str) -> bool:
if key == 'political_municipality':
if not self.show_political_municipality:
return True
if key == 'ticket_statistics':
if not self.show_ticket_statistics:
return True
return False
[docs]
def populate_obj(self, model: 'User') -> None: # type:ignore[override]
super().populate_obj(model)
if model:
model.data = model.data or {}
model.realname = self.name
for key in self.extra_fields:
if self.should_skip_key(key):
continue
model.data[key] = self.data.get(key)
# strip whitespace from all fields
if isinstance(model.data[key], str):
model.data[key] = model.data[key].strip()
[docs]
def process_obj(self, model: 'User') -> None: # type:ignore[override]
super().process_obj(model)
if model:
modeldata = model.data or {}
# NOTE: Technically this should always be set, but it could be
# empty for externally created users. decode_name can deal
# with `None` so we should not assert here, we need an
# asymmetric property to model this behavior
self.name = model.realname # type:ignore[assignment]
for key in self.extra_fields:
if self.should_skip_key(key):
continue
default = getattr(self, key).default
getattr(self, key).data = modeldata.get(key, default)