mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-17 22:14:25 +01:00
WIP: Add form globals (#1469)
* Work on #1467 * Creates the Form global which will contain all forms.
This commit is contained in:
@@ -163,17 +163,13 @@ def config():
|
||||
# Clear the config cache so that we don't get stale values
|
||||
clear_config()
|
||||
|
||||
database_tables = sorted(db.metadata.tables.keys())
|
||||
|
||||
configs = Configs.query.all()
|
||||
configs = dict([(c.key, get_config(c.key)) for c in configs])
|
||||
|
||||
themes = ctf_config.get_themes()
|
||||
themes.remove(get_config("ctf_theme"))
|
||||
|
||||
return render_template(
|
||||
"admin/config.html", database_tables=database_tables, themes=themes, **configs
|
||||
)
|
||||
return render_template("admin/config.html", themes=themes, **configs)
|
||||
|
||||
|
||||
@admin.route("/admin/reset", methods=["GET", "POST"])
|
||||
|
||||
18
CTFd/auth.py
18
CTFd/auth.py
@@ -17,7 +17,7 @@ from CTFd.utils.config.visibility import registration_visible
|
||||
from CTFd.utils.crypto import verify_password
|
||||
from CTFd.utils.decorators import ratelimit
|
||||
from CTFd.utils.decorators.visibility import check_registration_visibility
|
||||
from CTFd.utils.helpers import error_for, get_errors
|
||||
from CTFd.utils.helpers import error_for, get_errors, markup
|
||||
from CTFd.utils.logging import log
|
||||
from CTFd.utils.modes import TEAMS_MODE
|
||||
from CTFd.utils.security.auth import login_user, logout_user
|
||||
@@ -93,6 +93,16 @@ def confirm(data=None):
|
||||
@auth.route("/reset_password/<data>", methods=["POST", "GET"])
|
||||
@ratelimit(method="POST", limit=10, interval=60)
|
||||
def reset_password(data=None):
|
||||
if config.can_send_mail() is False:
|
||||
return render_template(
|
||||
"reset_password.html",
|
||||
errors=[
|
||||
markup(
|
||||
"This CTF is not configured to send email.<br> Please contact an organizer to have your password reset."
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
if data is not None:
|
||||
try:
|
||||
email_address = unserialize(data, max_age=1800)
|
||||
@@ -142,12 +152,6 @@ def reset_password(data=None):
|
||||
|
||||
get_errors()
|
||||
|
||||
if config.can_send_mail() is False:
|
||||
return render_template(
|
||||
"reset_password.html",
|
||||
errors=["Email could not be sent due to server misconfiguration"],
|
||||
)
|
||||
|
||||
if not user:
|
||||
return render_template(
|
||||
"reset_password.html",
|
||||
|
||||
@@ -6,6 +6,10 @@ class _ConfigsWrapper:
|
||||
def __getattr__(self, attr):
|
||||
return get_config(attr)
|
||||
|
||||
@property
|
||||
def ctf_name(self):
|
||||
return get_config("theme_header", default="CTFd")
|
||||
|
||||
@property
|
||||
def theme_header(self):
|
||||
return markup(get_config("theme_header", default=""))
|
||||
|
||||
49
CTFd/forms/__init__.py
Normal file
49
CTFd/forms/__init__.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from wtforms import Form
|
||||
from wtforms.csrf.core import CSRF
|
||||
|
||||
|
||||
class CTFdCSRF(CSRF):
|
||||
def generate_csrf_token(self, csrf_token_field):
|
||||
from flask import session
|
||||
|
||||
return session.get("nonce")
|
||||
|
||||
|
||||
class BaseForm(Form):
|
||||
class Meta:
|
||||
csrf = True
|
||||
csrf_class = CTFdCSRF
|
||||
csrf_field_name = "nonce"
|
||||
|
||||
|
||||
class _FormsWrapper:
|
||||
pass
|
||||
|
||||
|
||||
Forms = _FormsWrapper()
|
||||
|
||||
from CTFd.forms import auth # noqa: I001 isort:skip
|
||||
from CTFd.forms import self # noqa: I001 isort:skip
|
||||
from CTFd.forms import teams # noqa: I001 isort:skip
|
||||
from CTFd.forms import setup # noqa: I001 isort:skip
|
||||
from CTFd.forms import submissions # noqa: I001 isort:skip
|
||||
from CTFd.forms import users # noqa: I001 isort:skip
|
||||
from CTFd.forms import challenges # noqa: I001 isort:skip
|
||||
from CTFd.forms import notifications # noqa: I001 isort:skip
|
||||
from CTFd.forms import config # noqa: I001 isort:skip
|
||||
from CTFd.forms import pages # noqa: I001 isort:skip
|
||||
from CTFd.forms import awards # noqa: I001 isort:skip
|
||||
from CTFd.forms import email # noqa: I001 isort:skip
|
||||
|
||||
Forms.auth = auth
|
||||
Forms.self = self
|
||||
Forms.teams = teams
|
||||
Forms.setup = setup
|
||||
Forms.submissions = submissions
|
||||
Forms.users = users
|
||||
Forms.challenges = challenges
|
||||
Forms.notifications = notifications
|
||||
Forms.config = config
|
||||
Forms.pages = pages
|
||||
Forms.awards = awards
|
||||
Forms.email = email
|
||||
33
CTFd/forms/auth.py
Normal file
33
CTFd/forms/auth.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from wtforms import PasswordField, StringField
|
||||
from wtforms.fields.html5 import EmailField
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
|
||||
|
||||
class RegistrationForm(BaseForm):
|
||||
name = StringField("User Name", validators=[InputRequired()])
|
||||
email = EmailField("Email", validators=[InputRequired()])
|
||||
password = PasswordField("Password", validators=[InputRequired()])
|
||||
submit = SubmitField("Submit")
|
||||
|
||||
|
||||
class LoginForm(BaseForm):
|
||||
name = StringField("User Name or Email", validators=[InputRequired()])
|
||||
password = PasswordField("Password", validators=[InputRequired()])
|
||||
submit = SubmitField("Submit")
|
||||
|
||||
|
||||
class ConfirmForm(BaseForm):
|
||||
submit = SubmitField("Resend")
|
||||
|
||||
|
||||
class ResetPasswordRequestForm(BaseForm):
|
||||
email = EmailField("Email", validators=[InputRequired()])
|
||||
submit = SubmitField("Submit")
|
||||
|
||||
|
||||
class ResetPasswordForm(BaseForm):
|
||||
password = PasswordField("Password", validators=[InputRequired()])
|
||||
submit = SubmitField("Submit")
|
||||
30
CTFd/forms/awards.py
Normal file
30
CTFd/forms/awards.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from wtforms import RadioField, StringField, TextAreaField
|
||||
from wtforms.fields.html5 import IntegerField
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
|
||||
|
||||
class AwardCreationForm(BaseForm):
|
||||
name = StringField("Name")
|
||||
value = IntegerField("Value")
|
||||
category = StringField("Category")
|
||||
description = TextAreaField("Description")
|
||||
submit = SubmitField("Create")
|
||||
icon = RadioField(
|
||||
"Icon",
|
||||
choices=[
|
||||
("", "None"),
|
||||
("shield", "Shield"),
|
||||
("bug", "Bug"),
|
||||
("crown", "Crown"),
|
||||
("crosshairs", "Crosshairs"),
|
||||
("ban", "Ban"),
|
||||
("lightning", "Lightning"),
|
||||
("skull", "Skull"),
|
||||
("brain", "Brain"),
|
||||
("code", "Code"),
|
||||
("cowboy", "Cowboy"),
|
||||
("angry", "Angry"),
|
||||
],
|
||||
)
|
||||
30
CTFd/forms/challenges.py
Normal file
30
CTFd/forms/challenges.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from wtforms import MultipleFileField, SelectField, StringField
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
|
||||
|
||||
class ChallengeSearchForm(BaseForm):
|
||||
field = SelectField(
|
||||
"Search Field",
|
||||
choices=[
|
||||
("name", "Name"),
|
||||
("id", "ID"),
|
||||
("category", "Category"),
|
||||
("type", "Type"),
|
||||
],
|
||||
default="name",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
q = StringField("Parameter", validators=[InputRequired()])
|
||||
submit = SubmitField("Search")
|
||||
|
||||
|
||||
class ChallengeFilesUploadForm(BaseForm):
|
||||
file = MultipleFileField(
|
||||
"Upload Files",
|
||||
description="Attach multiple files using Control+Click or Cmd+Click.",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
submit = SubmitField("Upload")
|
||||
62
CTFd/forms/config.py
Normal file
62
CTFd/forms/config.py
Normal file
@@ -0,0 +1,62 @@
|
||||
from wtforms import BooleanField, SelectField, StringField
|
||||
from wtforms.fields.html5 import IntegerField
|
||||
from wtforms.widgets.html5 import NumberInput
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
from CTFd.models import db
|
||||
|
||||
|
||||
class ResetInstanceForm(BaseForm):
|
||||
accounts = BooleanField(
|
||||
"Accounts",
|
||||
description="Deletes all user and team accounts and their associated information",
|
||||
)
|
||||
submissions = BooleanField(
|
||||
"Submissions",
|
||||
description="Deletes all records that accounts gained points or took an action",
|
||||
)
|
||||
challenges = BooleanField(
|
||||
"Challenges", description="Deletes all challenges and associated data"
|
||||
)
|
||||
pages = BooleanField(
|
||||
"Pages", description="Deletes all pages and their associated files"
|
||||
)
|
||||
notifications = BooleanField(
|
||||
"Notifications", description="Deletes all notifications"
|
||||
)
|
||||
submit = SubmitField("Reset CTF")
|
||||
|
||||
|
||||
class AccountSettingsForm(BaseForm):
|
||||
domain_whitelist = StringField(
|
||||
"Account Email Whitelist",
|
||||
description="Comma-seperated email domains which users can register under (e.g. ctfd.io, gmail.com, yahoo.com)",
|
||||
)
|
||||
team_size = IntegerField(
|
||||
widget=NumberInput(min=0), description="Amount of users per team"
|
||||
)
|
||||
verify_emails = SelectField(
|
||||
"Verify Emails",
|
||||
description="Control whether users must confirm their email addresses before playing",
|
||||
choices=[("true", "Enabled"), ("false", "Disabled")],
|
||||
default="false",
|
||||
)
|
||||
name_changes = SelectField(
|
||||
"Name Changes",
|
||||
description="Control whether users can change their names",
|
||||
choices=[("true", "Enabled"), ("false", "Disabled")],
|
||||
default="true",
|
||||
)
|
||||
|
||||
submit = SubmitField("Update")
|
||||
|
||||
|
||||
class ExportCSVForm(BaseForm):
|
||||
table = SelectField(
|
||||
"Database Table",
|
||||
choices=list(
|
||||
zip(sorted(db.metadata.tables.keys()), sorted(db.metadata.tables.keys()))
|
||||
),
|
||||
)
|
||||
submit = SubmitField("Download CSV")
|
||||
10
CTFd/forms/email.py
Normal file
10
CTFd/forms/email.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from wtforms import TextAreaField
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
|
||||
|
||||
class SendEmailForm(BaseForm):
|
||||
message = TextAreaField("Message", validators=[InputRequired()])
|
||||
submit = SubmitField("Send")
|
||||
17
CTFd/forms/fields.py
Normal file
17
CTFd/forms/fields.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from wtforms import SubmitField as _SubmitField
|
||||
|
||||
|
||||
class SubmitField(_SubmitField):
|
||||
"""
|
||||
This custom SubmitField exists because wtforms is dumb.
|
||||
|
||||
See https://github.com/wtforms/wtforms/issues/205, https://github.com/wtforms/wtforms/issues/36
|
||||
The .submit() handler in JS will break if the form has an input with the name or id of "submit" so submit fields need to be changed.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
name = kwargs.pop("name", "_submit")
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.name == "submit" or name:
|
||||
self.id = name
|
||||
self.name = name
|
||||
26
CTFd/forms/notifications.py
Normal file
26
CTFd/forms/notifications.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from wtforms import BooleanField, RadioField, StringField, TextAreaField
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
|
||||
|
||||
class NotificationForm(BaseForm):
|
||||
title = StringField("Title", description="Notification title")
|
||||
content = TextAreaField(
|
||||
"Content",
|
||||
description="Notification contents. Can consist of HTML and/or Markdown.",
|
||||
)
|
||||
type = RadioField(
|
||||
"Notification Type",
|
||||
choices=[("toast", "Toast"), ("alert", "Alert"), ("background", "Background")],
|
||||
default="toast",
|
||||
description="What type of notification users receive",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
sound = BooleanField(
|
||||
"Play Sound",
|
||||
default=True,
|
||||
description="Play sound for users when they receive the notification",
|
||||
)
|
||||
submit = SubmitField("Submit")
|
||||
33
CTFd/forms/pages.py
Normal file
33
CTFd/forms/pages.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from wtforms import (
|
||||
BooleanField,
|
||||
HiddenField,
|
||||
MultipleFileField,
|
||||
StringField,
|
||||
TextAreaField,
|
||||
)
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
|
||||
|
||||
class PageEditForm(BaseForm):
|
||||
title = StringField(
|
||||
"Title", description="This is the title shown on the navigation bar"
|
||||
)
|
||||
route = StringField(
|
||||
"Route",
|
||||
description="This is the URL route that your page will be at (e.g. /page). You can also enter links to link to that page.",
|
||||
)
|
||||
draft = BooleanField("Draft")
|
||||
hidden = BooleanField("Hidden")
|
||||
auth_required = BooleanField("Authentication Required")
|
||||
content = TextAreaField("Content")
|
||||
|
||||
|
||||
class PageFilesUploadForm(BaseForm):
|
||||
file = MultipleFileField(
|
||||
"Upload Files",
|
||||
description="Attach multiple files using Control+Click or Cmd+Click.",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
type = HiddenField("Page Type", default="page", validators=[InputRequired()])
|
||||
22
CTFd/forms/self.py
Normal file
22
CTFd/forms/self.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from wtforms import PasswordField, SelectField, StringField
|
||||
from wtforms.fields.html5 import DateField, URLField
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
from CTFd.utils.countries import SELECT_COUNTRIES_LIST
|
||||
|
||||
|
||||
class SettingsForm(BaseForm):
|
||||
name = StringField("User Name")
|
||||
email = StringField("Email")
|
||||
password = PasswordField("Password")
|
||||
confirm = PasswordField("Current Password")
|
||||
affiliation = StringField("Affiliation")
|
||||
website = URLField("Website")
|
||||
country = SelectField("Country", choices=SELECT_COUNTRIES_LIST)
|
||||
submit = SubmitField("Submit")
|
||||
|
||||
|
||||
class TokensForm(BaseForm):
|
||||
expiration = DateField("Expiration")
|
||||
submit = SubmitField("Generate")
|
||||
66
CTFd/forms/setup.py
Normal file
66
CTFd/forms/setup.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from wtforms import (
|
||||
HiddenField,
|
||||
PasswordField,
|
||||
RadioField,
|
||||
SelectField,
|
||||
StringField,
|
||||
TextAreaField,
|
||||
)
|
||||
from wtforms.fields.html5 import EmailField
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
from CTFd.utils.config import get_themes
|
||||
|
||||
|
||||
class SetupForm(BaseForm):
|
||||
ctf_name = StringField(
|
||||
"Event Name", description="The name of your CTF event/workshop"
|
||||
)
|
||||
ctf_description = TextAreaField(
|
||||
"Event Description", description="Description for the CTF"
|
||||
)
|
||||
user_mode = RadioField(
|
||||
"User Mode",
|
||||
choices=[("teams", "Team Mode"), ("users", "User Mode")],
|
||||
default="teams",
|
||||
description="Controls whether users join together in teams to play (Team Mode) or play as themselves (User Mode)",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
|
||||
name = StringField(
|
||||
"Admin Username",
|
||||
description="Your username for the administration account",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
email = EmailField(
|
||||
"Admin Email",
|
||||
description="Your email address for the administration account",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
password = PasswordField(
|
||||
"Admin Password",
|
||||
description="Your password for the administration account",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
|
||||
ctf_theme = SelectField(
|
||||
"Theme",
|
||||
description="CTFd Theme to use",
|
||||
choices=list(zip(get_themes(), get_themes())),
|
||||
default="core",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
theme_color = HiddenField(
|
||||
"Theme Color",
|
||||
description="Color used by theme to control aesthetics. Requires theme support. Optional.",
|
||||
)
|
||||
|
||||
start = StringField(
|
||||
"Start Time", description="Time when your CTF is scheduled to start. Optional."
|
||||
)
|
||||
end = StringField(
|
||||
"End Time", description="Time when your CTF is scheduled to end. Optional."
|
||||
)
|
||||
submit = SubmitField("Finish")
|
||||
16
CTFd/forms/submissions.py
Normal file
16
CTFd/forms/submissions.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from wtforms import SelectField, StringField
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
|
||||
|
||||
class SubmissionSearchForm(BaseForm):
|
||||
field = SelectField(
|
||||
"Search Field",
|
||||
choices=[("provided", "Provided"), ("id", "ID")],
|
||||
default="provided",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
q = StringField("Parameter", validators=[InputRequired()])
|
||||
submit = SubmitField("Search")
|
||||
62
CTFd/forms/teams.py
Normal file
62
CTFd/forms/teams.py
Normal file
@@ -0,0 +1,62 @@
|
||||
from wtforms import BooleanField, PasswordField, SelectField, StringField
|
||||
from wtforms.fields.html5 import EmailField, URLField
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
from CTFd.utils.countries import SELECT_COUNTRIES_LIST
|
||||
|
||||
|
||||
class TeamJoinForm(BaseForm):
|
||||
name = StringField("Team Name", validators=[InputRequired()])
|
||||
password = PasswordField("Team Password", validators=[InputRequired()])
|
||||
submit = SubmitField("Join")
|
||||
|
||||
|
||||
class TeamRegisterForm(BaseForm):
|
||||
name = StringField("Team Name", validators=[InputRequired()])
|
||||
password = PasswordField("Team Password", validators=[InputRequired()])
|
||||
submit = SubmitField("Create")
|
||||
|
||||
|
||||
class TeamSettingsForm(BaseForm):
|
||||
name = StringField("Team Name")
|
||||
confirm = PasswordField("Current Password")
|
||||
password = PasswordField("Team Password")
|
||||
affiliation = StringField("Affiliation")
|
||||
website = URLField("Website")
|
||||
country = SelectField("Country", choices=SELECT_COUNTRIES_LIST)
|
||||
submit = SubmitField("Submit")
|
||||
|
||||
|
||||
class TeamCaptainForm(BaseForm):
|
||||
# Choices are populated dynamically at form creation time
|
||||
captain_id = SelectField("Team Captain", choices=[], validators=[InputRequired()])
|
||||
submit = SubmitField("Submit")
|
||||
|
||||
|
||||
class TeamSearchForm(BaseForm):
|
||||
field = SelectField(
|
||||
"Search Field",
|
||||
choices=[("name", "Name"), ("id", "ID"), ("affiliation", "Affiliation")],
|
||||
default="name",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
q = StringField("Parameter", validators=[InputRequired()])
|
||||
submit = SubmitField("Search")
|
||||
|
||||
|
||||
class TeamCreateForm(BaseForm):
|
||||
name = StringField("Team Name", validators=[InputRequired()])
|
||||
email = EmailField("Email")
|
||||
password = PasswordField("Password")
|
||||
website = URLField("Website")
|
||||
affiliation = StringField("Affiliation")
|
||||
country = SelectField("Country", choices=SELECT_COUNTRIES_LIST)
|
||||
hidden = BooleanField("Hidden")
|
||||
banned = BooleanField("Banned")
|
||||
submit = SubmitField("Submit")
|
||||
|
||||
|
||||
class TeamEditForm(TeamCreateForm):
|
||||
pass
|
||||
42
CTFd/forms/users.py
Normal file
42
CTFd/forms/users.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from wtforms import BooleanField, PasswordField, SelectField, StringField
|
||||
from wtforms.fields.html5 import EmailField
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
from CTFd.forms import BaseForm
|
||||
from CTFd.forms.fields import SubmitField
|
||||
from CTFd.utils.countries import SELECT_COUNTRIES_LIST
|
||||
|
||||
|
||||
class UserSearchForm(BaseForm):
|
||||
field = SelectField(
|
||||
"Search Field",
|
||||
choices=[
|
||||
("name", "Name"),
|
||||
("id", "ID"),
|
||||
("email", "Email"),
|
||||
("affiliation", "Affiliation"),
|
||||
("ip", "IP Address"),
|
||||
],
|
||||
default="name",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
q = StringField("Parameter", validators=[InputRequired()])
|
||||
submit = SubmitField("Search")
|
||||
|
||||
|
||||
class UserEditForm(BaseForm):
|
||||
name = StringField("User Name", validators=[InputRequired()])
|
||||
email = EmailField("Email", validators=[InputRequired()])
|
||||
password = PasswordField("Password")
|
||||
website = StringField("Website")
|
||||
affiliation = StringField("Affiliation")
|
||||
country = SelectField("Country", choices=SELECT_COUNTRIES_LIST)
|
||||
type = SelectField("Type", choices=[("user", "User"), ("admin", "Admin")])
|
||||
verified = BooleanField("Verified")
|
||||
hidden = BooleanField("Hidden")
|
||||
banned = BooleanField("Banned")
|
||||
submit = SubmitField("Submit")
|
||||
|
||||
|
||||
class UserCreateForm(UserEditForm):
|
||||
notify = BooleanField("Email account credentials to user", default=True)
|
||||
@@ -158,8 +158,9 @@ function submit_form() {
|
||||
var target = "/api/v1/pages";
|
||||
var method = "POST";
|
||||
|
||||
if (params.id) {
|
||||
target += "/" + params.id;
|
||||
let part = window.location.pathname.split("/").pop();
|
||||
if (part !== "new") {
|
||||
target += "/" + part;
|
||||
method = "PATCH";
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -21,29 +21,29 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% if q and field %}
|
||||
<h5 class="text-muted text-center">Searching for challenges with <strong>{{ field }}</strong> matching <strong>{{ q }}</strong></h5>
|
||||
<h6 class="text-muted text-center pb-3">{{ total }} results</h6>
|
||||
<h5 class="text-muted text-center">
|
||||
Searching for challenges with <strong>{{ field }}</strong> matching <strong>{{ q }}</strong>
|
||||
</h5>
|
||||
<h6 class="text-muted text-center pb-3">
|
||||
{{ total }} results
|
||||
</h6>
|
||||
{% endif %}
|
||||
|
||||
{% with form = Forms.challenges.ChallengeSearchForm(field=field, q=q) %}
|
||||
<form method="GET" class="form-inline">
|
||||
<div class="form-group col-md-2">
|
||||
<label for="sel1" class="sr-only" >Search Field</label>
|
||||
<select class="form-control custom-select w-100" id="sel1" name="field">
|
||||
<option value="name" {% if field == 'name' %}selected{% endif %}>Name</option>
|
||||
<option value="id" {% if field == 'id' %}selected{% endif %}>ID</option>
|
||||
<option value="category" {% if field == 'category' %}selected{% endif %}>Category</option>
|
||||
<option value="type" {% if field == 'type' %}selected{% endif %}>Type</option>
|
||||
</select>
|
||||
{{ form.field(class="form-control custom-select w-100") }}
|
||||
</div>
|
||||
<div class="form-group col-md-8">
|
||||
<label for="challenges-search" class="sr-only">Parameter</label>
|
||||
<input type="text" class="form-control w-100" id="challenges-search" name="q" placeholder="Search for matching challenge" {% if q %}value="{{q}}"{% endif %}>
|
||||
{{ form.q(class="form-control w-100", placeholder="Search for matching challenge") }}
|
||||
</div>
|
||||
<div class="form-group col-md-2">
|
||||
<label class="sr-only">Search</label>
|
||||
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-search" aria-hidden="true"></i></button>
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="fas fa-search" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,57 +1,43 @@
|
||||
<div role="tabpanel" class="tab-pane config-section" id="accounts">
|
||||
|
||||
{% set verify_emails = "true" if verify_emails == True else "false" %}
|
||||
{% set name_changes = "true" if name_changes == True else "false" %}
|
||||
{% with form = Forms.config.AccountSettingsForm(verify_emails=verify_emails, name_changes=name_changes) %}
|
||||
<form method="POST" autocomplete="off" class="w-100">
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Account Email Whitelist
|
||||
<small class="form-text text-muted">
|
||||
Comma-seperated email domains which users can register under.
|
||||
</small>
|
||||
</label>
|
||||
<input class="form-control" id='domain-whitelist' name='domain_whitelist' type='text'
|
||||
placeholder=""
|
||||
{% if domain_whitelist is defined and domain_whitelist != None %}value="{{ domain_whitelist }}"{% endif %}>
|
||||
{{ form.domain_whitelist.label }}
|
||||
{{ form.domain_whitelist(class="form-control", value=domain_whitelist or "") }}
|
||||
<small class="form-text text-muted">
|
||||
e.g. ctfd.io,gmail.com,yahoo.com
|
||||
{{ form.domain_whitelist.description }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Verify Emails<br>
|
||||
<small class="form-text text-muted">
|
||||
Control whether users must confirm their email addresses before playing
|
||||
</small>
|
||||
</label>
|
||||
<select class="form-control custom-select" name="verify_emails">
|
||||
<option value="true" {% if verify_emails == True %}selected{% endif %}>Enabled</option>
|
||||
<option value="false" {% if verify_emails == False or verify_emails == None %}selected{% endif %}>Disabled</option>
|
||||
</select>
|
||||
{{ form.verify_emails.label }}
|
||||
{{ form.verify_emails(class="form-control custom-select") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.verify_emails.description }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Team Size<br>
|
||||
<small class="form-text text-muted">
|
||||
Amount of users per team
|
||||
</small>
|
||||
</label>
|
||||
<input class="form-control" id="team_size" name="team_size" type="number" min="0" {% if team_size %}value="{{ team_size }}"{% endif %}>
|
||||
{{ form.team_size.label }}
|
||||
{{ form.team_size(class="form-control", value=team_size) }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.team_size.description }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Name Changes<br>
|
||||
<small class="form-text text-muted">
|
||||
Control whether users can change their names
|
||||
</small>
|
||||
</label>
|
||||
<select class="form-control custom-select" name="name_changes">
|
||||
<option value="true" {% if name_changes == True or name_changes == None %}selected{% endif %}>Enabled</option>
|
||||
<option value="false" {% if name_changes == False %}selected{% endif %}>Disabled</option>
|
||||
</select>
|
||||
{{ form.name_changes.label }}
|
||||
{{ form.name_changes(class="form-control custom-select") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.name_changes.description }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-md btn-primary float-right">Update</button>
|
||||
{{ form.submit(class="btn btn-md btn-primary float-right") }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
@@ -39,17 +39,15 @@
|
||||
<input id="import-button" type="submit" class="btn btn-warning" value="Import">
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="save-csv">
|
||||
{% with form = Forms.config.ExportCSVForm() %}
|
||||
<form method="GET" action="{{ url_for('admin.export_csv') }}">
|
||||
<div class="form-group">
|
||||
<label for="container-files">Database Table</label>
|
||||
<select class="form-control custom-select" name="table">
|
||||
{% for table in database_tables %}
|
||||
<option value="{{ table }}">{{ table }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{{ form.table.label }}
|
||||
{{ form.table(class="form-control custom-select") }}
|
||||
</div>
|
||||
<input type="submit" class="btn btn-warning" value="Download CSV">
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,18 +69,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% with form = Forms.pages.PageFilesUploadForm() %}
|
||||
<form id="media-library-upload" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="media-files">
|
||||
Upload Files
|
||||
</label>
|
||||
<input type="file" name="file" id="media-files" class="form-control-file" multiple>
|
||||
<b>{{ form.file.label }}</b>
|
||||
{{ form.file(id="media-files", class="form-control-file") }}
|
||||
<sub class="help-block">
|
||||
Attach multiple files using Control+Click or Cmd+Click.
|
||||
{{ form.file.description }}
|
||||
</sub>
|
||||
</div>
|
||||
<input type="hidden" value="page" name="type">
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="float-right">
|
||||
@@ -106,29 +107,28 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% set content = page.content if page is defined else "" %}
|
||||
{% with form = Forms.pages.PageEditForm(content=content) %}
|
||||
<form id="page-edit" method="POST">
|
||||
<div class="form-group">
|
||||
<div class="col-md-12">
|
||||
<label>
|
||||
Title<br>
|
||||
<small class="text-muted">This is the title shown on the navigation bar</small>
|
||||
</label>
|
||||
<input class="form-control radius" id="route" type="text" name="title"
|
||||
value="{% if page is defined %}{{ page.title }}{% endif %}" placeholder="Title">
|
||||
{% set title = page.title if page is defined %}
|
||||
<b>{{ form.title.label }}</b>
|
||||
{{ form.title(class="form-control radius", id="route", placeholder="Title", value=title) }}
|
||||
<small class="text-muted">
|
||||
{{ form.title.description }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-12">
|
||||
<label>
|
||||
Route<br>
|
||||
<small class="text-muted">
|
||||
This is the URL route that your page will be at (e.g. /page). You can
|
||||
also enter links to link to that page.
|
||||
</small>
|
||||
</label>
|
||||
<input class="form-control radius" id="route" type="text" name="route"
|
||||
value="{% if page is defined %}{{ page.route }}{% endif %}" placeholder="Route">
|
||||
{% set route = page.route if page is defined %}
|
||||
<b>{{ form.route.label }}</b>
|
||||
{{ form.route(class="form-control radius", placeholder="Route", value=route) }}
|
||||
<small class="text-muted">
|
||||
{{ form.route.description }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -165,50 +165,28 @@
|
||||
<span class="nav-link d-none d-md-block d-lg-block">|</span>
|
||||
|
||||
<div class="form-group pr-3">
|
||||
Draft:
|
||||
{% set draft = page is defined and page.draft == True %}
|
||||
<select class="form-control custom-select" name="draft">
|
||||
<option value="true" {% if draft == True %}selected{% endif %}>
|
||||
True
|
||||
</option>
|
||||
<option value="false" {% if draft == False %}selected{% endif %}>
|
||||
False
|
||||
</option>
|
||||
</select>
|
||||
{{ form.draft(class="form-check-input", checked=draft) }}
|
||||
{{ form.draft.label(class="form-check-label") }}
|
||||
</div>
|
||||
|
||||
<div class="form-group pr-3">
|
||||
Hidden:
|
||||
{% set hidden = page is defined and page.hidden == True %}
|
||||
<select class="form-control custom-select" name="hidden">
|
||||
<option value="true" {% if hidden == True %}selected{% endif %}>True
|
||||
</option>
|
||||
<option value="false" {% if hidden == False %}selected{% endif %}>False
|
||||
</option>
|
||||
</select>
|
||||
{{ form.hidden(class="form-check-input", checked=hidden) }}
|
||||
{{ form.hidden.label(class="form-check-label") }}
|
||||
</div>
|
||||
|
||||
<div class="form-group pr-3">
|
||||
Authentication Required:
|
||||
{% set auth_required = page is defined and page.auth_required == True %}
|
||||
<select class="form-control custom-select" name="auth_required">
|
||||
<option value="true" {% if auth_required == True %}selected{% endif %}>
|
||||
True
|
||||
</option>
|
||||
<option value="false" {% if auth_required == False %}selected{% endif %}>
|
||||
False
|
||||
</option>
|
||||
</select>
|
||||
{{ form.auth_required(class="form-check-input", checked=auth_required) }}
|
||||
{{ form.auth_required.label(class="form-check-label") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea id="admin-pages-editor" class="d-none"
|
||||
name="content">{% if page is defined %}{{ page.content }}{% endif %}</textarea>
|
||||
<input name='id' type='hidden'
|
||||
{% if page is defined %}value="{{ page.id }}"{% endif %}>
|
||||
{{ form.content(id="admin-pages-editor", class="d-none") }}
|
||||
</div>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane content" id="content-preview" style="height:400px">
|
||||
@@ -218,17 +196,14 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group float-right">
|
||||
<input name='nonce' type='hidden' value="{{ nonce }}">
|
||||
{{ form.nonce() }}
|
||||
<button class="btn btn-primary" id="save-page">
|
||||
{% if page is defined %}
|
||||
Update
|
||||
{% else %}
|
||||
Save
|
||||
{% endif %}
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
{% with form = Forms.awards.AwardCreationForm() %}
|
||||
<form id="user-award-form" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="award-name-input">Name</label>
|
||||
<input type="text" class="form-control" id="award-name-input" name="name">
|
||||
<b>{{ form.name.label }}</b>
|
||||
{{ form.name(class="form-control", id="award-name-input") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="award-value-input">Value</label>
|
||||
<input type="number" class="form-control" id="award-value-input" name="value">
|
||||
<b>{{ form.value.label }}</b>
|
||||
{{ form.value(class="form-control", id="award-value-input") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="award-category-input">Category</label>
|
||||
<input type="text" class="form-control" id="award-category-input" name="category">
|
||||
<b>{{ form.category.label }}</b>
|
||||
{{ form.category(class="form-control", id="award-category-input") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="award-description-input" class="control-label">Description</label>
|
||||
<textarea id="award-description-input" class="form-control" name="description" rows="10"></textarea>
|
||||
<b>{{ form.description.label }}</b>
|
||||
{{ form.description(id="award-description-input", class="form-control", rows="5") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="award-icon-input">Icon</label>
|
||||
<b>{{ form.icon.label }}</b>
|
||||
<div class="pt-1">
|
||||
<table class="table table-sm w-100">
|
||||
<tr>
|
||||
@@ -129,6 +130,7 @@
|
||||
<div id="results">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" id="award-create-button" class="btn btn-primary">Create</button>
|
||||
{{ form.submit(id="award-create-button", class="btn btn-primary") }}
|
||||
</div>
|
||||
</form>
|
||||
{% endwith %}
|
||||
@@ -21,13 +21,17 @@
|
||||
</table>
|
||||
|
||||
<div class="col-md-12 mt-3">
|
||||
{% with form = Forms.challenges.ChallengeFilesUploadForm() %}
|
||||
<form id="file-add-form" method="POST">
|
||||
<div class="form-group">
|
||||
<input class="form-control-file" type="file" name="file" multiple="multiple">
|
||||
<sub class="text-muted">Attach multiple files using Control+Click or Cmd+Click</sub>
|
||||
{{ form.file(class="form-control-file") }}
|
||||
<sub class="text-muted">
|
||||
{{ form.file.description }}
|
||||
</sub>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-success float-right" id="submit-files">Upload</button>
|
||||
{{ form.submit(class="btn btn-success float-right") }}
|
||||
</div>
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
@@ -1,16 +1,12 @@
|
||||
{% with form = Forms.email.SendEmailForm() %}
|
||||
<form id="user-mail-form" method="POST">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Message
|
||||
<br>
|
||||
<small></small>
|
||||
</label>
|
||||
<textarea class="form-control" name="text" placeholder="" rows="15"></textarea>
|
||||
{{ form.message.label }}
|
||||
{{ form.message(class="form-control", rows="15") }}
|
||||
</div>
|
||||
<div id="results">
|
||||
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary float-right">
|
||||
Send
|
||||
</button>
|
||||
</form>
|
||||
{{ form.submit(class="btn btn-primary float-right") }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
@@ -1,57 +1,42 @@
|
||||
{% with form = Forms.teams.TeamEditForm() %}
|
||||
<form id="team-info-create-form" method="POST">
|
||||
<input type="hidden" name="id">
|
||||
<div class="form-group">
|
||||
<label for="name">Team Name</label>
|
||||
<input type="text" class="form-control" name="name" id="name"
|
||||
{% if team is defined and team.name %}value="{{ team.name }}"{% endif %} required>
|
||||
<b>{{ form.name.label }}</b>
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" class="form-control" name="email" id="email"
|
||||
{% if team is defined and team.email %}value="{{ team.email }}"{% endif %}>
|
||||
<b>{{ form.email.label }}</b>
|
||||
{{ form.email(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" class="form-control" name="password" id="password"/>
|
||||
<b>{{ form.password.label }}</b>
|
||||
{{ form.password(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="website">Website</label>
|
||||
<input type="url" class="form-control" name="website" id="website"
|
||||
{% if team is defined and team.website %}value="{{ team.website }}"{% endif %}>
|
||||
<b>{{ form.website.label }}</b>
|
||||
{{ form.website(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">Affiliation</label>
|
||||
<input type="text" class="form-control" name="affiliation" id="affiliation"
|
||||
{% if team is defined and team.affiliation %}value="{{ team.affiliation }}"{% endif %}>
|
||||
<b>{{ form.affiliation.label }}</b>
|
||||
{{ form.affiliation(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">Country</label>
|
||||
<select class="form-control custom-select" id="country-input" name="country">
|
||||
<option></option>
|
||||
{% set countries = get_countries() %}
|
||||
{% for country_code in countries.keys() %}
|
||||
<option value="{{ country_code }}" {% if team is defined and team.country == country_code %}selected{% endif %}>
|
||||
{{ countries[country_code] }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<b>{{ form.country.label }}</b>
|
||||
{{ form.country(class="form-control custom-select") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="hidden" id="hidden-checkbox"
|
||||
{% if team is defined and team.hidden %}checked{% endif %}>
|
||||
<label class="form-check-label" for="hidden-checkbox">Hidden</label>
|
||||
{{ form.hidden(class="form-check-input") }}
|
||||
{{ form.hidden.label(class="form-check-label") }}
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="banned" id="banned-checkbox"
|
||||
{% if team is defined and team.banned %}checked{% endif %}>
|
||||
<label class="form-check-label" for="banned-checkbox">Banned</label>
|
||||
{{ form.banned(class="form-check-input") }}
|
||||
{{ form.banned.label(class="form-check-label") }}
|
||||
</div>
|
||||
</div>
|
||||
<div id="results">
|
||||
|
||||
</div>
|
||||
<button id="update-team" type="submit" class="btn btn-primary btn-outlined float-right modal-action">
|
||||
Submit
|
||||
</button>
|
||||
{{ form.submit(id="update-team", class="btn btn-primary btn-outlined float-right modal-action") }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
@@ -1,57 +1,42 @@
|
||||
{% with form = Forms.teams.TeamCreateForm(obj=team) %}
|
||||
<form id="team-info-edit-form" method="POST">
|
||||
<input type="hidden" name="id">
|
||||
<div class="form-group">
|
||||
<label for="name">Team Name</label>
|
||||
<input type="text" class="form-control" name="name" id="name"
|
||||
{% if team is defined and team.name %}value="{{ team.name }}"{% endif %} required>
|
||||
{{ form.name.label }}
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" class="form-control" name="email" id="email"
|
||||
{% if team is defined and team.email %}value="{{ team.email }}"{% endif %}>
|
||||
{{ form.email.label }}
|
||||
{{ form.email(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" class="form-control" name="password" id="password"/>
|
||||
{{ form.password.label }}
|
||||
{{ form.password(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="website">Website</label>
|
||||
<input type="url" class="form-control" name="website" id="website"
|
||||
{% if team is defined and team.website %}value="{{ team.website }}"{% endif %}>
|
||||
{{ form.website.label }}
|
||||
{{ form.website(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">Affiliation</label>
|
||||
<input type="text" class="form-control" name="affiliation" id="affiliation"
|
||||
{% if team is defined and team.affiliation %}value="{{ team.affiliation }}"{% endif %}>
|
||||
{{ form.affiliation.label }}
|
||||
{{ form.affiliation(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">Country</label>
|
||||
<select class="form-control custom-select" id="country-input" name="country">
|
||||
<option></option>
|
||||
{% set countries = get_countries() %}
|
||||
{% for country_code in countries.keys() %}
|
||||
<option value="{{ country_code }}" {% if team is defined and team.country == country_code %}selected{% endif %}>
|
||||
{{ countries[country_code] }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{{ form.country.label }}
|
||||
{{ form.country(class="form-control custom-select") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="hidden" id="hidden-checkbox"
|
||||
{% if team is defined and team.hidden %}checked{% endif %}>
|
||||
<label class="form-check-label" for="hidden-checkbox">Hidden</label>
|
||||
{{ form.hidden(class="form-check-input") }}
|
||||
{{ form.hidden.label(class="form-check-label") }}
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="banned" id="banned-checkbox"
|
||||
{% if team is defined and team.banned %}checked{% endif %}>
|
||||
<label class="form-check-label" for="banned-checkbox">Banned</label>
|
||||
{{ form.banned(class="form-check-input") }}
|
||||
{{ form.banned.label(class="form-check-label") }}
|
||||
</div>
|
||||
</div>
|
||||
<div id="results">
|
||||
|
||||
</div>
|
||||
<button id="update-team" type="submit" class="btn btn-primary btn-outlined float-right modal-action">
|
||||
Submit
|
||||
</button>
|
||||
{{ form.submit(id="update-team", class="btn btn-primary btn-outlined float-right modal-action") }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
@@ -1,78 +1,59 @@
|
||||
{% with form = Forms.users.UserCreateForm() %}
|
||||
<form id="user-info-create-form" method="POST">
|
||||
<input type="hidden" name="id">
|
||||
<div class="form-group">
|
||||
<label for="name">User Name</label>
|
||||
<input type="text" class="form-control" name="name" id="name"
|
||||
{% if user is defined and user.name %}value="{{ user.name }}"{% endif %} required>
|
||||
{{ form.name.label }}
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" class="form-control" name="email" id="email"
|
||||
{% if user is defined and user.email %}value="{{ user.email }}"{% endif %} required>
|
||||
{{ form.email.label }}
|
||||
{{ form.email(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" class="form-control" name="password" id="password"/>
|
||||
{{ form.password.label }}
|
||||
{{ form.password(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="website">Website</label>
|
||||
<input type="text" class="form-control" name="website" id="website"
|
||||
{% if user is defined and user.website %}value="{{ user.website }}"{% endif %}>
|
||||
{{ form.website.label }}
|
||||
{{ form.website(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">Affiliation</label>
|
||||
<input type="text" class="form-control" name="affiliation" id="affiliation"
|
||||
{% if user is defined and user.affiliation %}value="{{ user.affiliation }}"{% endif %}>
|
||||
{{ form.affiliation.label }}
|
||||
{{ form.affiliation(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">Country</label>
|
||||
<select class="form-control custom-select" id="country-input" name="country">
|
||||
<option></option>
|
||||
{% set countries = get_countries() %}
|
||||
{% for country_code in countries.keys() %}
|
||||
<option value="{{ country_code }}" {% if user is defined and user.country == country_code %}selected{% endif %}>
|
||||
{{ countries[country_code] }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{{ form.country.label }}
|
||||
{{ form.country(class="form-control custom-select") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check form-check-inline">
|
||||
<select class="form-control form-inline custom-select" id="type-select" name="type">
|
||||
<option value="user"{% if user is defined and user.type == 'user' %} selected{% endif %}>
|
||||
User
|
||||
</option>
|
||||
<option value="admin"{% if user is defined and user.type == 'admin' %} selected{% endif %}>
|
||||
Admin
|
||||
</option>
|
||||
</select>
|
||||
{{ form.type(class="form-control form-inline custom-select", id="type-select") }}
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="verified" id="verified-checkbox">
|
||||
<label class="form-check-label" for="verified-checkbox">Verified</label>
|
||||
{{ form.verified(class="form-check-input") }}
|
||||
{{ form.verified.label(class="form-check-label") }}
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="hidden" id="hidden-checkbox">
|
||||
<label class="form-check-label" for="hidden-checkbox">Hidden</label>
|
||||
{{ form.hidden(class="form-check-input") }}
|
||||
{{ form.hidden.label(class="form-check-label") }}
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="banned" id="banned-checkbox">
|
||||
<label class="form-check-label" for="banned-checkbox">Banned</label>
|
||||
{{ form.banned(class="form-check-input") }}
|
||||
{{ form.banned.label(class="form-check-label") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if can_send_mail() %}
|
||||
<div class="form-group">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="notify" id="notify-checkbox" checked>
|
||||
<label class="form-check-label" for="notify-checkbox">Email account credentials to user</label>
|
||||
{{ form.notify(class="form-check-input", id="notify-checkbox") }}
|
||||
{{ form.notify.label(class="form-check-label") }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="results">
|
||||
</div>
|
||||
<button id="update-user" type="submit" class="btn btn-primary btn-outlined float-right modal-action">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{{ form.submit(id="update-user", class="btn btn-primary btn-outlined float-right modal-action") }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
@@ -1,68 +1,48 @@
|
||||
{% with form = Forms.users.UserEditForm(obj=user) %}
|
||||
<form id="user-info-edit-form">
|
||||
<input type="hidden" name="id">
|
||||
<div class="form-group">
|
||||
<label for="name">User Name</label>
|
||||
<input type="text" class="form-control" name="name" id="name"
|
||||
{% if user is defined and user.name %}value="{{ user.name }}"{% endif %} required>
|
||||
{{ form.name.label }}
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" class="form-control" name="email" id="email"
|
||||
{% if user is defined and user.email %}value="{{ user.email }}"{% endif %} required>
|
||||
{{ form.email.label }}
|
||||
{{ form.email(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" class="form-control" name="password" id="password"/>
|
||||
{{ form.password.label }}
|
||||
{{ form.password(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="website">Website</label>
|
||||
<input type="text" class="form-control" name="website" id="website"
|
||||
{% if user is defined and user.website %}value="{{ user.website }}"{% endif %}>
|
||||
{{ form.website.label }}
|
||||
{{ form.website(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">Affiliation</label>
|
||||
<input type="text" class="form-control" name="affiliation" id="affiliation"
|
||||
{% if user is defined and user.affiliation %}value="{{ user.affiliation }}"{% endif %}>
|
||||
{{ form.affiliation.label }}
|
||||
{{ form.affiliation(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">Country</label>
|
||||
<select class="form-control custom-select" id="country-input" name="country">
|
||||
<option></option>
|
||||
{% set countries = get_countries() %}
|
||||
{% for country_code in countries.keys() %}
|
||||
<option value="{{ country_code }}" {% if user is defined and user.country == country_code %}selected{% endif %}>
|
||||
{{ countries[country_code] }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{{ form.country.label }}
|
||||
{{ form.country(class="form-control custom-select") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check form-check-inline">
|
||||
<select class="form-control form-inline custom-select" id="type-select" name="type">
|
||||
<option value="user"{% if user is defined and user.type == 'user' %} selected{% endif %}>
|
||||
User
|
||||
</option>
|
||||
<option value="admin"{% if user is defined and user.type == 'admin' %} selected{% endif %}>
|
||||
Admin
|
||||
</option>
|
||||
</select>
|
||||
{{ form.type(class="form-control form-inline custom-select", id="type-select") }}
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="verified" id="verified-checkbox" {% if user is defined and user.verified %}checked{% endif %}>
|
||||
<label class="form-check-label" for="verified-checkbox">Verified</label>
|
||||
{{ form.verified(class="form-check-input") }}
|
||||
{{ form.verified.label(class="form-check-label") }}
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="hidden" id="hidden-checkbox" {% if user is defined and user.hidden %}checked{% endif %}>
|
||||
<label class="form-check-label" for="hidden-checkbox">Hidden</label>
|
||||
{{ form.hidden(class="form-check-input") }}
|
||||
{{ form.hidden.label(class="form-check-label") }}
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="banned" id="banned-checkbox" {% if user is defined and user.banned %}checked{% endif %}>
|
||||
<label class="form-check-label" for="banned-checkbox">Banned</label>
|
||||
{{ form.banned(class="form-check-input") }}
|
||||
{{ form.banned.label(class="form-check-label") }}
|
||||
</div>
|
||||
</div>
|
||||
<div id="results">
|
||||
</div>
|
||||
<button id="update-user" type="submit" class="btn btn-primary btn-outlined float-right modal-action">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
{{ form.submit(class="btn btn-primary btn-outlined float-right modal-action") }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
@@ -9,67 +9,55 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% with form = Forms.notifications.NotificationForm() %}
|
||||
<form method="POST" id="notifications_form">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Title
|
||||
<small class="form-text text-muted">
|
||||
Notification title
|
||||
</small>
|
||||
</label>
|
||||
<input class="form-control" type="text" name="title">
|
||||
<b>{{ form.title.label }}</b>
|
||||
{{ form.title(class="form-control") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.title.description }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Content
|
||||
<small class="form-text text-muted">
|
||||
Notification contents can be made up of HTML
|
||||
</small>
|
||||
</label>
|
||||
<textarea class="form-control" type="text" name="content" rows="3"></textarea>
|
||||
<b>{{ form.content.label }}</b>
|
||||
{{ form.content(class="form-control", rows="3") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.content.description }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Type
|
||||
<small class="form-text text-muted">
|
||||
What type of notification users receive
|
||||
</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline pr-1">
|
||||
<input class="form-check-input" type="radio" name="type" value="toast" id="type-radio-toast" checked>
|
||||
<label class="form-check-label" for="type-radio-toast">Toast</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline pr-1">
|
||||
<input class="form-check-input" type="radio" name="type" value="alert" id="type-radio-alert">
|
||||
<label class="form-check-label" for="type-radio-alert">Alert</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="type" value="background" id="type-radio-bg">
|
||||
<label class="form-check-label" for="type-radio-bg">Background</label>
|
||||
<b>{{ form.type.label }}</b>
|
||||
</div>
|
||||
{% for radio in form.type %}
|
||||
<div class="form-check form-check-inline pr-1">
|
||||
{{ radio(class="form-check-input") }}
|
||||
{{ radio.label(class="form-check-label") }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.type.description }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Sound
|
||||
<small class="form-text text-muted">
|
||||
Play sound for users
|
||||
</small>
|
||||
</label>
|
||||
<b>{{ form.sound.label }}</b>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="sound" value="true" id="sound-enable" checked>
|
||||
<label class="form-check-label" for="sound-enable">Play Sound</label>
|
||||
{{ form.sound(class="form-check-input") }}
|
||||
{{ form.sound.label(class="form-check-label") }}
|
||||
</div>
|
||||
<small class="form-text text-muted">
|
||||
{{ form.sound.description }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<button type="submit" class="btn btn-success text-center">Submit</button>
|
||||
{{ form.submit(class="btn btn-success text-center") }}
|
||||
</div>
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-md-3">
|
||||
{% with form = Forms.config.ResetInstanceForm() %}
|
||||
<form method="POST" id="reset-ctf-form">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<p>
|
||||
@@ -38,9 +39,8 @@
|
||||
|
||||
<div class="form-group pb-2">
|
||||
<div class="form-check">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" name="accounts"> Accounts
|
||||
</label>
|
||||
{{ form.accounts(class="form-check-input") }}
|
||||
{{ form.accounts.label(class="form-check-label") }}
|
||||
</div>
|
||||
<span class="text-muted">
|
||||
Deletes all user and team accounts and their associated information<br>
|
||||
@@ -50,9 +50,8 @@
|
||||
|
||||
<div class="form-group pb-2">
|
||||
<div class="form-check">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" name="submissions"> Submissions
|
||||
</label>
|
||||
{{ form.submissions(class="form-check-input") }}
|
||||
{{ form.submissions.label(class="form-check-label") }}
|
||||
</div>
|
||||
<span class="text-muted">
|
||||
Deletes all records that accounts gained points or took an action<br>
|
||||
@@ -62,9 +61,8 @@
|
||||
|
||||
<div class="form-group pb-2">
|
||||
<div class="form-check">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" name="challenges"> Challenges
|
||||
</label>
|
||||
{{ form.challenges(class="form-check-input") }}
|
||||
{{ form.challenges.label(class="form-check-label") }}
|
||||
</div>
|
||||
<span class="text-muted">
|
||||
Deletes all challenges and associated data<br>
|
||||
@@ -74,9 +72,8 @@
|
||||
|
||||
<div class="form-group pb-2">
|
||||
<div class="form-check">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" name="pages"> Pages
|
||||
</label>
|
||||
{{ form.pages(class="form-check-input") }}
|
||||
{{ form.pages.label(class="form-check-label") }}
|
||||
</div>
|
||||
<span class="text-muted">
|
||||
Deletes all pages and their associated files<br>
|
||||
@@ -86,9 +83,8 @@
|
||||
|
||||
<div class="form-group pb-2">
|
||||
<div class="form-check">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" name="notifications"> Notifications
|
||||
</label>
|
||||
{{ form.notifications(class="form-check-input") }}
|
||||
{{ form.notifications.label(class="form-check-label") }}
|
||||
</div>
|
||||
<span class="text-muted">
|
||||
Deletes all notifications<br>
|
||||
@@ -98,12 +94,11 @@
|
||||
|
||||
<br>
|
||||
|
||||
<input id="nonce" type="hidden" name="nonce" value="{{ nonce }}">
|
||||
{{ form.nonce() }}
|
||||
|
||||
<button class="btn btn-warning btn-lg btn-block">
|
||||
Reset CTF
|
||||
</button>
|
||||
{{ form.submit(class="btn btn-warning btn-lg btn-block") }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,27 +16,29 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% if q and field %}
|
||||
<h5 class="text-muted text-center">Searching for submissions with <strong>{{ field }}</strong> matching <strong>{{ q }}</strong></h5>
|
||||
<h6 class="text-muted text-center pb-3">Page {{ submissions.page }} of {{ submissions.total }} results</h6>
|
||||
<h5 class="text-muted text-center">
|
||||
Searching for submissions with <strong>{{ field }}</strong> matching <strong>{{ q }}</strong>
|
||||
</h5>
|
||||
<h6 class="text-muted text-center pb-3">
|
||||
Page {{ submissions.page }} of {{ submissions.total }} results
|
||||
</h6>
|
||||
{% endif %}
|
||||
|
||||
{% with form = Forms.submissions.SubmissionSearchForm(field=field, q=q) %}
|
||||
<form method="GET" class="form-inline">
|
||||
<div class="form-group col-md-2">
|
||||
<label for="sel1" class="sr-only" >Search Field</label>
|
||||
<select class="form-control custom-select w-100" id="sel1" name="field">
|
||||
<option value="provided" {% if field == 'provided' %}selected{% endif %}>Provided</option>
|
||||
<option value="id" {% if field == 'id' %}selected{% endif %}>ID</option>
|
||||
</select>
|
||||
{{ form.field(class="form-control custom-select w-100") }}
|
||||
</div>
|
||||
<div class="form-group col-md-8">
|
||||
<label for="team-name-search" class="sr-only">Parameter</label>
|
||||
<input type="text" class="form-control w-100" id="team-name-search" name="q" placeholder="Search for matching submission" {% if q %}value="{{q}}"{% endif %}>
|
||||
{{ form.q(class="form-control w-100", placeholder="Search for matching submission") }}
|
||||
</div>
|
||||
<div class="form-group col-md-2">
|
||||
<label for="team-name-search" class="sr-only">Search</label>
|
||||
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-search" aria-hidden="true"></i></button>
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="fas fa-search" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -20,28 +20,29 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% if q and field %}
|
||||
<h5 class="text-muted text-center">Searching for teams with <strong>{{ field }}</strong> matching <strong>{{ q }}</strong></h5>
|
||||
<h6 class="text-muted text-center pb-3">Page {{ teams.page }} of {{ teams.total }} results</h6>
|
||||
<h5 class="text-muted text-center">
|
||||
Searching for teams with <strong>{{ field }}</strong> matching <strong>{{ q }}</strong>
|
||||
</h5>
|
||||
<h6 class="text-muted text-center pb-3">
|
||||
Page {{ teams.page }} of {{ teams.total }} results
|
||||
</h6>
|
||||
{% endif %}
|
||||
|
||||
{% with form = Forms.teams.TeamSearchForm() %}
|
||||
<form method="GET" class="form-inline">
|
||||
<div class="form-group col-md-2">
|
||||
<label for="sel1" class="sr-only" >Search Field</label>
|
||||
<select class="form-control custom-select w-100" id="sel1" name="field">
|
||||
<option value="name" {% if field == 'name' %}selected{% endif %}>Name</option>
|
||||
<option value="id" {% if field == 'id' %}selected{% endif %}>ID</option>
|
||||
<option value="affiliation" {% if field == 'affiliation' %}selected{% endif %}>Affiliation</option>
|
||||
</select>
|
||||
{{ form.field(class="form-control custom-select w-100") }}
|
||||
</div>
|
||||
<div class="form-group col-md-8">
|
||||
<label for="team-search" class="sr-only">Parameter</label>
|
||||
<input type="text" class="form-control w-100" id="team-search" name="q" placeholder="Search for matching teams" {% if q %}value="{{q}}"{% endif %}>
|
||||
{{ form.q(class="form-control w-100", placeholder="Search for matching teams") }}
|
||||
</div>
|
||||
<div class="form-group col-md-2">
|
||||
<label class="sr-only">Search</label>
|
||||
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-search" aria-hidden="true"></i></button>
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="fas fa-search" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -21,30 +21,29 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% if q and field %}
|
||||
<h5 class="text-muted text-center">Searching for users with <strong>{{ field }}</strong> matching <strong>{{ q }}</strong></h5>
|
||||
<h6 class="text-muted text-center pb-3">Page {{ users.page }} of {{ users.total }} results</h6>
|
||||
<h5 class="text-muted text-center">
|
||||
Searching for users with <strong>{{ field }}</strong> matching <strong>{{ q }}</strong>
|
||||
</h5>
|
||||
<h6 class="text-muted text-center pb-3">
|
||||
Page {{ users.page }} of {{ users.total }} results
|
||||
</h6>
|
||||
{% endif %}
|
||||
|
||||
{% with form = Forms.users.UserSearchForm(field=field, q=q) %}
|
||||
<form method="GET" class="form-inline">
|
||||
<div class="form-group col-md-2">
|
||||
<label for="sel1" class="sr-only" >Search Field</label>
|
||||
<select class="form-control custom-select w-100" id="sel1" name="field">
|
||||
<option value="name" {% if field == 'name' %}selected{% endif %}>Name</option>
|
||||
<option value="id" {% if field == 'id' %}selected{% endif %}>ID</option>
|
||||
<option value="email" {% if field == 'email' %}selected{% endif %}>Email</option>
|
||||
<option value="affiliation" {% if field == 'affiliation' %}selected{% endif %}>Affiliation</option>
|
||||
<option value="ip" {% if field == 'ip' %}selected{% endif %}>IP Address</option>
|
||||
</select>
|
||||
{{ form.field(class="form-control custom-select w-100") }}
|
||||
</div>
|
||||
<div class="form-group col-md-8">
|
||||
<label for="users-search" class="sr-only">Parameter</label>
|
||||
<input type="text" class="form-control w-100" id="users-search" name="q" placeholder="Search for matching user" {% if q %}value="{{q}}"{% endif %}>
|
||||
{{ form.q(class="form-control w-100", placeholder="Search for matching users") }}
|
||||
</div>
|
||||
<div class="form-group col-md-2">
|
||||
<label for="users-search" class="sr-only">Search</label>
|
||||
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-search" aria-hidden="true"></i></button>
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="fas fa-search" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script type="text/javascript">
|
||||
var init = {
|
||||
'urlRoot': "{{ request.script_root }}",
|
||||
'csrfNonce': "{{ nonce }}",
|
||||
'csrfNonce': "{{ Session.nonce }}",
|
||||
'userMode': "{{ Configs.user_mode }}",
|
||||
'userId': {{ Session.id }},
|
||||
'start': {{ Configs.start | tojson }},
|
||||
|
||||
@@ -26,15 +26,17 @@
|
||||
|
||||
<hr>
|
||||
|
||||
{% with form = Forms.auth.ConfirmForm() %}
|
||||
<form method="POST" action="{{ url_for('auth.confirm') }}">
|
||||
<h4 class="text-center">
|
||||
Need to resend the confirmation email?
|
||||
</h4>
|
||||
<div class="col-md-12 text-center">
|
||||
<button type="submit" tabindex="0" class="btn btn-md btn-primary btn-outlined">Resend</button>
|
||||
{{ form.submit(class="btn btn-md btn-primary btn-outlined") }}
|
||||
</div>
|
||||
<input type="hidden" name="nonce" value="{{ nonce }}">
|
||||
{{ form.nonce() }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,18 +22,15 @@
|
||||
<hr>
|
||||
{% endif %}
|
||||
|
||||
{% with form = Forms.auth.LoginForm() %}
|
||||
<form method="post" accept-charset="utf-8" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
User Name or Email
|
||||
</label>
|
||||
<input class="form-control" type="text" name="name" />
|
||||
<b>{{ form.name.label }}</b>
|
||||
{{ form.name(class="form-control", value=name) }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Password
|
||||
</label>
|
||||
<input class="form-control" type="password" name="password" />
|
||||
<b>{{ form.password.label }}</b>
|
||||
{{ form.password(class="form-control", value=password) }}
|
||||
</div>
|
||||
<div class="row pt-3">
|
||||
<div class="col-md-6">
|
||||
@@ -42,13 +39,12 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button type="submit" class="btn btn-md btn-primary btn-outlined float-right">
|
||||
Submit
|
||||
</button>
|
||||
{{ form.submit(class="btn btn-md btn-primary btn-outlined float-right") }}
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="nonce" value="{{ nonce }}">
|
||||
{{ form.nonce() }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,32 +22,28 @@
|
||||
<hr>
|
||||
{% endif %}
|
||||
|
||||
{% with form = Forms.auth.RegistrationForm() %}
|
||||
<form method="post" accept-charset="utf-8" autocomplete="off" role="form">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
User Name
|
||||
</label>
|
||||
<input class="form-control" type="text" name="name" {% if name %}value="{{ name }}"{% endif %} />
|
||||
{{ form.name.label }}
|
||||
{{ form.name(class="form-control", value=name) }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Email
|
||||
</label>
|
||||
<input class="form-control" type="text" name="email" {% if email %}value="{{ email }}"{% endif %} />
|
||||
{{ form.email.label }}
|
||||
{{ form.email(class="form-control", value=email) }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Password
|
||||
</label>
|
||||
<input class="form-control" type="password" name="password" {% if password %}value="{{ password }}"{% endif %}/>
|
||||
{{ form.password.label }}
|
||||
{{ form.password(class="form-control", value=password) }}
|
||||
</div>
|
||||
<div class="row pt-3">
|
||||
<div class="col-md-12">
|
||||
<button type="submit" class="btn btn-md btn-primary btn-outlined float-right">Submit</button>
|
||||
{{ form.submit(class="btn btn-md btn-primary btn-outlined float-right") }}
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="nonce" value="{{ nonce }}">
|
||||
{{ form.nonce() }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,34 +14,46 @@
|
||||
<div class="col-md-6 offset-md-3">
|
||||
{% include "components/errors.html" %}
|
||||
|
||||
{% if can_send_mail() %}
|
||||
<form method="post" accept-charset="utf-8" autocomplete="off" role="form" class="form-horizontal">
|
||||
<input name='nonce' type='hidden' value="{{ nonce }}">
|
||||
{% if mode %}
|
||||
<div class="form-group">
|
||||
<label for="password-input">
|
||||
Password
|
||||
</label>
|
||||
<input class="form-control" type="password" name="password" id="password-input" />
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="form-group">
|
||||
<label for="email-input">
|
||||
Email
|
||||
</label>
|
||||
<input class="form-control" type="text" name="email" id="email-input" />
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-md-6">
|
||||
<button type="submit" tabindex="0" class="btn btn-md btn-primary float-right">Submit</button>
|
||||
{% if mode == "set" %}
|
||||
{% with form = Forms.auth.ResetPasswordForm() %}
|
||||
<form method="post" accept-charset="utf-8" autocomplete="off" role="form" class="form-horizontal">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>You can now reset the password for your account and log in. Please enter in a new password below.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-group">
|
||||
{{ form.password.label }}
|
||||
{{ form.password(class="form-control") }}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-md-6">
|
||||
{{ form.submit(class="btn btn-md btn-primary float-right") }}
|
||||
</div>
|
||||
</div>
|
||||
{{ form.nonce() }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<h3 class="text-center">Contact a CTF organizer</h3>
|
||||
<p>This CTF is not configured to send email.</p>
|
||||
<p>Please contact an organizer to have your password reset</p>
|
||||
{% with form = Forms.auth.ResetPasswordRequestForm() %}
|
||||
<form method="post" accept-charset="utf-8" autocomplete="off" role="form" class="form-horizontal">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>Please provide the email address associated with your account below.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.email.label }}
|
||||
{{ form.email(class="form-control") }}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-md-6">
|
||||
{{ form.submit(class="btn btn-md btn-primary float-right") }}
|
||||
</div>
|
||||
</div>
|
||||
{{ form.nonce() }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,91 +22,70 @@
|
||||
<div class="tab-pane fade show active" id="profile" role="tabpanel">
|
||||
{% include "components/errors.html" %}
|
||||
|
||||
{% with form = Forms.self.SettingsForm(country=country) %}
|
||||
<form id="user-profile-form" method="post" accept-charset="utf-8" autocomplete="off" role="form"
|
||||
class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label for="name-input">
|
||||
User Name
|
||||
</label>
|
||||
<input class="form-control" type="text" name="name" id="name-input" value="{{name}}" />
|
||||
{{ form.name.label }}
|
||||
{{ form.name(class="form-control", value=name) }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email-input">
|
||||
Email
|
||||
</label>
|
||||
<input class="form-control" type="email" name="email" id="email-input" value="{{email}}" />
|
||||
{{ form.email.label }}
|
||||
{{ form.email(class="form-control", value=email) }}
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="confirm-input">
|
||||
Current Password
|
||||
</label>
|
||||
<input class="form-control" type="password" name="confirm" id="confirm-input" />
|
||||
{{ form.confirm.label }}
|
||||
{{ form.confirm(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password-input">
|
||||
New Password
|
||||
</label>
|
||||
<input class="form-control" type="password" name="password" id="password-input" />
|
||||
{{ form.password.label }}
|
||||
{{ form.password(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="affiliation-input">
|
||||
Affiliation
|
||||
</label>
|
||||
<input class="form-control" type="text" name="affiliation" id="affiliation-input"
|
||||
value="{% if affiliation %}{{affiliation}}{% endif %}" />
|
||||
{{ form.affiliation.label }}
|
||||
{{ form.affiliation(class="form-control", value=affiliation or "") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="website-input">
|
||||
Website
|
||||
</label>
|
||||
<input class="form-control" type="url" name="website" id="website-input"
|
||||
value="{% if website %}{{website}}{% endif %}" />
|
||||
{{ form.website.label }}
|
||||
{{ form.website(class="form-control", value=website or "") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="country-input">
|
||||
Country
|
||||
</label>
|
||||
<select class="form-control custom-select" id="country-input" name="country">
|
||||
<option value=""></option>
|
||||
{% set countries = get_countries() %}
|
||||
{% for country_code in countries.keys() %}
|
||||
<option value="{{ country_code }}" {% if country == country_code %}selected{% endif %}>
|
||||
{{ countries[country_code] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{{ form.country.label }}
|
||||
{{ form.country(class="form-control custom-select", value=country) }}
|
||||
</div>
|
||||
|
||||
<div id="results" class="form-group">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" tabindex="0" class="btn btn-md btn-primary btn-outlined float-right">Submit</button>
|
||||
{{ form.submit(class="btn btn-md btn-primary btn-outlined float-right") }}
|
||||
</div>
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="tab-pane fade" id="tokens" role="tabpanel">
|
||||
<form method="POST"id="user-token-form">
|
||||
{% with form = Forms.self.TokensForm() %}
|
||||
<form method="POST" id="user-token-form">
|
||||
<div class="form-group">
|
||||
<label for="name-input">
|
||||
Expiration
|
||||
</label>
|
||||
<input class="form-control" type="date" name="expiration" id="expiration-input" />
|
||||
{{ form.expiration.label }}
|
||||
{{ form.expiration(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<div class="form-group text-right">
|
||||
<button type="submit" class="btn btn-md btn-primary btn-outlined">Generate</button>
|
||||
{{ form.submit(class="btn btn-md btn-primary btn-outlined") }}
|
||||
</div>
|
||||
</form>
|
||||
{% endwith %}
|
||||
|
||||
{% if tokens %}
|
||||
<hr>
|
||||
<h4>Active Tokens</h4>
|
||||
<h4 class="text-center">Active Tokens</h4>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<div class="col-md-8 offset-md-2">
|
||||
{% include "components/errors.html" %}
|
||||
|
||||
{% with form = Forms.setup.SetupForm() %}
|
||||
<form method="post" accept-charset="utf-8" autocomplete="off" role="form" class="form-horizontal" id="setup-form">
|
||||
<ul class="nav nav-pills nav-fill mb-4">
|
||||
<li class="nav-item">
|
||||
@@ -35,38 +36,32 @@
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="general" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
CTF Name<br>
|
||||
<small class="form-text text-muted">
|
||||
Name for the CTF
|
||||
</small>
|
||||
</label>
|
||||
<input class="form-control" type="text" id="ctf_name" name="ctf_name" />
|
||||
<b>{{ form.ctf_name.label }}</b>
|
||||
{{ form.ctf_name(class="form-control") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.ctf_name.description }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
CTF Description<br>
|
||||
<small class="form-text text-muted">
|
||||
Description for the CTF
|
||||
</small>
|
||||
</label>
|
||||
<textarea class="form-control" type="text" id="ctf_description" name="ctf_description" rows="5"></textarea>
|
||||
<b>{{ form.ctf_description.label }}</b>
|
||||
{{ form.ctf_description(class="form-control", rows="5") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.ctf_description.description }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
CTF User Mode
|
||||
<small class="form-text text-muted">
|
||||
User Mode for the CTF. <br>
|
||||
Dictates whether users join teams to play (Team Mode) or
|
||||
play as themselves (User Mode)
|
||||
</small>
|
||||
</label>
|
||||
<select class="form-control custom-select" id="user_mode" name="user_mode">
|
||||
<option value="teams">Team Mode</option>
|
||||
<option value="users">User Mode</option>
|
||||
</select>
|
||||
<b>{{ form.user_mode.label }}</b>
|
||||
{% for radio in form.user_mode %}
|
||||
<div class="form-check ml-3">
|
||||
{{ radio(class="form-check-input") }}
|
||||
{{ radio.label }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.user_mode.description }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="float-right">
|
||||
@@ -77,31 +72,25 @@
|
||||
</div>
|
||||
<div class="tab-pane fade" id="administration" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Admin Username<br>
|
||||
<small class="form-text text-muted">
|
||||
Your username for the administration account
|
||||
</small>
|
||||
</label>
|
||||
<input class="form-control" type="text" name="name" required/>
|
||||
<b>{{ form.name.label }}</b>
|
||||
{{ form.name(class="form-control") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.name.description }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Admin Email<br>
|
||||
<small class="form-text text-muted">
|
||||
Your email address for the administration account
|
||||
</small>
|
||||
</label>
|
||||
<input class="form-control" type="email" name="email" required/>
|
||||
<b>{{ form.email.label }}</b>
|
||||
{{ form.email(class="form-control") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.email.description }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Admin Password<br>
|
||||
<small class="form-text text-muted">
|
||||
Your password for the administration account
|
||||
</small>
|
||||
</label>
|
||||
<input class="form-control" type="password" name="password" required/>
|
||||
<b>{{ form.password.label }}</b>
|
||||
{{ form.password(class="form-control") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.password.description }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
@@ -119,28 +108,17 @@
|
||||
</div>
|
||||
<div class="tab-pane fade" id="style" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Theme<br>
|
||||
<small class="form-text text-muted">
|
||||
CTFd Theme to use
|
||||
</small>
|
||||
</label>
|
||||
<select class="form-control custom-select" name="ctf_theme">
|
||||
{% for theme in themes %}
|
||||
<option>{{ theme }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<b>{{ form.ctf_theme.label }}</b>
|
||||
{{ form.ctf_theme(class="form-control custom-select") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.ctf_theme.description }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Theme Color
|
||||
<small class="form-text text-muted">
|
||||
Color used by theme to control aesthetics. Requires theme support.
|
||||
</small>
|
||||
</label>
|
||||
<b>{{ form.theme_color.label }}</b>
|
||||
<br>
|
||||
<div class="d-inline-block">
|
||||
<input type="hidden" name="theme_color" id="config-color-input">
|
||||
{{ form.theme_color(id="config-color-input") }}
|
||||
<div class="btn-group">
|
||||
<input type="color" id="config-color-picker" class="pr-1" style="width: 100px; height: 30px;" value="">
|
||||
</div>
|
||||
@@ -148,6 +126,9 @@
|
||||
<button type="button" id="config-color-reset">Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
<small class="form-text text-muted">
|
||||
{{ form.theme_color.description }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<button type="button" class="btn btn-primary btn-outlined tab-next" data-href="#datetime">
|
||||
@@ -157,12 +138,7 @@
|
||||
</div>
|
||||
<div class="tab-pane fade" id="datetime" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Start Time<br>
|
||||
<small class="form-text text-muted">
|
||||
Time when your CTF is scheduled to start. Optional.
|
||||
</small>
|
||||
</label>
|
||||
<b>{{ form.start.label }}</b>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<label>Date</label>
|
||||
@@ -174,18 +150,16 @@
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label>UTC Preview</label>
|
||||
<input class="form-control" type="text" id="start-preview" name="start" readonly/>
|
||||
{{ form.start(class="form-control", id="start-preview", readonly=True) }}
|
||||
</div>
|
||||
</div>
|
||||
<small class="form-text text-muted">
|
||||
{{ form.start.description }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
End Time<br>
|
||||
<small class="form-text text-muted">
|
||||
Time when your CTF is scheduled to end. Optional.
|
||||
</small>
|
||||
</label>
|
||||
<b>{{ form.end.label }}</b>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<label>Date</label>
|
||||
@@ -197,9 +171,12 @@
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label>UTC Preview</label>
|
||||
<input class="form-control" type="text" id="end-preview" name="end" readonly />
|
||||
{{ form.start(class="form-control", id="end-preview", readonly=True) }}
|
||||
</div>
|
||||
</div>
|
||||
<small class="form-text text-muted">
|
||||
{{ form.end.description }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="float-right">
|
||||
@@ -231,16 +208,14 @@
|
||||
<br>
|
||||
|
||||
<div class="submit-row float-right">
|
||||
<button type="submit" tabindex="0" class="btn btn-md btn-primary btn-outlined">
|
||||
Finish
|
||||
</button>
|
||||
{{ form.submit(class="btn btn-md btn-primary btn-outlined") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="nonce" value="{{ nonce }}">
|
||||
{# This nonce is implemented specially in the route itself #}
|
||||
{{ form.nonce() }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,24 +14,24 @@
|
||||
<div class="col-md-6 offset-md-3">
|
||||
{% include "components/errors.html" %}
|
||||
|
||||
{% with form = Forms.teams.TeamJoinForm() %}
|
||||
<form method="POST">
|
||||
<div class="form-group">
|
||||
<label>Team Name:</label>
|
||||
<input class="form-control" type="text" name="name">
|
||||
{{ form.name.label }}
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Team Password:</label>
|
||||
<input class="form-control" type="password" name="password">
|
||||
{{ form.password.label }}
|
||||
{{ form.password(class="form-control") }}
|
||||
</div>
|
||||
<input type="hidden" name="nonce" value="{{ nonce }}">
|
||||
<div class="row pt-3">
|
||||
<div class="col-md-12">
|
||||
<button type="submit" class="btn btn-success float-right">
|
||||
Join
|
||||
</button>
|
||||
{{ form.submit(class="btn btn-success float-right") }}
|
||||
</div>
|
||||
</div>
|
||||
{{ form.nonce() }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,25 +14,25 @@
|
||||
<div class="col-md-6 offset-md-3">
|
||||
{% include "components/errors.html" %}
|
||||
|
||||
{% with form = Forms.teams.TeamRegisterForm() %}
|
||||
<form method="POST">
|
||||
<div class="form-group">
|
||||
<label>Team Name:</label>
|
||||
<input class="form-control" type="text" name="name">
|
||||
{{ form.name.label }}
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Team Password:</label>
|
||||
<input class="form-control" type="password" name="password">
|
||||
{{ form.password.label }}
|
||||
{{ form.password(class="form-control") }}
|
||||
</div>
|
||||
<input type="hidden" name="nonce" value="{{ nonce }}">
|
||||
<div class="row pt-3">
|
||||
<div class="col-md-12">
|
||||
<p>After creating your team, share the team name and password with your teammates so they can join your team.</p>
|
||||
<button type="submit" class="btn btn-success float-right">
|
||||
Create
|
||||
</button>
|
||||
{{ form.submit(class="btn btn-success float-right") }}
|
||||
</div>
|
||||
</div>
|
||||
{{ form.nonce() }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,50 +14,38 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body clearfix">
|
||||
{% with form = Forms.teams.TeamSettingsForm(obj=team) %}
|
||||
<form id="team-info-form" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="name">Team Name</label>
|
||||
<input type="text" class="form-control" name="name" id="name"
|
||||
{% if team is defined and team.name %}value="{{ team.name }}"{% endif %} required />
|
||||
{{ form.name.label }}
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Current Password</label>
|
||||
<input type="password" class="form-control" name="confirm" id="confirm"/>
|
||||
{{ form.confirm.label }}
|
||||
{{ form.confirm(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" class="form-control" name="password" id="password"/>
|
||||
{{ form.password.label }}
|
||||
{{ form.password(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="website">Website</label>
|
||||
<input type="url" class="form-control" name="website" id="website"
|
||||
{% if team is defined and team.website %}value="{{ team.website }}"{% endif %} />
|
||||
{{ form.website.label }}
|
||||
{{ form.website(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">Affiliation</label>
|
||||
<input type="text" class="form-control" name="affiliation" id="affiliation"
|
||||
{% if team is defined and team.affiliation %}value="{{ team.affiliation }}"{% endif %} />
|
||||
{{ form.affiliation.label }}
|
||||
{{ form.affiliation(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">Country</label>
|
||||
<select class="form-control custom-select" id="country-input" name="country">
|
||||
<option></option>
|
||||
{% set countries = get_countries() %}
|
||||
{% for country_code in countries.keys() %}
|
||||
<option value="{{ country_code }}"{% if team is defined and team.country == country_code %} selected{% endif %}>
|
||||
{{ countries[country_code] }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{{ form.country.label }}
|
||||
{{ form.country(class="form-control custom-select") }}
|
||||
</div>
|
||||
<div id="results">
|
||||
|
||||
</div>
|
||||
<button id="update-team" type="submit"
|
||||
class="btn btn-primary btn-outlined float-right modal-action">
|
||||
Submit
|
||||
</button>
|
||||
{{ form.submit(class="btn btn-primary btn-outlined float-right modal-action") }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -73,28 +61,21 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body clearfix">
|
||||
{% with form = Forms.teams.TeamCaptainForm(captain_id=team.captain_id) %}
|
||||
<form id="team-captain-form" method="POST">
|
||||
<input type="hidden" name="id">
|
||||
<div class="form-group">
|
||||
<label for="captain">Team Captain</label>
|
||||
<select class="form-control custom-select" id="captain" name="captain_id">
|
||||
{% if team is defined %}
|
||||
<option value="{{ team.captain.id }}">{{ team.captain.name }}</option>
|
||||
{% for member in team.members %}
|
||||
{% if member.id != team.captain.id %}
|
||||
<option value="{{ member.id }}">{{ member.name }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</select>
|
||||
{{ form.captain_id.label }}
|
||||
{% for member in team.members %}
|
||||
{# Append members to the select choices #}
|
||||
{% set _ = form.captain_id.choices.append((member.id, member.name)) %}
|
||||
{% endfor %}
|
||||
{{ form.captain_id(class="form-control custom-select") }}
|
||||
</div>
|
||||
<div id="results">
|
||||
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-outlined float-right modal-action">
|
||||
Submit
|
||||
</button>
|
||||
{{ form.submit(class="btn btn-primary btn-outlined float-right modal-action") }}
|
||||
</form>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -262,6 +262,9 @@ COUNTRIES_LIST = [
|
||||
# Nicely titled (and translatable) country names.
|
||||
COUNTRIES_DICT = OrderedDict(COUNTRIES_LIST)
|
||||
|
||||
# List of countries suitable for use in forms
|
||||
SELECT_COUNTRIES_LIST = [("", "")] + COUNTRIES_LIST
|
||||
|
||||
|
||||
def get_countries():
|
||||
return COUNTRIES_DICT
|
||||
|
||||
@@ -60,6 +60,7 @@ def init_template_globals(app):
|
||||
from CTFd.constants.config import Configs
|
||||
from CTFd.constants.plugins import Plugins
|
||||
from CTFd.constants.sessions import Session
|
||||
from CTFd.forms import Forms
|
||||
|
||||
app.jinja_env.globals.update(config=config)
|
||||
app.jinja_env.globals.update(get_pages=get_pages)
|
||||
@@ -94,6 +95,7 @@ def init_template_globals(app):
|
||||
app.jinja_env.globals.update(Configs=Configs)
|
||||
app.jinja_env.globals.update(Plugins=Plugins)
|
||||
app.jinja_env.globals.update(Session=Session)
|
||||
app.jinja_env.globals.update(Forms=Forms)
|
||||
|
||||
|
||||
def init_logs(app):
|
||||
|
||||
@@ -226,12 +226,7 @@ def setup():
|
||||
cache.clear()
|
||||
|
||||
return redirect(url_for("views.static_html"))
|
||||
return render_template(
|
||||
"setup.html",
|
||||
nonce=session.get("nonce"),
|
||||
state=serialize(generate_nonce()),
|
||||
themes=config.get_themes(),
|
||||
)
|
||||
return render_template("setup.html", state=serialize(generate_nonce()))
|
||||
return redirect(url_for("views.static_html"))
|
||||
|
||||
|
||||
|
||||
@@ -22,3 +22,4 @@ flask-marshmallow==0.10.1
|
||||
marshmallow-sqlalchemy==0.17.0
|
||||
boto3==1.13.9
|
||||
marshmallow==2.20.2
|
||||
WTForms==2.3.1
|
||||
|
||||
@@ -277,7 +277,7 @@ def test_contact_for_password_reset():
|
||||
forgot_link = "http://localhost/reset_password"
|
||||
r = client.get(forgot_link)
|
||||
|
||||
assert "Contact a CTF organizer" in r.get_data(as_text=True)
|
||||
assert "contact an organizer" in r.get_data(as_text=True)
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user