"""WTForms — all forms get CSRF protection automatically via Flask-WTF.""" from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileAllowed, FileRequired from wtforms import StringField, PasswordField, BooleanField, IntegerField, SelectField, DateField, TextAreaField, SubmitField from wtforms.validators import DataRequired, Email, Length, EqualTo, Optional, NumberRange, Regexp class LoginForm(FlaskForm): username = StringField("Username", validators=[DataRequired(), Length(min=2, max=64)]) password = PasswordField("Password", validators=[DataRequired(), Length(min=1, max=200)]) submit = SubmitField("Sign in") class SetupAdminForm(FlaskForm): """First-run form: create the very first administrator account.""" username = StringField("Admin username", validators=[ DataRequired(), Length(min=2, max=64), Regexp(r"^[A-Za-z0-9_.\-]+$", message="Letters, digits, underscore, dot, dash only."), ]) email = StringField("Admin email", validators=[DataRequired(), Email(), Length(max=255)]) password = PasswordField("Password", validators=[ DataRequired(), Length(min=10, max=200, message="Min 10 characters"), ]) confirm = PasswordField("Confirm password", validators=[ DataRequired(), EqualTo("password", message="Passwords must match."), ]) submit = SubmitField("Create administrator") class AvatarForm(FlaskForm): avatar = FileField("Avatar image", validators=[ FileRequired(message="Choose an image file."), FileAllowed(["png", "jpg", "jpeg", "gif", "webp"], "Images only (png, jpg, gif, webp)."), ]) submit = SubmitField("Upload avatar") class MfaForm(FlaskForm): code = StringField("Authentication code", validators=[ DataRequired(), Length(min=6, max=6, message="Must be 6 digits"), Regexp(r"^\d{6}$", message="Digits only"), ]) submit = SubmitField("Verify") class MfaSetupForm(FlaskForm): code = StringField("Verification code", validators=[ DataRequired(), Length(min=6, max=6), Regexp(r"^\d{6}$"), ]) submit = SubmitField("Enable MFA") class ChangePasswordForm(FlaskForm): current_password = PasswordField("Current password", validators=[DataRequired()]) new_password = PasswordField("New password", validators=[ DataRequired(), Length(min=10, max=200, message="Min 10 characters"), ]) confirm = PasswordField("Confirm new password", validators=[ DataRequired(), EqualTo("new_password", message="Passwords must match."), ]) submit = SubmitField("Change password") class AlertForm(FlaskForm): title = StringField("Title", validators=[DataRequired(), Length(max=200)]) category = StringField("Category", validators=[Optional(), Length(max=64)]) description = TextAreaField("Description", validators=[Optional(), Length(max=5000)]) expiration_date = DateField("Expiration date", validators=[DataRequired()], format="%Y-%m-%d") reminder_days = StringField( "Reminder days (comma-separated, days before expiration)", validators=[DataRequired(), Regexp(r"^[\d,\s]+$", message="Numbers separated by commas only")], default="30,14,7,1", ) is_active = BooleanField("Active", default=True) submit = SubmitField("Save") class UserForm(FlaskForm): username = StringField("Username", validators=[ DataRequired(), Length(min=2, max=64), Regexp(r"^[A-Za-z0-9_.\-]+$", message="Letters, digits, underscore, dot, dash only."), ]) email = StringField("Email", validators=[DataRequired(), Email(), Length(max=255)]) role = SelectField("Role", choices=[("user", "User (view only)"), ("admin", "Administrator")]) is_active = BooleanField("Active", default=True) password = PasswordField("Password (leave blank to keep current)", validators=[Optional(), Length(min=10, max=200)]) submit = SubmitField("Save") class MailConfigForm(FlaskForm): smtp_host = StringField("SMTP Host", validators=[DataRequired(), Length(max=255)]) smtp_port = IntegerField("Port", validators=[DataRequired(), NumberRange(min=1, max=65535)]) smtp_username = StringField("Username", validators=[Optional(), Length(max=255)]) smtp_password = PasswordField("Password (leave blank to keep current)", validators=[Optional(), Length(max=255)]) smtp_encryption = SelectField("Encryption", choices=[ ("none", "None (plain — not recommended)"), ("starttls", "STARTTLS (port 587)"), ("ssl", "SSL/TLS (port 465)"), ]) smtp_from_address = StringField("From address", validators=[DataRequired(), Email(), Length(max=255)]) smtp_from_name = StringField("From name", validators=[Optional(), Length(max=255)]) # ---- IMAP: used to save a copy of every sent message to the Sent folder ---- imap_enabled = BooleanField("Save a copy of sent mail to the mailbox (IMAP)", default=True) imap_host = StringField("IMAP Host", validators=[Optional(), Length(max=255)]) imap_port = IntegerField("IMAP Port", validators=[Optional(), NumberRange(min=1, max=65535)]) imap_username = StringField("IMAP Username (blank = use SMTP username)", validators=[Optional(), Length(max=255)]) imap_password = PasswordField("IMAP Password (blank = keep current / use SMTP password)", validators=[Optional(), Length(max=255)]) imap_sent_folder = StringField("Sent folder name", validators=[Optional(), Length(max=128)]) submit = SubmitField("Save mail configuration") class TestMailForm(FlaskForm): test_recipient = StringField("Send test email to", validators=[DataRequired(), Email()]) submit = SubmitField("Send test email") class LogEmailForm(FlaskForm): """Email an exported log (CSV attachment) to a destination address.""" log_recipient = StringField("Email log to", validators=[DataRequired(), Email()]) submit = SubmitField("Email log") class GlobalSettingsForm(FlaskForm): default_reminder_days = StringField( "Default reminder days (comma-separated)", validators=[DataRequired(), Regexp(r"^[\d,\s]+$")], ) submit = SubmitField("Save settings")