team creation, admin panel, etc.
This commit is contained in:
parent
b7f4e75350
commit
dea6de5cf3
15 changed files with 289 additions and 41 deletions
|
@ -7,6 +7,7 @@ server {
|
||||||
index index.html index.htm;
|
index index.html index.htm;
|
||||||
|
|
||||||
server_name localhost;
|
server_name localhost;
|
||||||
|
error_page 404 /404;
|
||||||
|
|
||||||
# location / {
|
# location / {
|
||||||
# try_files $uri $uri/ =404;
|
# try_files $uri $uri/ =404;
|
||||||
|
@ -21,7 +22,7 @@ server {
|
||||||
default_type text/html;
|
default_type text/html;
|
||||||
try_files /index.html /index.html;
|
try_files /index.html /index.html;
|
||||||
}
|
}
|
||||||
location ~^/profile/(.*)$ {
|
location ~^/(profile|team)/(.*)$ {
|
||||||
default_type text/html;
|
default_type text/html;
|
||||||
try_files /index.html /index.html;
|
try_files /index.html /index.html;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,5 @@ import logger
|
||||||
import models
|
import models
|
||||||
import problem
|
import problem
|
||||||
import user
|
import user
|
||||||
|
import team
|
||||||
import utils
|
import utils
|
||||||
|
|
|
@ -6,7 +6,7 @@ import json
|
||||||
|
|
||||||
blueprint = Blueprint("admin", __name__)
|
blueprint = Blueprint("admin", __name__)
|
||||||
|
|
||||||
@blueprint.route("/problems/list", methods=["POST"])
|
@blueprint.route("/problems/list", methods=["GET"])
|
||||||
@api_wrapper
|
@api_wrapper
|
||||||
@admins_only
|
@admins_only
|
||||||
def problem_data():
|
def problem_data():
|
||||||
|
|
|
@ -13,9 +13,17 @@ def api_wrapper(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def wrapper(*args, **kwds):
|
def wrapper(*args, **kwds):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
token = session.pop("csrf_token")
|
try:
|
||||||
if not token or token != request.form.get("csrf_token"):
|
token = str(session.pop("csrf_token"))
|
||||||
return make_response(json.dumps({ "success": 0, "message": "Token has been tampered with." }), 403, response_header)
|
provided_token = str(request.form.get("csrf_token"))
|
||||||
|
if not token or token != provided_token:
|
||||||
|
raise Exception
|
||||||
|
except Exception, e:
|
||||||
|
response = make_response(json.dumps({ "success": 0, "message": "Token has been tampered with." }), 403, response_header)
|
||||||
|
token = utils.generate_string()
|
||||||
|
response.set_cookie("csrf_token", token)
|
||||||
|
session["csrf_token"] = token
|
||||||
|
return response
|
||||||
|
|
||||||
web_result = {}
|
web_result = {}
|
||||||
response = 200
|
response = 200
|
||||||
|
@ -29,11 +37,11 @@ def api_wrapper(f):
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
web_result = { "success": 0, "message": "Something went wrong! Please notify us about this immediately.", "error": [ str(error), traceback.format_exc() ] }
|
web_result = { "success": 0, "message": "Something went wrong! Please notify us about this immediately.", "error": [ str(error), traceback.format_exc() ] }
|
||||||
result = (json.dumps(web_result), response, response_header)
|
result = (json.dumps(web_result), response, response_header)
|
||||||
|
response = make_response(result)
|
||||||
|
|
||||||
# Setting CSRF token
|
# Setting CSRF token
|
||||||
if "token" not in session:
|
if "token" not in session:
|
||||||
token = utils.generate_string()
|
token = utils.generate_string()
|
||||||
response = make_response(result)
|
|
||||||
response.set_cookie("csrf_token", token)
|
response.set_cookie("csrf_token", token)
|
||||||
session["csrf_token"] = token
|
session["csrf_token"] = token
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,9 @@ def initialize_logs():
|
||||||
if not os.path.exists(log_path):
|
if not os.path.exists(log_path):
|
||||||
os.mkdir(log_path)
|
os.mkdir(log_path)
|
||||||
|
|
||||||
logs = [os.path.join(log_path, "registrations.log"), os.path.join(log_path, "logins.log"), os.path.join(log_path, "submissions.log")]
|
# logs = [os.path.join(log_path, "registrations.log"), os.path.join(log_path, "logins.log"), os.path.join(log_path, "submissions.log")]
|
||||||
|
logs = map(lambda x: os.path.join(log_path, x + ".log"), \
|
||||||
|
[ "registrations", "logins", "submissions", "create_team" ])
|
||||||
|
|
||||||
registration_log = logging.handlers.RotatingFileHandler(logs[0], maxBytes=10000)
|
registration_log = logging.handlers.RotatingFileHandler(logs[0], maxBytes=10000)
|
||||||
login_log = logging.handlers.RotatingFileHandler(logs[1], maxBytes=10000)
|
login_log = logging.handlers.RotatingFileHandler(logs[1], maxBytes=10000)
|
||||||
|
|
|
@ -29,17 +29,23 @@ class Users(db.Model):
|
||||||
|
|
||||||
class Teams(db.Model):
|
class Teams(db.Model):
|
||||||
tid = db.Column(db.Integer, primary_key=True)
|
tid = db.Column(db.Integer, primary_key=True)
|
||||||
name = db.Column(db.String(64), unique=True)
|
teamname = db.Column(db.String(64), unique=True)
|
||||||
join_code = db.Column(db.String(128), unique=True)
|
teamname_lower = db.Column(db.String(64), unique=True)
|
||||||
school = db.Column(db.Text)
|
school = db.Column(db.Text)
|
||||||
size = db.Column(db.Integer)
|
|
||||||
score = db.Column(db.Integer)
|
|
||||||
observer = db.Column(db.Boolean)
|
|
||||||
owner = db.Column(db.Integer)
|
owner = db.Column(db.Integer)
|
||||||
|
|
||||||
def __init__(self, name, school):
|
def __init__(self, teamname, owner):
|
||||||
self.name = name
|
self.teamname = teamname
|
||||||
self.school = school
|
self.teamname_lower = teamname.lower()
|
||||||
|
self.owner = owner
|
||||||
|
|
||||||
|
def get_members(self):
|
||||||
|
members = [ ]
|
||||||
|
for member in Users.query.filter_by(tid=self.tid).all():
|
||||||
|
members.append({
|
||||||
|
"username": member.username
|
||||||
|
})
|
||||||
|
return members
|
||||||
|
|
||||||
class Problems(db.Model):
|
class Problems(db.Model):
|
||||||
pid = db.Column(db.Integer, primary_key=True)
|
pid = db.Column(db.Integer, primary_key=True)
|
||||||
|
|
95
server/api/team.py
Normal file
95
server/api/team.py
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
from flask import Blueprint, request
|
||||||
|
from flask import current_app as app
|
||||||
|
from voluptuous import Schema, Length, Required
|
||||||
|
|
||||||
|
from models import db, Teams, Users
|
||||||
|
from decorators import api_wrapper, login_required, WebException
|
||||||
|
from user import in_team, get_user, is_logged_in
|
||||||
|
from schemas import verify_to_schema, check
|
||||||
|
|
||||||
|
import utils
|
||||||
|
|
||||||
|
blueprint = Blueprint("team", __name__)
|
||||||
|
|
||||||
|
###############
|
||||||
|
# TEAM ROUTES #
|
||||||
|
###############
|
||||||
|
|
||||||
|
@blueprint.route("/create", methods=["POST"])
|
||||||
|
@api_wrapper
|
||||||
|
@login_required
|
||||||
|
def team_create():
|
||||||
|
params = utils.flat_multi(request.form)
|
||||||
|
user = get_user().first()
|
||||||
|
if user.tid is not None or user.tid >= 0 or get_team(owner=user.uid).first() is not None:
|
||||||
|
raise WebException("You're already in a team!")
|
||||||
|
|
||||||
|
verify_to_schema(TeamSchema, params)
|
||||||
|
teamname = params.get("teamname")
|
||||||
|
|
||||||
|
team = Teams(teamname, user.uid)
|
||||||
|
with app.app_context():
|
||||||
|
db.session.add(team)
|
||||||
|
db.session.commit()
|
||||||
|
Users.query.filter_by(uid=user.uid).update({ "tid": team.tid })
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return { "success": 1, "message": "Success!" }
|
||||||
|
|
||||||
|
@blueprint.route("/info", methods=["GET"])
|
||||||
|
@api_wrapper
|
||||||
|
def team_info():
|
||||||
|
logged_in = is_logged_in()
|
||||||
|
me = False
|
||||||
|
teamname = utils.flat_multi(request.args).get("teamname")
|
||||||
|
if logged_in:
|
||||||
|
my_team = get_team().first()
|
||||||
|
if my_team is not None:
|
||||||
|
if teamname is None:
|
||||||
|
teamname = my_team.teamname
|
||||||
|
me = True
|
||||||
|
elif teamname.lower() == my_team.teamname.lower():
|
||||||
|
me = True
|
||||||
|
if teamname is None:
|
||||||
|
raise WebException("No team specified.")
|
||||||
|
team = get_team(teamname_lower=teamname.lower()).first()
|
||||||
|
if team is None:
|
||||||
|
raise WebException("Team not found.")
|
||||||
|
|
||||||
|
teamdata = {
|
||||||
|
"teamname": team.teamname
|
||||||
|
}
|
||||||
|
teamdata["in_team"] = me
|
||||||
|
return { "success": 1, "team": teamdata }
|
||||||
|
|
||||||
|
##################
|
||||||
|
# TEAM FUNCTIONS #
|
||||||
|
##################
|
||||||
|
|
||||||
|
__check_teamname = lambda teamname: get_team(teamname_lower=teamname.lower()).first() is None
|
||||||
|
|
||||||
|
TeamSchema = Schema({
|
||||||
|
Required("teamname"): check(
|
||||||
|
([str, Length(min=4, max=32)], "Your teamname should be between 4 and 32 characters long."),
|
||||||
|
([utils.__check_ascii], "Please only use ASCII characters in your teamname."),
|
||||||
|
([__check_teamname], "This teamname is taken, did you forget your password?")
|
||||||
|
),
|
||||||
|
}, extra=True)
|
||||||
|
|
||||||
|
def get_team(tid=None, teamname=None, teamname_lower=None, owner=None):
|
||||||
|
match = {}
|
||||||
|
if teamname != None:
|
||||||
|
match.update({ "teamname": teamname })
|
||||||
|
elif teamname_lower != None:
|
||||||
|
match.update({ "teamname_lower": teamname_lower })
|
||||||
|
elif tid != None:
|
||||||
|
match.update({ "tid": tid })
|
||||||
|
elif owner != None:
|
||||||
|
match.update({ "owner": owner })
|
||||||
|
elif is_logged_in():
|
||||||
|
user = get_user().first()
|
||||||
|
if user.tid is not None:
|
||||||
|
match.update({ "tid": user.tid })
|
||||||
|
with app.app_context():
|
||||||
|
result = Teams.query.filter_by(**match)
|
||||||
|
return result
|
|
@ -79,6 +79,8 @@ def user_status():
|
||||||
"admin": is_admin(),
|
"admin": is_admin(),
|
||||||
"username": session["username"] if logged_in else "",
|
"username": session["username"] if logged_in else "",
|
||||||
}
|
}
|
||||||
|
if logged_in:
|
||||||
|
result["has_team"] = in_team(get_user().first())
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -86,7 +88,7 @@ def user_status():
|
||||||
@api_wrapper
|
@api_wrapper
|
||||||
def user_info():
|
def user_info():
|
||||||
logged_in = is_logged_in()
|
logged_in = is_logged_in()
|
||||||
username = utils.flat_multi(request.form).get("username")
|
username = utils.flat_multi(request.args).get("username")
|
||||||
if username is None:
|
if username is None:
|
||||||
if logged_in:
|
if logged_in:
|
||||||
username = session["username"]
|
username = session["username"]
|
||||||
|
@ -116,8 +118,6 @@ def user_info():
|
||||||
# USER FUNCTIONS #
|
# USER FUNCTIONS #
|
||||||
##################
|
##################
|
||||||
|
|
||||||
__check_email_format = lambda email: re.match(".+@.+\..{2,}", email) is not None
|
|
||||||
__check_ascii = lambda s: all(ord(c) < 128 for c in s)
|
|
||||||
__check_username = lambda username: get_user(username_lower=username.lower()).first() is None
|
__check_username = lambda username: get_user(username_lower=username.lower()).first() is None
|
||||||
__check_email = lambda email: get_user(email=email.lower()).first() is None
|
__check_email = lambda email: get_user(email=email.lower()).first() is None
|
||||||
|
|
||||||
|
@ -125,19 +125,19 @@ UserSchema = Schema({
|
||||||
Required("email"): check(
|
Required("email"): check(
|
||||||
([str, Length(min=4, max=128)], "Your email should be between 4 and 128 characters long."),
|
([str, Length(min=4, max=128)], "Your email should be between 4 and 128 characters long."),
|
||||||
([__check_email], "Someone already registered this email."),
|
([__check_email], "Someone already registered this email."),
|
||||||
([__check_email_format], "Please enter a legit email.")
|
([utils.__check_email_format], "Please enter a legit email.")
|
||||||
),
|
),
|
||||||
Required("name"): check(
|
Required("name"): check(
|
||||||
([str, Length(min=4, max=128)], "Your name should be between 4 and 128 characters long.")
|
([str, Length(min=4, max=128)], "Your name should be between 4 and 128 characters long.")
|
||||||
),
|
),
|
||||||
Required("username"): check(
|
Required("username"): check(
|
||||||
([str, Length(min=4, max=32)], "Your username should be between 4 and 32 characters long."),
|
([str, Length(min=4, max=32)], "Your username should be between 4 and 32 characters long."),
|
||||||
([__check_ascii], "Please only use ASCII characters in your username."),
|
([utils.__check_ascii], "Please only use ASCII characters in your username."),
|
||||||
([__check_username], "This username is taken, did you forget your password?")
|
([__check_username], "This username is taken, did you forget your password?")
|
||||||
),
|
),
|
||||||
Required("password"): check(
|
Required("password"): check(
|
||||||
([str, Length(min=4, max=64)], "Your password should be between 4 and 64 characters long."),
|
([str, Length(min=4, max=64)], "Your password should be between 4 and 64 characters long."),
|
||||||
([__check_ascii], "Please only use ASCII characters in your password."),
|
([utils.__check_ascii], "Please only use ASCII characters in your password."),
|
||||||
),
|
),
|
||||||
Required("type"): check(
|
Required("type"): check(
|
||||||
([str, lambda x: x.isdigit()], "Please use the online form.")
|
([str, lambda x: x.isdigit()], "Please use the online form.")
|
||||||
|
@ -155,8 +155,8 @@ def get_user(username=None, username_lower=None, email=None, uid=None):
|
||||||
match.update({ "uid": uid })
|
match.update({ "uid": uid })
|
||||||
elif email != None:
|
elif email != None:
|
||||||
match.update({ "email": email })
|
match.update({ "email": email })
|
||||||
# elif api.auth.is_logged_in():
|
elif is_logged_in():
|
||||||
# match.update({ "uid": api.auth.get_uid() })
|
match.update({ "username": session["username"] })
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
result = Users.query.filter_by(**match)
|
result = Users.query.filter_by(**match)
|
||||||
return result
|
return result
|
||||||
|
@ -184,6 +184,9 @@ def login_user(username, password):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def in_team(user):
|
||||||
|
return user.tid is not None and user.tid >= 0
|
||||||
|
|
||||||
def is_logged_in():
|
def is_logged_in():
|
||||||
if not("sid" in session and "username" in session): return False
|
if not("sid" in session and "username" in session): return False
|
||||||
sid = session["sid"]
|
sid = session["sid"]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
import string
|
import string
|
||||||
import traceback
|
import traceback
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
@ -8,6 +9,9 @@ import unicodedata
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
|
||||||
|
__check_email_format = lambda email: re.match(".+@.+\..{2,}", email) is not None
|
||||||
|
__check_ascii = lambda s: all(c in string.printable for c in s)
|
||||||
|
|
||||||
def hash_password(s):
|
def hash_password(s):
|
||||||
return generate_password_hash(s)
|
return generate_password_hash(s)
|
||||||
|
|
||||||
|
@ -28,5 +32,5 @@ def flat_multi(multidict):
|
||||||
flat = {}
|
flat = {}
|
||||||
for key, values in multidict.items():
|
for key, values in multidict.items():
|
||||||
value = values[0] if type(values) == list and len(values) == 1 else values
|
value = values[0] if type(values) == list and len(values) == 1 else values
|
||||||
flat[key] = unicodedata.normalize("NFKD", value).encode("ascii", "ignore")
|
flat[key] = value.encode("utf-8")
|
||||||
return flat
|
return flat
|
|
@ -25,8 +25,9 @@ with app.app_context():
|
||||||
app.secret_key = config.SECRET_KEY
|
app.secret_key = config.SECRET_KEY
|
||||||
|
|
||||||
app.register_blueprint(api.admin.blueprint, url_prefix="/api/admin")
|
app.register_blueprint(api.admin.blueprint, url_prefix="/api/admin")
|
||||||
app.register_blueprint(api.user.blueprint, url_prefix="/api/user")
|
|
||||||
app.register_blueprint(api.problem.blueprint, url_prefix="/api/problem")
|
app.register_blueprint(api.problem.blueprint, url_prefix="/api/problem")
|
||||||
|
app.register_blueprint(api.team.blueprint, url_prefix="/api/team")
|
||||||
|
app.register_blueprint(api.user.blueprint, url_prefix="/api/user")
|
||||||
api.logger.initialize_logs()
|
api.logger.initialize_logs()
|
||||||
|
|
||||||
@app.route("/api")
|
@app.route("/api")
|
||||||
|
|
|
@ -44,6 +44,14 @@ app.config(function($routeProvider, $locationProvider) {
|
||||||
templateUrl: "pages/settings.html",
|
templateUrl: "pages/settings.html",
|
||||||
controller: "mainController"
|
controller: "mainController"
|
||||||
})
|
})
|
||||||
|
.when("/team", {
|
||||||
|
templateUrl: "pages/team.html",
|
||||||
|
controller: "teamController"
|
||||||
|
})
|
||||||
|
.when("/team/:teamname", {
|
||||||
|
templateUrl: "pages/team.html",
|
||||||
|
controller: "teamController"
|
||||||
|
})
|
||||||
.when("/admin/problems", {
|
.when("/admin/problems", {
|
||||||
templateUrl: "pages/admin/problems.html",
|
templateUrl: "pages/admin/problems.html",
|
||||||
controller: "adminProblemsController"
|
controller: "adminProblemsController"
|
||||||
|
@ -59,10 +67,9 @@ app.controller("mainController", ["$scope", "$http", function($scope, $http) {
|
||||||
$scope.config = { navbar: { } };
|
$scope.config = { navbar: { } };
|
||||||
$.get("/api/user/status", function(result) {
|
$.get("/api/user/status", function(result) {
|
||||||
if (result["success"] == 1) {
|
if (result["success"] == 1) {
|
||||||
$scope.config.navbar.logged_in = result["logged_in"];
|
delete result["success"];
|
||||||
$scope.config.navbar.username = result["username"];
|
$scope.config.navbar = result;
|
||||||
$scope.config.navbar.admin = result["admin"];
|
$scope.$emit("loginStatus");
|
||||||
$scope.$emit("adminStatus");
|
|
||||||
} else {
|
} else {
|
||||||
$scope.config.navbar.logged_in = false;
|
$scope.config.navbar.logged_in = false;
|
||||||
}
|
}
|
||||||
|
@ -92,9 +99,35 @@ app.controller("profileController", ["$controller", "$scope", "$http", "$routePa
|
||||||
});
|
});
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
app.controller("loginController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
||||||
|
$controller("mainController", { $scope: $scope });
|
||||||
|
$scope.$on("loginStatus", function() {
|
||||||
|
if ($scope.config["navbar"].logged_in != true) {
|
||||||
|
location.href = "/login";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller("teamController", ["$controller", "$scope", "$http", "$routeParams", function($controller, $scope, $http, $routeParams) {
|
||||||
|
var data = { };
|
||||||
|
if ("teamname" in $routeParams) {
|
||||||
|
data["teamname"] = $routeParams["teamname"];
|
||||||
|
} else {
|
||||||
|
$controller("loginController", { $scope: $scope });
|
||||||
|
}
|
||||||
|
$.get("/api/team/info", data, function(result) {
|
||||||
|
if (result["success"] == 1) {
|
||||||
|
$scope.team = result["team"];
|
||||||
|
}
|
||||||
|
$scope.$apply();
|
||||||
|
$(".timeago").timeago();
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
|
||||||
app.controller("adminController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
app.controller("adminController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
||||||
$controller("mainController", { $scope: $scope });
|
$controller("mainController", { $scope: $scope });
|
||||||
$scope.$on("adminStatus", function() {
|
$scope.$on("loginStatus", function() {
|
||||||
if ($scope.config["navbar"].logged_in != true) {
|
if ($scope.config["navbar"].logged_in != true) {
|
||||||
location.href = "/login";
|
location.href = "/login";
|
||||||
return;
|
return;
|
||||||
|
@ -177,3 +210,21 @@ var login_form = function() {
|
||||||
display_message("login_msg", "danger", "Error " + jqXHR["status"] + ": " + result["message"]);
|
display_message("login_msg", "danger", "Error " + jqXHR["status"] + ": " + result["message"]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// team page
|
||||||
|
|
||||||
|
var create_team = function() {
|
||||||
|
var input = "#create_team input";
|
||||||
|
var data = $("#create_team").serializeObject();
|
||||||
|
data["csrf_token"] = $.cookie("csrf_token");
|
||||||
|
$.post("/api/team/create", data, function(result) {
|
||||||
|
if (result["success"] == 1) {
|
||||||
|
location.reload(true);
|
||||||
|
} else {
|
||||||
|
display_message("create_team_msg", "danger", result["message"]);
|
||||||
|
}
|
||||||
|
}).fail(function(jqXHR, status, error) {
|
||||||
|
var result = JSON.parse(jqXHR["responseText"]);
|
||||||
|
display_message("create_team_msg", "danger", "Error " + jqXHR["status"] + ": " + result["message"]);
|
||||||
|
});
|
||||||
|
};
|
0
web/pages/learn.html
Normal file
0
web/pages/learn.html
Normal file
|
@ -39,7 +39,12 @@
|
||||||
<h4 class="panel-title">Team Information</h4>
|
<h4 class="panel-title">Team Information</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
Hi.
|
<div ng-show="user['in_team']==true">
|
||||||
|
</div>
|
||||||
|
<div ng-show="user['in_team']!=true">
|
||||||
|
<p>{{ user['me']==true ? "You're" : "This user is" }} not a part of a team.</p>
|
||||||
|
<a href="/team" class="btn btn-primary" ng-show="user['me']==true">Join or create one now »</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -73,7 +78,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="user['user_found']==false">
|
<div ng-show="user['user_found']!=true">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>User Not Found</h1>
|
<h1>User Not Found</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
71
web/pages/team.html
Normal file
71
web/pages/team.html
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<style>
|
||||||
|
.editable {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 15px;
|
||||||
|
outline: none;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
.editable:hover {
|
||||||
|
border: 1px solid #999;
|
||||||
|
}
|
||||||
|
.editable:focus {
|
||||||
|
border: 1px solid #999;
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
.padded {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div ng-show="!(config.navbar['logged_in']==true && config.navbar['has_team']!=true)">
|
||||||
|
<div class="jumbotron">
|
||||||
|
<center>
|
||||||
|
<div ng-show="team['in_team']==true">
|
||||||
|
<h1><span data-toggle="tooltip" data-placement="top" title="Click to edit team name." id="teamname_edit" class="editable" contenteditable>{{ team['teamname'] }}</span></h1>
|
||||||
|
<h4><i class="fa fa-fw fa-university"></i> <span data-toggle="tooltip" data-placement="top" title="Click to edit school." id="school_edit" class="editable" contenteditable>{{ team['school'] || 'Add School' }}</span></h4>
|
||||||
|
</div>
|
||||||
|
<div ng-show="team['in_team']!=true">
|
||||||
|
<h1><span class="padded">{{ team['teamname'] }}</span></h1>
|
||||||
|
<h4><i class="fa fa-fw fa-university"></i> <span class="padded">{{ team['school'] || 'Unknown Affiliation' }}</span></h4>
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ng-show="config.navbar['logged_in']==true && config.navbar['has_team']!=true">
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>Team</h1>
|
||||||
|
</div>
|
||||||
|
<p>To participate in EasyCTF, you must be on a <b>team</b>. If you'd like to go solo, just create a team by yourself. Read about team eligibility in the <a href="/rules">rules</a>.</p>
|
||||||
|
|
||||||
|
<form class="form-horizontal" onsubmit="create_team(); return false;" id="create_team">
|
||||||
|
<fieldset>
|
||||||
|
<div id="create_team_msg"></div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="input-group">
|
||||||
|
<input class="form-control input-lg" type="text" required name="teamname" id="teamname" placeholder="Choose a team name..." autocomplete="off" />
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<input type="hidden" id="_csrf" value="{{ csrf_token }}" />
|
||||||
|
<input type="submit" class="btn btn-success btn-lg" value="Create Team" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<h3>Invitations</h3>
|
||||||
|
</div>
|
||||||
|
<p>You need an invitation to join another team. If you'd like to request to be a member of their team, go to their team page and click the Request button.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$("#teamname_edit").on("keypress", function(e) {
|
||||||
|
if (e.keyCode == 13) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("[data-toggle=tooltip]").tooltip();
|
||||||
|
</script>
|
Loading…
Reference in a new issue