added profile page
This commit is contained in:
parent
80ca1377de
commit
df5d146e08
15 changed files with 288 additions and 139 deletions
|
@ -17,6 +17,10 @@ server {
|
||||||
default_type text/html;
|
default_type text/html;
|
||||||
try_files /index.html /index.html;
|
try_files /index.html /index.html;
|
||||||
}
|
}
|
||||||
|
location ~^/admin/(problems)$ {
|
||||||
|
default_type text/html;
|
||||||
|
try_files /index.html /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
location ~ /api {
|
location ~ /api {
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
|
4
deploy
4
deploy
|
@ -5,7 +5,9 @@ pkill gunicorn
|
||||||
sudo service nginx stop
|
sudo service nginx stop
|
||||||
tmux kill-session -t ctf 2> /dev/null
|
tmux kill-session -t ctf 2> /dev/null
|
||||||
|
|
||||||
|
sudo cp /vagrant/ctf.nginx /etc/nginx/sites-enabled/ctf
|
||||||
|
|
||||||
echo "Starting the server..."
|
echo "Starting the server..."
|
||||||
cd /home/vagrant/server
|
cd /home/vagrant/server
|
||||||
sudo service nginx start
|
sudo service nginx start
|
||||||
tmux new-session -s ctf -d 'gunicorn "app:app" -c /home/vagrant/scripts/gunicorn.py.ini'
|
tmux new-session -s ctf -d 'gunicorn "app:app" -c /home/vagrant/scripts/gunicorn.py.ini'
|
31
server/api.html
Normal file
31
server/api.html
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||||
|
<html><head><title>Python: package api</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
</head><body bgcolor="#f0f0f8">
|
||||||
|
|
||||||
|
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
||||||
|
<tr bgcolor="#7799ee">
|
||||||
|
<td valign=bottom> <br>
|
||||||
|
<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong>api</strong></big></big></font></td
|
||||||
|
><td align=right valign=bottom
|
||||||
|
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/vagrant/server/api/__init__.py">/home/vagrant/server/api/__init__.py</a></font></td></tr></table>
|
||||||
|
<p></p>
|
||||||
|
<p>
|
||||||
|
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||||
|
<tr bgcolor="#aa55cc">
|
||||||
|
<td colspan=3 valign=bottom> <br>
|
||||||
|
<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
|
||||||
|
|
||||||
|
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </td>
|
||||||
|
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="api.admin.html">admin</a><br>
|
||||||
|
<a href="api.api.html">api</a><br>
|
||||||
|
<a href="api.decorators.html">decorators</a><br>
|
||||||
|
</td><td width="25%" valign=top><a href="api.logger.html">logger</a><br>
|
||||||
|
<a href="api.models.html">models</a><br>
|
||||||
|
<a href="api.problem.html">problem</a><br>
|
||||||
|
</td><td width="25%" valign=top><a href="api.schemas.html">schemas</a><br>
|
||||||
|
<a href="api.user.html">user</a><br>
|
||||||
|
<a href="api.utils.html">utils</a><br>
|
||||||
|
</td><td width="25%" valign=top></td></tr></table></td></tr></table>
|
||||||
|
</body></html>
|
|
@ -1,19 +1,26 @@
|
||||||
from flask import Blueprint, jsonify
|
from flask import Blueprint, jsonify
|
||||||
from decorators import admins_only, api_wrapper, login_required
|
from decorators import admins_only, api_wrapper
|
||||||
from models import db, Problems, Files
|
from models import db, Problems, Files
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
blueprint = Blueprint("admin", __name__)
|
blueprint = Blueprint("admin", __name__)
|
||||||
|
|
||||||
@blueprint.route("/problem/data", methods=["POST"])
|
@blueprint.route("/problems/list", methods=["POST"])
|
||||||
#@api_wrapper # Disable atm due to json serialization issues: will fix
|
@api_wrapper
|
||||||
@admins_only
|
@admins_only
|
||||||
@login_required
|
|
||||||
def problem_data():
|
def problem_data():
|
||||||
problems = Problems.query.add_columns("pid", "name", "category", "description", "hint", "value", "solves", "disabled", "flag").order_by(Problems.value).all()
|
problems = Problems.query.order_by(Problems.value).all()
|
||||||
jason = []
|
problems_return = [ ]
|
||||||
|
for problem in problems:
|
||||||
for problem in problems:
|
problems_return.append({
|
||||||
problem_files = [ str(_file.location) for _file in Files.query.filter_by(pid=int(problem.pid)).all() ]
|
"pid": problem.pid,
|
||||||
jason.append({"pid": problem[1], "name": problem[2] ,"category": problem[3], "description": problem[4], "hint": problem[5], "value": problem[6], "solves": problem[7], "disabled": problem[8], "flag": problem[9], "files": problem_files})
|
"name": problem.name,
|
||||||
|
"category": problem.category,
|
||||||
return jsonify(data=jason)
|
"description": problem.description,
|
||||||
|
"hint": problem.hint,
|
||||||
|
"value": problem.value,
|
||||||
|
"threshold": problem.threshold,
|
||||||
|
"weightmap": json.loads(problem.weightmap)
|
||||||
|
})
|
||||||
|
return { "success": 1, "problems": problems_return }
|
|
@ -1,5 +1,5 @@
|
||||||
from flask.ext.sqlalchemy import SQLAlchemy
|
from flask.ext.sqlalchemy import SQLAlchemy
|
||||||
import datetime
|
import time
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
|
@ -14,6 +14,8 @@ class Users(db.Model):
|
||||||
password = db.Column(db.String(128))
|
password = db.Column(db.String(128))
|
||||||
admin = db.Column(db.Boolean)
|
admin = db.Column(db.Boolean)
|
||||||
utype = db.Column(db.Integer)
|
utype = db.Column(db.Integer)
|
||||||
|
tid = db.Column(db.Integer)
|
||||||
|
registertime = db.Column(db.Integer)
|
||||||
|
|
||||||
def __init__(self, name, username, email, password, utype=1):
|
def __init__(self, name, username, email, password, utype=1):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -23,6 +25,7 @@ class Users(db.Model):
|
||||||
self.password = utils.hash_password(password)
|
self.password = utils.hash_password(password)
|
||||||
self.utype = utype
|
self.utype = utype
|
||||||
self.admin = False
|
self.admin = False
|
||||||
|
self.registertime = int(time.time())
|
||||||
|
|
||||||
class Teams(db.Model):
|
class Teams(db.Model):
|
||||||
tid = db.Column(db.Integer, primary_key=True)
|
tid = db.Column(db.Integer, primary_key=True)
|
||||||
|
@ -90,11 +93,11 @@ class LoginTokens(db.Model):
|
||||||
ua = db.Column(db.String(128))
|
ua = db.Column(db.String(128))
|
||||||
ip = db.Column(db.String(16))
|
ip = db.Column(db.String(16))
|
||||||
|
|
||||||
def __init__(self, uid, username, expiry=datetime.datetime.utcnow(), active=True, ua=None, ip=None):
|
def __init__(self, uid, username, expiry=int(time.time()), active=True, ua=None, ip=None):
|
||||||
self.sid = utils.generate_string()
|
self.sid = utils.generate_string()
|
||||||
self.uid = uid
|
self.uid = uid
|
||||||
self.username = username
|
self.username = username
|
||||||
self.issued = datetime.datetime.utcnow()
|
self.issued = int(time.time())
|
||||||
self.expiry = expiry
|
self.expiry = expiry
|
||||||
self.active = active
|
self.active = active
|
||||||
self.ua = ua
|
self.ua = ua
|
||||||
|
|
|
@ -6,6 +6,7 @@ from models import db, LoginTokens, Users
|
||||||
from decorators import api_wrapper, WebException
|
from decorators import api_wrapper, WebException
|
||||||
from schemas import verify_to_schema, check
|
from schemas import verify_to_schema, check
|
||||||
|
|
||||||
|
import datetime
|
||||||
import logger
|
import logger
|
||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
@ -43,7 +44,7 @@ def user_register():
|
||||||
|
|
||||||
return { "success": 1, "message": "Success!" }
|
return { "success": 1, "message": "Success!" }
|
||||||
|
|
||||||
@blueprint.route("/logout", methods=["GET", "POST"])
|
@blueprint.route("/logout", methods=["POST"])
|
||||||
@api_wrapper
|
@api_wrapper
|
||||||
def user_logout():
|
def user_logout():
|
||||||
sid = session["sid"]
|
sid = session["sid"]
|
||||||
|
@ -80,6 +81,36 @@ def user_status():
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@blueprint.route("/info", methods=["POST"])
|
||||||
|
@api_wrapper
|
||||||
|
def user_info():
|
||||||
|
logged_in = is_logged_in()
|
||||||
|
username = utils.flat_multi(request.form).get("username")
|
||||||
|
if username is None:
|
||||||
|
if logged_in:
|
||||||
|
username = session["username"]
|
||||||
|
if username is None:
|
||||||
|
raise WebException("No user specified.")
|
||||||
|
me = username.lower() == session["username"].lower()
|
||||||
|
user = get_user(username_lower=username.lower()).first()
|
||||||
|
if user is None:
|
||||||
|
raise WebException("User not found.")
|
||||||
|
|
||||||
|
show_email = me if logged_in else False
|
||||||
|
userdata = {
|
||||||
|
"user_found": True,
|
||||||
|
"name": user.name,
|
||||||
|
"username": user.username,
|
||||||
|
"type": ["Student", "Instructor", "Observer"][user.utype - 1],
|
||||||
|
"admin": user.admin,
|
||||||
|
"registertime": datetime.datetime.fromtimestamp(user.registertime).isoformat() + "Z",
|
||||||
|
"me": me,
|
||||||
|
"show_email": show_email
|
||||||
|
}
|
||||||
|
if show_email:
|
||||||
|
userdata["email"] = user.email
|
||||||
|
return { "success": 1, "user": userdata }
|
||||||
|
|
||||||
##################
|
##################
|
||||||
# USER FUNCTIONS #
|
# USER FUNCTIONS #
|
||||||
##################
|
##################
|
||||||
|
@ -148,7 +179,7 @@ def login_user(username, password):
|
||||||
|
|
||||||
session["sid"] = token.sid
|
session["sid"] = token.sid
|
||||||
session["username"] = token.username
|
session["username"] = token.username
|
||||||
session["admin"] = user.utype == 0
|
session["admin"] = user.admin == True
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ import api
|
||||||
import config
|
import config
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from api.decorators import api_wrapper
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
app.config["SQLALCHEMY_DATABASE_URI"] = config.SQLALCHEMY_DATABASE_URI
|
app.config["SQLALCHEMY_DATABASE_URI"] = config.SQLALCHEMY_DATABASE_URI
|
||||||
|
@ -24,8 +26,9 @@ app.register_blueprint(api.problem.blueprint, url_prefix="/api/problem")
|
||||||
api.logger.initialize_logs()
|
api.logger.initialize_logs()
|
||||||
|
|
||||||
@app.route("/api")
|
@app.route("/api")
|
||||||
|
@api_wrapper
|
||||||
def api_main():
|
def api_main():
|
||||||
return json.dumps({ "success": 1, "message": "The API is online." })
|
return { "success": 1, "message": "The API is online." }
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
|
|
@ -10,4 +10,14 @@
|
||||||
|
|
||||||
* {
|
* {
|
||||||
font-family: "Proxima Nova";
|
font-family: "Proxima Nova";
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding: 10px;
|
||||||
|
> .tab-pane {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
> .active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -39,6 +39,16 @@
|
||||||
<div ng-show="config.navbar['competition_started']==true">
|
<div ng-show="config.navbar['competition_started']==true">
|
||||||
</div>
|
</div>
|
||||||
<li><a href="/chat">Chat</a></li>
|
<li><a href="/chat">Chat</a></li>
|
||||||
|
<li class="dropdown" ng-show="config.navbar['admin']==true">
|
||||||
|
<a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Admin <span class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="/admin/stats">Statistics</a></li>
|
||||||
|
<li><a href="/admin/problems">Problem Editor</a></li>
|
||||||
|
<li><a href="/admin/teams">Team Management</a></li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
<li><a href="/admin/settings">CTF Settings</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ config.navbar["username"] }} <span class="caret"></span></a>
|
<a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ config.navbar["username"] }} <span class="caret"></span></a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
@ -55,7 +65,7 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="mainContent" class="ui container">
|
<div id="mainContent" class="container">
|
||||||
<div ng-view></div>
|
<div ng-view></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -64,7 +74,8 @@
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js" integrity="sha384-r1y8TJcloKTvouxnYsi4PJAx+nHNr90ibsEn3zznzDzWBN9X3o3kbHLSgcIPtzAp" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js" integrity="sha384-r1y8TJcloKTvouxnYsi4PJAx+nHNr90ibsEn3zznzDzWBN9X3o3kbHLSgcIPtzAp" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular-route.min.js" integrity="sha384-9MZDoFf10trgrfsQOs9GJhf/mP/sh5weVp3FDSi8h/4TEaV6dloEDkpxGTaOmAs6" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular-route.min.js" integrity="sha384-9MZDoFf10trgrfsQOs9GJhf/mP/sh5weVp3FDSi8h/4TEaV6dloEDkpxGTaOmAs6" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.9.3/js/bootstrap-select.min.js" integrity="sha384-1qZEXZBmj54fSiiWT8bZQGEpCumJWDrAoEqMdg6N5bTTLCkU5RXoNeUsKWekRYob" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.9.3/js/bootstrap-select.min.js" integrity="sha384-1qZEXZBmj54fSiiWT8bZQGEpCumJWDrAoEqMdg6N5bTTLCkU5RXoNeUsKWekRYob" crossorigin="anonymous"></script>
|
||||||
<script src="js/easyctf.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.4.3/jquery.timeago.min.js" integrity="sha384-Bap3DetwPgo4GEFvaIDVSIrz5G0mUAUsfCUcEsi+JrrNu7dyj3gBmuAG4hDIGg/4" crossorigin="anonymous"></script>
|
||||||
|
<script src="/js/easyctf.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -16,6 +16,10 @@ app.config(function($routeProvider, $locationProvider) {
|
||||||
templateUrl: "pages/learn.html",
|
templateUrl: "pages/learn.html",
|
||||||
controller: "mainController"
|
controller: "mainController"
|
||||||
})
|
})
|
||||||
|
.when("/chat", {
|
||||||
|
templateUrl: "pages/chat.html",
|
||||||
|
controller: "mainController"
|
||||||
|
})
|
||||||
.when("/register", {
|
.when("/register", {
|
||||||
templateUrl: "pages/register.html",
|
templateUrl: "pages/register.html",
|
||||||
controller: "mainController"
|
controller: "mainController"
|
||||||
|
@ -26,11 +30,19 @@ app.config(function($routeProvider, $locationProvider) {
|
||||||
})
|
})
|
||||||
.when("/profile", {
|
.when("/profile", {
|
||||||
templateUrl: "pages/profile.html",
|
templateUrl: "pages/profile.html",
|
||||||
controller: "mainController"
|
controller: "profileController"
|
||||||
})
|
})
|
||||||
.when("/logout", {
|
.when("/logout", {
|
||||||
templateUrl: "pages/blank.html",
|
templateUrl: "pages/blank.html",
|
||||||
controller: "logoutController"
|
controller: "logoutController"
|
||||||
|
})
|
||||||
|
.when("/admin/problems", {
|
||||||
|
templateUrl: "pages/admin/problems.html",
|
||||||
|
controller: "adminProblemsController"
|
||||||
|
})
|
||||||
|
.otherwise({
|
||||||
|
templateUrl: "pages/404.html",
|
||||||
|
controller: "mainController"
|
||||||
});
|
});
|
||||||
$locationProvider.html5Mode(true);
|
$locationProvider.html5Mode(true);
|
||||||
});
|
});
|
||||||
|
@ -41,11 +53,14 @@ app.controller("mainController", ["$scope", "$http", function($scope, $http) {
|
||||||
if (result["success"] == 1) {
|
if (result["success"] == 1) {
|
||||||
$scope.config.navbar.logged_in = result["logged_in"];
|
$scope.config.navbar.logged_in = result["logged_in"];
|
||||||
$scope.config.navbar.username = result["username"];
|
$scope.config.navbar.username = result["username"];
|
||||||
|
$scope.config.navbar.admin = result["admin"];
|
||||||
} else {
|
} else {
|
||||||
$scope.config.navbar.logged_in = false;
|
$scope.config.navbar.logged_in = false;
|
||||||
}
|
}
|
||||||
|
$scope.$apply();
|
||||||
}).fail(function() {
|
}).fail(function() {
|
||||||
$scope.config.navbar.logged_in = false;
|
$scope.config.navbar.logged_in = false;
|
||||||
|
$scope.$apply();
|
||||||
});
|
});
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
@ -55,6 +70,21 @@ app.controller("logoutController", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.controller("profileController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
||||||
|
$controller("mainController", { $scope: $scope });
|
||||||
|
$.post("/api/user/info", function(result) {
|
||||||
|
if (result["success"] == 1) {
|
||||||
|
$scope.user = result["user"];
|
||||||
|
}
|
||||||
|
$scope.$apply();
|
||||||
|
$(".timeago").timeago();
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller("adminProblemsController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
||||||
|
$controller("mainController", { $scope: $scope });
|
||||||
|
}]);
|
||||||
|
|
||||||
function display_message(containerId, alertType, message, callback) {
|
function display_message(containerId, alertType, message, callback) {
|
||||||
$("#" + containerId).html("<div class=\"alert alert-" + alertType + "\">" + message + "</div>");
|
$("#" + containerId).html("<div class=\"alert alert-" + alertType + "\">" + message + "</div>");
|
||||||
$("#" + containerId).hide().slideDown("fast", "swing", function() {
|
$("#" + containerId).hide().slideDown("fast", "swing", function() {
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
function login_form(email, password) {
|
|
||||||
$("#login").attr("disabled", "disabled");
|
|
||||||
$.post("/api/user/login", {
|
|
||||||
email: email,
|
|
||||||
password: password
|
|
||||||
}, function(data) {
|
|
||||||
if (data.success == 1) {
|
|
||||||
display_message("status", "success", "Success!", function() {
|
|
||||||
$("#login").removeAttr("disabled");
|
|
||||||
window.location = "#/account";
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
display_message("status", "danger", data.message, function() {$("#login").removeAttr("disabled");});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
$("#registration-form").on("submit", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
register($("#name").val(), $("#username").val(), $("#password").val(), $("#password_confirm").val(), $("#email").val(), $("#g-recaptcha-response").val());
|
|
||||||
});
|
|
||||||
|
|
||||||
function register(name, username, password, password_confirm, email, captcha_response) {
|
|
||||||
$("#register").attr("disabled", "disabled");
|
|
||||||
$.post("/api/user/register", {
|
|
||||||
name: name,
|
|
||||||
username: username,
|
|
||||||
password: password,
|
|
||||||
password_confirm: password_confirm,
|
|
||||||
email: email,
|
|
||||||
captcha_response: captcha_response
|
|
||||||
}, function(data) {
|
|
||||||
$("#status").text(data.message);
|
|
||||||
if (data.success == 1) {
|
|
||||||
display_message("status", "success", "Success!", function() {
|
|
||||||
$("#register").removeAttr("disabled");
|
|
||||||
window.location = "#/login";
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
display_message("status", "danger", data.message, function() {$("#register").removeAttr("disabled")});
|
|
||||||
grecaptcha.reset();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
5
web/pages/404.html
Normal file
5
web/pages/404.html
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>404: Page Not Found</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>Go away. Stop snooping around, you little creep.</p>
|
|
@ -1,77 +1,77 @@
|
||||||
<center class="fade_in ng-scope">
|
<center class="fade_in ng-scope">
|
||||||
<h1>Problems</h1>
|
<h1>Problems</h1>
|
||||||
<div id="status"></div>
|
<div id="status"></div>
|
||||||
<input type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#add-modal" value="Add Problem">
|
<input type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#add-modal" value="Add Problem">
|
||||||
<div id="problems"></div>
|
<div id="problems"></div>
|
||||||
<div class="modal fade" id="add-modal" tabindex="-2" role="dialog" aria-labelledby="add-modal-label" data-backdrop="false">
|
<div class="modal fade" id="add-modal" tabindex="-2" role="dialog" aria-labelledby="add-modal-label" data-backdrop="false">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
<h4 class="modal-title" id="add-modal-label">Add Problem</h4>
|
<h4 class="modal-title" id="add-modal-label">Add Problem</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form id="add-form" method="POST" action="/api/problem/add" enctype="multipart/form-data">
|
<form id="add-form" method="POST" action="/api/problem/add" enctype="multipart/form-data">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<input type="text" name="name" id="name" autocomplete="on" placeholder="Name" class="form-control">
|
<input type="text" name="name" id="name" autocomplete="on" placeholder="Name" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<input type="text" name="category" id="category" autocomplete="on" placeholder="Category" class="form-control">
|
<input type="text" name="category" id="category" autocomplete="on" placeholder="Category" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<textarea type="text" name="description" id="description" autocomplete="on" placeholder="Description" class="form-control"></textarea>
|
<textarea type="text" name="description" id="description" autocomplete="on" placeholder="Description" class="form-control"></textarea>
|
||||||
<br><br>
|
<br><br>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<input type="text" name="flag" id="flag" autocomplete="off" placeholder="EasyCTF{insert_correct_flag_here}" class="form-control">
|
<input type="text" name="flag" id="flag" autocomplete="off" placeholder="EasyCTF{insert_correct_flag_here}" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<input type="text" name="problem-hint" id="problem-hint" autocomplete="off" placeholder="Hint" class="form-control">
|
<input type="text" name="problem-hint" id="problem-hint" autocomplete="off" placeholder="Hint" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="number" name="value" id="value" autocomplete="off" placeholder="Value" class="form-control-number center-block">
|
<input type="number" name="value" id="value" autocomplete="off" placeholder="Value" class="form-control-number center-block">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer">
|
||||||
<h4>These are important files!</h4>
|
<h4>These are important files!</h4>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="file" name="files[]" id="files" multiple="true">
|
<input type="file" name="files[]" id="files" multiple="true">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
<input form="add-form" id="add-problem" type="submit" class="btn btn-primary" value="Add Problem">
|
<input form="add-form" id="add-problem" type="submit" class="btn btn-primary" value="Add Problem">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade" id="delete-modal" tabindex="-1" role="dialog" aria-labelledby="delete-modal-label" data-backdrop="false">
|
<div class="modal fade" id="delete-modal" tabindex="-1" role="dialog" aria-labelledby="delete-modal-label" data-backdrop="false">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
<h4 class="modal-title" id="delete-modal-label">Delete Problem</h4>
|
<h4 class="modal-title" id="delete-modal-label">Delete Problem</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
Are you sure you want to delete this problem? You cannot undo this.
|
Are you sure you want to delete this problem? You cannot undo this.
|
||||||
<div id="delete_status"></div>
|
<div id="delete_status"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">No</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">No</button>
|
||||||
<button type="button" id="delete" class="btn btn-primary">Yes</button>
|
<button type="button" id="delete" class="btn btn-primary">Yes</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="js/admin/problems.js"></script>
|
<script src="js/admin/problems.js"></script>
|
||||||
</center>
|
</center>
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<div ng-show="user['user_found']==true">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 col-xs-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<img src="//www.gravatar.com/avatar/?size=512" id="avatar" style="max-width:100%;" />
|
||||||
|
<small style="display:block;text-align:right;" ng-show="user['me']==true"><a href="http://en.gravatar.com/emails/" target="_blank">Edit Picture</a></small>
|
||||||
|
<h2 style="margin:0px;font-weight:bold;font-size:2em;">{{ user.name }}</h2>
|
||||||
|
<small style="display:block;font-size:1.5em;">@{{ user.username }}</small>
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<i class="fa fa-fw fa-user"></i>
|
||||||
|
{{ user.type }}
|
||||||
|
<div class="label label-info" ng-show="user['admin']==true">ADMIN</div>
|
||||||
|
</div>
|
||||||
|
<div ng-show="user['show_email']==true">
|
||||||
|
<i class="fa fa-fw fa-envelope"></i>
|
||||||
|
<a style="color:#666;" href="mailto:{{ user.email }}">
|
||||||
|
<span id="email">{{ user.email }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<i class="fa fa-fw fa-clocK-o"></i>
|
||||||
|
Joined <time class="timeago" datetime="{{ user.registertime }}"></time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9 col-xs-12">
|
||||||
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
|
<li role="presentation" class="active"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">Profile</a></li>
|
||||||
|
<li role="presentation"><a href="#activity" aria-controls="activity" role="tab" data-toggle="tab">Activity</a></li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div role="tabpanel" class="tab-pane active" id="profile">
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="activity">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ng-show="user['user_found']==false">
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>User Not Found</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>The user you were looking for doesn't exist. Check to make sure you've spelled the name right.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
$("ul[role=tablist]").tab();
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Reference in a new issue