Source code for fluxscoreboard.forms.admin

# -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import, print_function
from fluxscoreboard.forms import CSRFForm
from fluxscoreboard.forms._fields import (IntegerOrEvaluatedField, ButtonWidget,
    team_size_field, TZDateTimeField)
from fluxscoreboard.forms._validators import (email_length_validator,
    password_length_validator_conditional, password_required_if_new,
    required_validator, name_length_validator, not_dynamic, only_if_dynamic,
    required_except, required_or_not_allowed, dynamic_check_multiple_allowed)
from fluxscoreboard.models import dynamic_challenges
from fluxscoreboard.models.challenge import (get_all_challenges,
    get_all_categories)
from fluxscoreboard.models.country import get_all_countries
from fluxscoreboard.models.team import get_all_teams
from wtforms import validators
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from wtforms.fields.core import BooleanField, SelectField
from wtforms.fields.html5 import EmailField, IntegerField
from wtforms.fields.simple import (TextAreaField, SubmitField, HiddenField,
    TextField, PasswordField)


def get_dynamic_module_choices():
    module_list = [(name, module.title())
                   for name, module in dynamic_challenges.registry.items()]
    return [('', '-- Not dynamic --')] + module_list


[docs]class NewsForm(CSRFForm): """ Form to add or edit an announcement. Attrs: ``message``: The announcement message. Required. ``published``: Whether the announcement should be published. Required. ``challenge``: A list of challenges to choose from, alternatively a blank field with text "-- General Announcement --" to not assign it to a specific challenge. ``id``: Hidden field keeps track of challenge. ``submit``: Save the announcement to the database. ``cancel``: Do not save the announcement. """ message = TextAreaField("Message", validators=[required_validator]) published = BooleanField("Published") challenge = QuerySelectField("Challenge", query_factory=get_all_challenges, allow_blank=True, blank_text='-- General Announcement --') id = HiddenField() submit = SubmitField("Save") cancel = SubmitField("Cancel")
[docs]class ChallengeForm(CSRFForm): """ Form to add or edit a challenge. Attrs: ``title``: Title of the challenge. Required. ``text``: Description of the challenge. Required. ``solution``: Solution. Required. ``base_points``: How many base points is this challenge worth? Only required if the challenge is not manual, otherwise not allowed to be anything other than 0 or empty. ``online``: If the challenge is online. ``manual``: If the points for this challenge are given manually. ``id``: Track the id of the challenge. ``submit``: Save challenge. ``cancel``: Abort saving. """ title = TextField("Title", validators=[required_validator, validators.Length(max=255), ] ) text = TextAreaField("Text", validators=[required_except(["dynamic"])] ) solution = TextField( "Solution", validators=[required_or_not_allowed(["manual", "dynamic"])]) base_points = IntegerOrEvaluatedField( "Base Points", validators=[required_or_not_allowed(["manual", "dynamic"])]) author = TextField("Author(s)") category = QuerySelectField("Category", query_factory=get_all_categories, allow_blank=False, blank_text='-- Choose a category --') online = BooleanField("Online") manual = BooleanField("Manual Challenge", validators=[not_dynamic]) dynamic = BooleanField( "Dynamic Challenge", description=("A dynamic challenge is a challenge that uses a custom " "Python module for validation, which, of course, has to " "be written separately. If you check this, you have to " "select the corresponding module for this challenge " "below. Also, you may NOT make it a manual challenge as " "well!")) module = SelectField( "Dynamic Module", description=("Which module should be used for this dynamic challenge " "(only relevant if dynamic is checked above), see above " "for details."), choices=get_dynamic_module_choices(), validators=[only_if_dynamic, dynamic_check_multiple_allowed] ) published = BooleanField( "Published", description=("An unpublished challenge will not be displayed in the " "frontend.")) has_token = BooleanField( "Has Token", description=("If this is active, the teams token will be displayed " "below the challenge so they can provide it to the " "challenge. A challenge can use it to identify teams. " "Check docs for move info.")) id = HiddenField() submit = SubmitField("Save") cancel = SubmitField("Cancel")
[docs]class CategoryForm(CSRFForm): """ Form to add or edit a category. Attrs: ``name``: Title of the category. Required. ``id``: Track the id of the category. ``submit``: Save category. ``cancel``: Abort saving. """ name = TextField("Name", validators=[required_validator, validators.Length(max=255), ] ) id = HiddenField() submit = SubmitField("Save") cancel = SubmitField("Cancel")
[docs]class TeamForm(CSRFForm): """ Form to add or edit a team. The same restrictions as on :class:`fluxscoreboard.forms.front.RegisterForm` apply. Attrs: ``name``: The name of the name of the team. Required. ``password``: The team's password. Only required if the team is new. ``email``: E-Mail address. Required. ``country``: Location of team. Required. ``active``: If the team is active, i.e. able to log in. ``local``: If the team is local or remote. ``id``: Tracks the id of the team. ``submit``: Save the team. ``cancel``: Don't save. """ name = TextField("Team Name", validators=[required_validator, name_length_validator, ] ) password = PasswordField("Password", validators=[password_length_validator_conditional, password_required_if_new, ] ) email = EmailField("E-Mail Address", validators=[required_validator, email_length_validator, ] ) size = team_size_field() country = QuerySelectField("Country/State", query_factory=get_all_countries ) active = BooleanField("Active") local = BooleanField("Local") id = HiddenField(default=None) submit = SubmitField("Save") cancel = SubmitField("Cancel")
[docs]class IPSearchForm(CSRFForm): """ Form to search for an IP address and find the resulting team(s). """ term = TextField("IP Address") by_ip = SubmitField("Search by IP") by_name = SubmitField("Search by Team Name")
[docs]class SubmissionForm(CSRFForm): """ Form to add or edit a submission of a team. Attrs: ``challenge``: The :class:`fluxscoreboard.models.challenge.Challenge` to be chosen from a list. Required. ``team``: The :class:`fluxscoreboard.models.team.Team` to be chosem from a list. Required. ``bonus``: How many bonus points the team gets. Defaults to 0. ``submit``: Save. ``cancel``: Abort. """ challenge = QuerySelectField("Challenge", query_factory=get_all_challenges) team = QuerySelectField("Team", query_factory=get_all_teams) additional_pts = IntegerField("Additioanl Points", default=0) submit = SubmitField("Save") cancel = SubmitField("Cancel")
[docs]class MassMailForm(CSRFForm): """ A form to send a massmail to all users. Attrs: ``from_``: The sending address. Recommended to set it to ``settings["default_sender"]``, e.g.: .. code-block:: python if not form.from_.data: settings = self.request.registry.settings form.from_.data = settings["mail.default_sender"] ``subject``: A subject for the E-Mail. ``message``: A body for the E-Mail. ``submit``: Send the mail. ``cancel``: Don't send. """ from_ = EmailField("From") subject = TextField("Subject", validators=[required_validator]) message = TextAreaField("Message", validators=[required_validator]) submit = SubmitField("Send") cancel = SubmitField("Cancel")
[docs]class ButtonForm(CSRFForm): """ A form that gives a button and an ID. Useful for having a simple action that is identified in the forms ``action`` attribute. This provides CSRF support and the ability to POST submit commands such as edit or delete. """ button = SubmitField(widget=ButtonWidget()) id = HiddenField(validators=[required_validator]) def __init__(self, formdata=None, obj=None, prefix='', csrf_context=None, title=None, **kwargs): CSRFForm.__init__(self, formdata, obj, prefix, csrf_context, **kwargs) self.button.label.text = title
[docs]class SubmissionButtonForm(CSRFForm): """ Special variant of :class:`ButtonForm` that is tailored for the composite primary key table ``submission``. Instead of having one ``id`` field it has one field ``challenge_id`` identifying the challenge and a field ``team_id`` identifiying the team. """ button = SubmitField(widget=ButtonWidget()) challenge_id = HiddenField(validators=[required_validator]) team_id = HiddenField(validators=[required_validator]) def __init__(self, formdata=None, obj=None, prefix='', csrf_context=None, title=None, **kwargs): CSRFForm.__init__(self, formdata, obj, prefix, csrf_context, **kwargs) self.button.label.text = title
[docs]class TeamCleanupForm(CSRFForm): team_cleanup = SubmitField(widget=ButtonWidget()) def __init__(self, formdata=None, obj=None, prefix='', csrf_context=None, title=None, **kwargs): CSRFForm.__init__(self, formdata, obj, prefix, csrf_context, **kwargs) self.team_cleanup.label.text = title
[docs]class SettingsForm(CSRFForm): submission_disabled = BooleanField( "Submission disabled", description=("When submission is disabled, no more teams can submit " "solutions to challenges until this is re-enabled. " "Beyond that, the page stays alive.") ) ctf_start_date = TZDateTimeField( "CTF Start Date", description=("When the CTF should start, in format " "'%Y-%m-%d %H:%M:%S' and UTC timezone.") ) ctf_end_date = TZDateTimeField( "CTF End Date", description=("When the CTF should end, in format " "'%Y-%m-%d %H:%M:%S' and UTC timezone.") ) archive_mode = BooleanField( "Archive Mode", description=("Put the scoreboard in archive mode, protecting it from " "changes in the frontend and allowing public access. See " "documentation for details.") ) global_announcement = TextField( "Global Announcement", description=("Enter a message that should be displayed on all pages, " "e.g. an important notice. Leave empty to have no " "message.") ) submit = SubmitField("Send")