calendar-social/calsocial/forms.py

197 lines
6.0 KiB
Python
Raw Normal View History

# 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
2018-06-29 12:00:45 +00:00
from flask_wtf import FlaskForm
import pytz
from wtforms import BooleanField, PasswordField, SelectField, StringField
2018-06-30 04:44:45 +00:00
from wtforms.ext.dateutil.fields import DateTimeField
2018-06-29 12:00:45 +00:00
from wtforms.validators import DataRequired, Email, ValidationError
2018-06-30 04:44:45 +00:00
from wtforms.widgets import TextArea
2018-06-29 12:00:45 +00:00
class RegistrationForm(FlaskForm):
"""Registration form
"""
2018-06-29 12:00:45 +00:00
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.
"""
2018-06-29 12:00:45 +00:00
if field.data != self.password.data:
raise ValidationError(_('The two passwords must match!'))
2018-06-30 04:44:45 +00:00
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)
2018-06-30 04:44:45 +00:00
class EventForm(FlaskForm):
"""Form for event creation/editing
"""
2018-06-30 04:44:45 +00:00
title = StringField(_('Title'), validators=[DataRequired()])
time_zone = TimezoneField(_('Time zone'), validators=[DataRequired()])
2018-06-30 04:44:45 +00:00
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)
2018-06-30 04:44:45 +00:00
def validate_end_time(self, field):
"""Validate the ``end_time`` field
Its value must be later than the value of the ``start_time`` field.
"""
2018-06-30 04:44:45 +00:00
if field.data < self.start_time.data:
raise ValidationError(_('End time must be later than start time!'))
2018-07-03 11:15:17 +00:00
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