Source code for agency.forms.agency
from cgi import FieldStorage
from io import BytesIO
from markupsafe import Markup
from wtforms import EmailField, TextAreaField
from onegov.agency import _
from onegov.agency.collections import ExtendedAgencyCollection
from onegov.agency.models import ExtendedAgency
from onegov.agency.utils import handle_empty_p_tags
from onegov.core.security import Private
from onegov.core.utils import linkify, ensure_scheme
from onegov.form import Form
from onegov.form.fields import ChosenSelectField, HtmlField
from onegov.form.fields import MultiCheckboxField
from onegov.form.fields import UploadField
from onegov.form.validators import FileSizeLimit
from onegov.form.validators import WhitelistedMimeType
from onegov.gis import CoordinatesField
from sqlalchemy import func
from wtforms.fields import StringField
from wtforms.validators import InputRequired
from typing import cast
from typing import Any
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from webob.request import _FieldStorageWithFile
[docs]
class ExtendedAgencyForm(Form):
""" Form to edit agencies. """
[docs]
organigram = UploadField(
label=_('Organigram'),
validators=[
WhitelistedMimeType({
'image/jpeg',
'image/png',
}),
FileSizeLimit(1 * 1024 * 1024)
]
)
[docs]
coordinates = CoordinatesField(
label=_('Location'),
description=_(
'Search for the exact address to set a marker. The zoom of '
'the map will be saved as well.'
),
fieldset=_('Map'),
render_kw={'data-map-type': 'marker'},
)
[docs]
export_fields = MultiCheckboxField(
label=_('Fields to include for each membership'),
choices=[
('membership.title', _('Membership: Title')),
('membership.since', _('Membership: Since')),
('membership.addition', _('Membership: Addition')),
('person.title', _('Person: Title')),
('person.function', _('Person: Function')),
('person.last_name', _('Person: Last Name')),
('person.first_name', _('Person: First Name')),
('person.born', _('Person: Born')),
('person.academic_title', _('Person: Academic Title')),
('person.profession', _('Person: Profession')),
('person.location_address', _('Person: Location address')),
('person.location_code_city', _('Person: Location Code and City')),
('person.postal_address', _('Person: Postal address')),
('person.postal_code_city', _('Person: Postal Code and City')),
('person.political_party', _('Person: Political Party')),
('person.parliamentary_group', _('Person: Parliamentary Group')),
('person.phone', _('Person: Phone')),
('person.phone_direct', _('Person: Direct Phone')),
],
default=['membership.title', 'person.title'],
fieldset=_('PDF Export'),
render_kw={'class_': 'sortable-multi-checkbox'}
)
[docs]
def get_useful_data(self) -> dict[str, Any]: # type:ignore[override]
result = super().get_useful_data(exclude={'csrf_token', 'organigram'})
if self.organigram.data:
result['organigram_file'] = self.organigram.file
if self.portrait.data:
result['portrait'] = linkify(self.portrait.data)
return result
[docs]
def update_model(self, model: ExtendedAgency) -> None:
assert self.title.data is not None
model.title = self.title.data
model.portrait = handle_empty_p_tags(linkify(self.portrait.data))
model.location_address = self.location_address.data
model.location_code_city = self.location_code_city.data
model.postal_address = self.postal_address.data
model.postal_code_city = self.postal_code_city.data
model.phone = self.phone.data
model.phone_direct = self.phone_direct.data
model.email = self.email.data
model.website = self.website.data
model.opening_hours = self.opening_hours.data
model.export_fields = self.export_fields.data or []
if self.organigram.action == 'delete':
del model.organigram
elif self.organigram.action == 'replace' and self.organigram.data:
model.organigram_file = self.organigram.file # type:ignore
model.coordinates = self.coordinates.data
if hasattr(self, 'access'):
model.access = self.access.data
if hasattr(self, 'publication_start'):
model.publication_start = self.publication_start.data
if hasattr(self, 'publication_end'):
model.publication_end = self.publication_end.data
[docs]
def reorder_export_fields(self) -> None:
titles: dict[str, str] = dict(
self.export_fields.choices # type:ignore[arg-type]
)
chosen = self.export_fields.data or []
self.export_fields.choices = [
(choice, titles[choice]) for choice in chosen
] + [
choice for choice in self.export_fields.choices
if choice[0] not in chosen
]
[docs]
def apply_model(self, model: ExtendedAgency) -> None:
self.title.data = model.title
self.portrait.data = model.portrait or Markup('')
self.location_address.data = model.location_address
self.location_code_city.data = model.location_code_city
self.postal_address.data = model.postal_address
self.postal_code_city.data = model.postal_code_city
self.phone.data = model.phone
self.phone_direct.data = model.phone_direct
self.email.data = model.email
self.website.data = model.website
self.opening_hours.data = model.opening_hours
self.export_fields.data = model.export_fields
if model.organigram_file:
fs = cast('_FieldStorageWithFile', FieldStorage())
fs.file = BytesIO(model.organigram_file.read())
fs.type = model.organigram_file.content_type
fs.filename = model.organigram_file.filename
self.organigram.data = self.organigram.process_fieldstorage(fs)
self.coordinates.data = model.coordinates
if hasattr(self, 'access'):
self.access.data = model.access
if hasattr(self, 'publication_start'):
self.publication_start.process_data(model.publication_start)
if hasattr(self, 'publication_end'):
self.publication_end.process_data(model.publication_end)
self.reorder_export_fields()
[docs]
class MoveAgencyForm(Form):
""" Form to move an agency. """
[docs]
parent_id = ChosenSelectField(
label=_('Destination'),
choices=[],
validators=[
InputRequired()
]
)
[docs]
def on_request(self) -> None:
self.request.include('common')
self.request.include('chosen')
agencies = ExtendedAgencyCollection(self.request.session)
self.parent_id.choices = [
(str(agency.id), agency.title)
for agency in agencies.query().order_by(None).order_by(
func.unaccent(ExtendedAgency.title)
)
if self.request.has_permission(agency, Private)
]
if self.request.has_permission(agencies, Private):
self.parent_id.choices.insert(
0, ('root', self.request.translate(_('- Root -')))
)
[docs]
def ensure_valid_parent(self) -> bool:
"""
As a new destination (parent page) every menu item is valid except
yourself. You cannot assign yourself as the new destination
:return: bool
"""
if self.parent_id.data and self.parent_id.data.isdigit():
new_parent_id = int(self.parent_id.data)
# prevent selecting yourself as new parent
if self.model.id == new_parent_id:
assert isinstance(self.parent_id.errors, list)
self.parent_id.errors.append(
_('Invalid destination selected'))
return False
return True
[docs]
def update_model(self, model: ExtendedAgency) -> None:
session = self.request.session
agencies = ExtendedAgencyCollection(session)
new_parent_id = None
new_parent = None
if self.parent_id.data and self.parent_id.data.isdigit():
new_parent_id = int(self.parent_id.data)
new_parent = agencies.by_id(new_parent_id)
model.name = agencies.get_unique_child_name(model.title, new_parent)
model.parent_id = new_parent_id
[docs]
def apply_model(self, model: ExtendedAgency) -> None:
def remove(item: ExtendedAgency) -> None:
choice = (str(item.id), item.title)
if choice in self.parent_id.choices:
assert isinstance(self.parent_id.choices, list)
self.parent_id.choices.remove(choice)
def remove_with_children(item: ExtendedAgency) -> None:
remove(item)
for child in item.children:
remove_with_children(child)
if model.parent:
remove(model.parent)
else:
assert isinstance(self.parent_id.choices, list)
self.parent_id.choices.pop(0)
remove_with_children(model)