Add account searching to the user facing side (#1517)

* Add account searching to the user facing side of CTFd
* Closes #1354
This commit is contained in:
Kevin Chung
2020-06-28 23:29:48 -04:00
committed by GitHub
parent a30a518eed
commit 96ecdedfdf
7 changed files with 148 additions and 21 deletions

View File

@@ -38,7 +38,27 @@ class TeamCaptainForm(BaseForm):
class TeamSearchForm(BaseForm):
field = SelectField(
"Search Field",
choices=[("name", "Name"), ("id", "ID"), ("affiliation", "Affiliation")],
choices=[
("name", "Name"),
("id", "ID"),
("affiliation", "Affiliation"),
("website", "Website"),
],
default="name",
validators=[InputRequired()],
)
q = StringField("Parameter", validators=[InputRequired()])
submit = SubmitField("Search")
class PublicTeamSearchForm(BaseForm):
field = SelectField(
"Search Field",
choices=[
("name", "Name"),
("affiliation", "Affiliation"),
("website", "Website"),
],
default="name",
validators=[InputRequired()],
)

View File

@@ -15,6 +15,7 @@ class UserSearchForm(BaseForm):
("id", "ID"),
("email", "Email"),
("affiliation", "Affiliation"),
("website", "Website"),
("ip", "IP Address"),
],
default="name",
@@ -24,6 +25,21 @@ class UserSearchForm(BaseForm):
submit = SubmitField("Search")
class PublicUserSearchForm(BaseForm):
field = SelectField(
"Search Field",
choices=[
("name", "Name"),
("affiliation", "Affiliation"),
("website", "Website"),
],
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()])

View File

@@ -20,15 +20,34 @@ teams = Blueprint("teams", __name__)
@check_account_visibility
@require_team_mode
def listing():
page = abs(request.args.get("page", 1, type=int))
q = request.args.get("q")
field = request.args.get("field", "name")
filters = []
if field not in ("name", "affiliation", "website"):
field = "name"
if q:
filters.append(getattr(Teams, field).like("%{}%".format(q)))
teams = (
Teams.query.filter_by(hidden=False, banned=False)
.filter(*filters)
.order_by(Teams.id.asc())
.paginate(page=page, per_page=50)
.paginate(per_page=50)
)
return render_template("teams/teams.html", teams=teams)
args = dict(request.args)
args.pop("page", 1)
return render_template(
"teams/teams.html",
teams=teams,
prev_page=url_for(request.endpoint, page=teams.prev_num, **args),
next_page=url_for(request.endpoint, page=teams.next_num, **args),
q=q,
field=field,
)
@teams.route("/teams/join", methods=["GET", "POST"])

View File

@@ -28,7 +28,7 @@
</h6>
{% endif %}
{% with form = Forms.teams.TeamSearchForm() %}
{% with form = Forms.teams.TeamSearchForm(field=field, q=q) %}
<form method="GET" class="form-inline">
<div class="form-group col-md-2">
{{ form.field(class="form-control custom-select w-100") }}

View File

@@ -10,6 +10,37 @@
</div>
</div>
<div class="container">
<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>
{% endif %}
{% with form = Forms.teams.PublicTeamSearchForm(field=field, q=q) %}
<form method="GET" class="form-inline">
<div class="form-group col-md-2">
{{ form.field(class="form-control custom-select w-100") }}
</div>
<div class="form-group col-md-8">
{{ form.q(class="form-control w-100", placeholder="Search for matching teams") }}
</div>
<div class="form-group col-md-2">
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-search" aria-hidden="true"></i>
</button>
</div>
</form>
{% endwith %}
</div>
</div>
<hr>
<div class="row">
<div class="col-md-12">
<table class="table table-striped">
@@ -76,9 +107,7 @@
<div class="text-center">Page
<br>
{% if teams.page != 1 %}
<a href="{{ url_for('teams.listing', page=teams.page - 1) }}">
&lt;&lt;&lt;
</a>
<a href="{{ prev_page }}">&lt;&lt;&lt;</a>
{% endif %}
<select class="page-select">
{% for page in range(1, teams.pages + 1) %}
@@ -86,9 +115,7 @@
{% endfor %}
</select>
{% if teams.next_num %}
<a href="{{ url_for('teams.listing', page=teams.page + 1) }}">
&gt;&gt;&gt;
</a>
<a href="{{ next_page }}">&gt;&gt;&gt;</a>
{% endif %}
</div>
</div>

View File

@@ -10,6 +10,37 @@
</div>
</div>
<div class="container">
<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>
{% endif %}
{% with form = Forms.users.PublicUserSearchForm(field=field, q=q) %}
<form method="GET" class="form-inline">
<div class="form-group col-md-2">
{{ form.field(class="form-control custom-select w-100") }}
</div>
<div class="form-group col-md-8">
{{ form.q(class="form-control w-100", placeholder="Search for matching users") }}
</div>
<div class="form-group col-md-2">
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-search" aria-hidden="true"></i>
</button>
</div>
</form>
{% endwith %}
</div>
</div>
<hr>
<div class="row">
<div class="col-md-12">
<table class="table table-striped">
@@ -79,9 +110,7 @@
<div class="text-center">Page
<br>
{% if users.page != 1 %}
<a href="{{ url_for('users.listing', page=users.page - 1) }}">
&lt;&lt;&lt;
</a>
<a href="{{ prev_page }}">&lt;&lt;&lt;</a>
{% endif %}
<select class="page-select">
{% for page in range(1, users.pages + 1) %}
@@ -89,9 +118,7 @@
{% endfor %}
</select>
{% if users.next_num %}
<a href="{{ url_for('users.listing', page=users.page + 1) }}">
&gt;&gt;&gt;
</a>
<a href="{{ next_page }}">&gt;&gt;&gt;</a>
{% endif %}
</div>
</div>

View File

@@ -1,4 +1,4 @@
from flask import Blueprint, render_template, request
from flask import Blueprint, render_template, request, url_for
from CTFd.models import Users
from CTFd.utils import config
@@ -16,15 +16,33 @@ users = Blueprint("users", __name__)
@users.route("/users")
@check_account_visibility
def listing():
page = abs(request.args.get("page", 1, type=int))
q = request.args.get("q")
field = request.args.get("field", "name")
if field not in ("name", "affiliation", "website"):
field = "name"
filters = []
if q:
filters.append(getattr(Users, field).like("%{}%".format(q)))
users = (
Users.query.filter_by(banned=False, hidden=False)
.filter(*filters)
.order_by(Users.id.asc())
.paginate(page=page, per_page=50)
.paginate(per_page=50)
)
return render_template("users/users.html", users=users)
args = dict(request.args)
args.pop("page", 1)
return render_template(
"users/users.html",
users=users,
prev_page=url_for(request.endpoint, page=users.prev_num, **args),
next_page=url_for(request.endpoint, page=users.next_num, **args),
q=q,
field=field,
)
@users.route("/profile")