calendar-social/calsocial/forms.py

197 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Forms for Calendar.social
"""
from flask_babelex import lazy_gettext as _
from flask_security.forms import LoginForm as BaseLoginForm
from flask_wtf import FlaskForm
import pytz
from wtforms import BooleanField, PasswordField, SelectField, StringField
from wtforms.ext.dateutil.fields import DateTimeField
from wtforms.validators import DataRequired, Email, ValidationError
from wtforms.widgets import TextArea
class RegistrationForm(FlaskForm):
"""Registration form
"""
username = StringField(_('Username'), validators=[DataRequired()])
email = StringField(_('Email address'), validators=[Email()])
password = PasswordField(_('Password'), validators=[DataRequired()])
password_retype = PasswordField(_('Password, once more'), validators=[DataRequired()])
def validate_password_retype(self, field):
"""Validate the ``password_retype`` field
Its value must match the ``password`` fields.
"""
if field.data != self.password.data:
raise ValidationError(_('The two passwords must match!'))
class TimezoneField(SelectField):
"""Field for selecting a time zone
Note: this field overrides whatever is passed to the ``choices`` parameter, and fills
``choices`` with the common timezones of pytz. In every other aspects, it behaves exactly
like `SelectField`.
"""
def __init__(self, *args, **kwargs):
self.data = None
kwargs.update({
'choices': [
(pytz.timezone(tz), tz.replace('_', ' '))
for tz in pytz.common_timezones
],
})
SelectField.__init__(self, *args, **kwargs)
def process_formdata(self, valuelist):
if not valuelist:
self.data = None
return
try:
self.data = pytz.timezone(valuelist[0])
except pytz.exceptions.UnknownTimeZoneError:
self.data = None
raise ValueError('Unknown time zone')
@staticmethod
def is_pytz_instance(value):
"""Check if ``value`` is a valid pytz time zone instance
"""
return value is pytz.UTC or isinstance(value, pytz.tzinfo.BaseTzInfo)
def process_data(self, value):
if value is None:
self.data = None
return
if self.is_pytz_instance(value):
self.data = value
return
try:
self.data = pytz.timezone(value)
except pytz.exceptions.UnknownTimeZoneError:
raise ValueError(f'Unknown time zone {value}')
def iter_choices(self):
for value, label in self.choices:
yield (value, label, value == self.data)
class EventForm(FlaskForm):
"""Form for event creation/editing
"""
title = StringField(_('Title'), validators=[DataRequired()])
time_zone = TimezoneField(_('Time zone'), validators=[DataRequired()])
start_time = DateTimeField(_('Start time'), validators=[DataRequired()])
end_time = DateTimeField(_('End time'), validators=[DataRequired()])
all_day = BooleanField(_('All day'))
description = StringField(_('Description'), widget=TextArea())
def populate_obj(self, obj):
"""Populate ``obj`` with event data
"""
FlaskForm.populate_obj(self, obj)
timezone = self.time_zone.data
obj.time_zone = str(timezone)
obj.start_time = timezone.localize(self.start_time.data).astimezone(pytz.utc)
obj.end_time = timezone.localize(self.end_time.data).astimezone(pytz.utc)
def validate_end_time(self, field):
"""Validate the ``end_time`` field
Its value must be later than the value of the ``start_time`` field.
"""
if field.data < self.start_time.data:
raise ValidationError(_('End time must be later than start time!'))
class SettingsForm(FlaskForm):
"""Form for user settings
"""
timezone = TimezoneField(_('Time zone'), validators=[DataRequired()])
def __init__(self, user, *args, **kwargs):
self.user = user
kwargs.setdefault('timezone', user.timezone)
FlaskForm.__init__(self, *args, **kwargs)
def populate_obj(self, user):
"""Populate ``obj`` with event data
"""
for name, field in self._fields.items():
if not (hasattr(self.__class__, name) and not hasattr(FlaskForm, name)):
continue
user.settings[name] = str(field.data)
class LoginForm(BaseLoginForm):
"""Login form for Calendar.social
"""
email = StringField(_('Username or email'), validators=[DataRequired()])
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.user = None
def validate(self):
from flask_security.utils import _datastore
from flask_security.utils import verify_and_update_password
from .models import AuditLog
ret = super(LoginForm, self).validate()
if self.user is None:
self.user = _datastore.get_user(self.email.data)
if self.user is None:
# We cant figure out the user thats trying to log in, just return
return ret
if not verify_and_update_password(self.password.data, self.user):
AuditLog.log(self.user, AuditLog.TYPE_LOGIN_FAIL)
return ret