Finished up project - with bonus: card filters!
This commit is contained in:
15
README.md
15
README.md
@@ -31,15 +31,14 @@ To Do:
|
||||
- [x] Auth errors
|
||||
- [x] Session set
|
||||
- [x] Logout
|
||||
- [ ] Card Area
|
||||
- [x] Card Area
|
||||
- [x] List cards
|
||||
- [x] Add cards
|
||||
- [x] Edit card
|
||||
- [ ] Delete card
|
||||
- [x] Delete card
|
||||
- Card Memorization
|
||||
- [ ] Card type toggle
|
||||
- [ ] Show card
|
||||
- [ ] Flip card
|
||||
- [ ] Next card
|
||||
- [ ] Last card
|
||||
- [ ] Mark as known
|
||||
- [x] Card type toggle
|
||||
- [x] Flip card
|
||||
- [x] Mark as known, next card
|
||||
- [x] Next card
|
||||
- [x] Card filters
|
||||
|
||||
109
flash_cards.py
109
flash_cards.py
@@ -57,7 +57,10 @@ def close_db(error):
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
if session.get('logged_in'):
|
||||
return redirect(url_for('general'))
|
||||
else:
|
||||
return redirect(url_for('login'))
|
||||
|
||||
|
||||
@app.route('/cards')
|
||||
@@ -65,9 +68,39 @@ def cards():
|
||||
if not session.get('logged_in'):
|
||||
return redirect(url_for('login'))
|
||||
db = get_db()
|
||||
cur = db.execute('SELECT id, type, front, back, known FROM cards ORDER BY id DESC')
|
||||
query = '''
|
||||
SELECT id, type, front, back, known
|
||||
FROM cards
|
||||
ORDER BY id DESC
|
||||
'''
|
||||
cur = db.execute(query)
|
||||
cards = cur.fetchall()
|
||||
return render_template('cards.html', cards=cards)
|
||||
return render_template('cards.html', cards=cards, filter_name=None)
|
||||
|
||||
|
||||
@app.route('/filter_cards/<filter_name>')
|
||||
def filter_cards(filter_name):
|
||||
if not session.get('logged_in'):
|
||||
return redirect(url_for('login'))
|
||||
|
||||
filters = {
|
||||
"all": "where 1 = 1",
|
||||
"general": "where type = 1",
|
||||
"code": "where type = 2",
|
||||
"known": "where known = 1",
|
||||
"unknown": "where known = 0",
|
||||
}
|
||||
|
||||
query = filters.get(filter_name)
|
||||
|
||||
if not query:
|
||||
return redirect(url_for('cards'))
|
||||
|
||||
db = get_db()
|
||||
fullquery = "SELECT id, type, front, back, known FROM cards " + query + " ORDER BY id DESC"
|
||||
cur = db.execute(fullquery)
|
||||
cards = cur.fetchall()
|
||||
return render_template('cards.html', cards=cards, filter_name=filter_name)
|
||||
|
||||
|
||||
@app.route('/add', methods=['POST'])
|
||||
@@ -90,7 +123,12 @@ def edit(card_id):
|
||||
if not session.get('logged_in'):
|
||||
return redirect(url_for('login'))
|
||||
db = get_db()
|
||||
cur = db.execute('SELECT * FROM cards WHERE id = ?', [card_id])
|
||||
query = '''
|
||||
SELECT id, type, front, back, known
|
||||
FROM cards
|
||||
WHERE id = ?
|
||||
'''
|
||||
cur = db.execute(query, [card_id])
|
||||
card = cur.fetchone()
|
||||
return render_template('edit.html', card=card)
|
||||
|
||||
@@ -102,7 +140,16 @@ def edit_card():
|
||||
selected = request.form.getlist('known')
|
||||
known = bool(selected)
|
||||
db = get_db()
|
||||
db.execute('UPDATE cards set type = ?, front = ?, back = ?, known = ? where id = ?',
|
||||
command = '''
|
||||
UPDATE cards
|
||||
SET
|
||||
type = ?,
|
||||
front = ?,
|
||||
back = ?,
|
||||
known = ?
|
||||
WHERE id = ?
|
||||
'''
|
||||
db.execute(command,
|
||||
[request.form['type'],
|
||||
request.form['front'],
|
||||
request.form['back'],
|
||||
@@ -129,14 +176,62 @@ def delete(card_id):
|
||||
def general():
|
||||
if not session.get('logged_in'):
|
||||
return redirect(url_for('login'))
|
||||
return render_template('general.html')
|
||||
return memorize("general")
|
||||
|
||||
|
||||
@app.route('/code')
|
||||
def code():
|
||||
if not session.get('logged_in'):
|
||||
return redirect(url_for('login'))
|
||||
return render_template('code.html')
|
||||
return memorize("code")
|
||||
|
||||
|
||||
def memorize(card_type):
|
||||
if card_type == "general":
|
||||
type = 1
|
||||
elif card_type == "code":
|
||||
type = 2
|
||||
else:
|
||||
return redirect(url_for('cards'))
|
||||
|
||||
card = get_card(type)
|
||||
if not card:
|
||||
flash("You've learned all the " + card_type + " cards.")
|
||||
return redirect(url_for('cards'))
|
||||
short_answer = (len(card['back']) < 75)
|
||||
return render_template('memorize.html',
|
||||
card=card,
|
||||
card_type=card_type,
|
||||
short_answer=short_answer)
|
||||
|
||||
|
||||
def get_card(type):
|
||||
db = get_db()
|
||||
|
||||
query = '''
|
||||
SELECT
|
||||
id, type, front, back, known
|
||||
FROM cards
|
||||
WHERE
|
||||
type = ?
|
||||
and known = 0
|
||||
ORDER BY RANDOM()
|
||||
LIMIT 1
|
||||
'''
|
||||
|
||||
cur = db.execute(query, [type])
|
||||
return cur.fetchone()
|
||||
|
||||
|
||||
@app.route('/mark_known/<card_id>/<card_type>')
|
||||
def mark_known(card_id, card_type):
|
||||
if not session.get('logged_in'):
|
||||
return redirect(url_for('login'))
|
||||
db = get_db()
|
||||
db.execute('UPDATE cards SET known = 1 WHERE id = ?', [card_id])
|
||||
db.commit()
|
||||
flash('Card marked as known.')
|
||||
return redirect(url_for(card_type))
|
||||
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
|
||||
1
static/fastclick.min.js
vendored
Normal file
1
static/fastclick.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
57
static/general.js
Normal file
57
static/general.js
Normal file
@@ -0,0 +1,57 @@
|
||||
$(document).ready(function(){
|
||||
if ($('.memorizePanel').length != 0) {
|
||||
|
||||
$('.flipCard').click(function(){
|
||||
$('.cardFront').hide();
|
||||
$('.cardBack').show();
|
||||
});
|
||||
}
|
||||
|
||||
if ($('.cardForm').length != 0) {
|
||||
|
||||
$('.cardForm').submit(function(){
|
||||
|
||||
var frontTrim = $.trim($('#front').val());
|
||||
$('#front').val(frontTrim);
|
||||
var backTrim = $.trim($('#back').val());
|
||||
$('#back').val(backTrim);
|
||||
|
||||
if (! $('#front').val() || ! $('#back').val()) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ($('.editPanel').length != 0) {
|
||||
|
||||
function checkit() {
|
||||
var checkedVal = $('input[name=type]:checked').val();
|
||||
if (checkedVal === undefined) {
|
||||
// hide the fields
|
||||
$('.fieldFront').hide();
|
||||
$('.fieldBack').hide();
|
||||
$('.saveButton').hide();
|
||||
} else {
|
||||
$('.toggleButton').removeClass('toggleSelected');
|
||||
$(this).addClass('toggleSelected');
|
||||
|
||||
if (checkedVal == '1') {
|
||||
$('textarea[name=back]').attr('rows', 5);
|
||||
} else {
|
||||
$('textarea[name=back]').attr('rows', 12);
|
||||
}
|
||||
|
||||
$('.fieldFront').show();
|
||||
$('.fieldBack').show();
|
||||
$('.saveButton').show();
|
||||
}
|
||||
}
|
||||
|
||||
$('.toggleButton').click(checkit);
|
||||
|
||||
checkit();
|
||||
}
|
||||
|
||||
// to remove the short delay on click on touch devices
|
||||
FastClick.attach(document.body);
|
||||
});
|
||||
@@ -5,4 +5,46 @@
|
||||
|
||||
textarea {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.cardContent h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.alignContainer {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.alignMiddle {
|
||||
height: 20em;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.cardBack {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.largerText {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
|
||||
/* disables collapsing header menu */
|
||||
|
||||
.navbar-collapse.collapse {
|
||||
display: block!important;
|
||||
}
|
||||
|
||||
.navbar-nav>li, .navbar-nav {
|
||||
float: left !important;
|
||||
}
|
||||
|
||||
.navbar-nav.navbar-right:last-child {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.navbar-right {
|
||||
float: right!important;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
|
||||
<div class="well">
|
||||
<div class="well editPanel">
|
||||
<h2>Add a Card</h2>
|
||||
<form action="{{ url_for('add_card') }}" method=post>
|
||||
<form action="{{ url_for('add_card') }}" method="post" class="cardForm">
|
||||
<div class="form-group">
|
||||
<label for="general" class="toggleButton btn btn-default btn-lg">General
|
||||
<input type="radio" name="type" value="1" id="general"/>
|
||||
@@ -14,7 +14,7 @@
|
||||
</div>
|
||||
<div class="form-group fieldFront">
|
||||
<label for="front">Front of Card</label>
|
||||
<input type="text" name="front" class="form-control">
|
||||
<input type="text" name="front" id="front" class="form-control">
|
||||
</div>
|
||||
<div class="form-group fieldBack">
|
||||
<label for="back">Back of Card</label>
|
||||
@@ -31,57 +31,46 @@
|
||||
</div>
|
||||
|
||||
<div class="page-header">
|
||||
<h2>{{ cards|length }} Card{{ '' if (cards|length == 1) else 's' }}</h2>
|
||||
<h2>{{ cards|length }} Card{{ '' if (cards|length == 1) else 's' }}</h2>
|
||||
</div>
|
||||
<div class="btn-group btn-group-md" role="group" aria-label="filters">
|
||||
<a href="{{ url_for('filter_cards', filter_name="all") }}" class="btn btn-{{ "primary" if filter_name == "all" else "default" }}">All</a>
|
||||
<a href="{{ url_for('filter_cards', filter_name="general") }}" class="btn btn-{{ "primary" if filter_name == "general" else "default" }}">General</a>
|
||||
<a href="{{ url_for('filter_cards', filter_name="code") }}" class="btn btn-{{ "primary" if filter_name == "code" else "default" }}">Code</a>
|
||||
<a href="{{ url_for('filter_cards', filter_name="known") }}" class="btn btn-{{ "primary" if filter_name == "known" else "default" }}">Known</a>
|
||||
<a href="{{ url_for('filter_cards', filter_name="unknown") }}" class="btn btn-{{ "primary" if filter_name == "unknown" else "default" }}">Unknown</a>
|
||||
</div>
|
||||
|
||||
{% for card in cards %}
|
||||
<div>
|
||||
<h3>
|
||||
<a href="{{ url_for('edit', card_id=card.id) }}" class="btn btn-xs btn-primary"><i class="fa fa-pencil" aria-hidden="true"></i></a>
|
||||
{{ card.front }}
|
||||
</h3>
|
||||
{% if card.type == 1 %}
|
||||
{{ card.back|replace("\n", "<br />")|safe }}
|
||||
{% else %}
|
||||
<pre>{{ card.back|safe }}</pre>
|
||||
{% endif %}
|
||||
<hr>
|
||||
</div>
|
||||
{% else %}
|
||||
<li><em>Unbelievable. No cards here so far.</em>
|
||||
{% endfor %}
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<script type=text/javascript>
|
||||
<table class="table table-bordered">
|
||||
{% for card in cards %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ url_for('edit', card_id=card.id) }}" class="btn btn-xs btn-primary"><i class="fa fa-pencil" aria-hidden="true"></i></a>
|
||||
</td>
|
||||
<td class="cardContent">
|
||||
<h4>
|
||||
{{ card.front }}
|
||||
</h4>
|
||||
{% if card.type == 1 %}
|
||||
{{ card.back|replace("\n", "<br />")|safe }}
|
||||
{% else %}
|
||||
<pre>{{ card.back|safe }}</pre>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td>
|
||||
<em>No cards to show.</em>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
$(document).ready(function () {
|
||||
function checkit() {
|
||||
var checkedVal = $('input[name=type]:checked').val();
|
||||
if (checkedVal === undefined) {
|
||||
// hide the fields
|
||||
$('.fieldFront').hide();
|
||||
$('.fieldBack').hide();
|
||||
$('.saveButton').hide();
|
||||
} else {
|
||||
$('.toggleButton').removeClass('toggleSelected');
|
||||
$(this).addClass('toggleSelected');
|
||||
|
||||
if (checkedVal == '1') {
|
||||
$('textarea[name=back]').attr('rows', 5);
|
||||
} else {
|
||||
$('textarea[name=back]').attr('rows', 12);
|
||||
}
|
||||
|
||||
$('.fieldFront').show();
|
||||
$('.fieldBack').show();
|
||||
$('.saveButton').show();
|
||||
}
|
||||
}
|
||||
|
||||
$('.toggleButton').click(checkit);
|
||||
|
||||
checkit();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
code
|
||||
{% endblock %}
|
||||
@@ -2,7 +2,7 @@
|
||||
{% block body %}
|
||||
<div class="well">
|
||||
<h2>Edit Card #{{ card.id }}</h2>
|
||||
<form action="{{ url_for('edit_card') }}" method=post>
|
||||
<form action="{{ url_for('edit_card') }}" method="post" class="cardForm">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="general" class="btn btn-default btn-lg">General
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="front">Front of Card</label>
|
||||
<input type="text" name="front" class="form-control" value="{{ card.front|e }}">
|
||||
<input type="text" name="front" id="front" class="form-control" value="{{ card.front|e }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="back">Back of Card</label>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
general
|
||||
{% endblock %}
|
||||
@@ -1,4 +0,0 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -2,48 +2,34 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>CS Flash Cards</title>
|
||||
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
|
||||
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}" />
|
||||
<!-- I know this doesn't belong in head, but I have script in a template that needs to have jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
|
||||
<script src="https://use.fontawesome.com/8cea844162.js"></script>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
|
||||
integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
|
||||
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<br />
|
||||
<br/>
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container-fluid">
|
||||
<!-- toggle menu -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">CS Flash Cards</a>
|
||||
<a class="navbar-brand" href="{{ url_for('index') }}">CS Flash Cards</a>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{% if not session.logged_in %}
|
||||
<li><a href="{{ url_for('login') }}">log in</a></li>
|
||||
{% else %}
|
||||
<li><a href="{{ url_for('cards') }}">cards</a></li>
|
||||
<li><a href="{{ url_for('general') }}">general</a></li>
|
||||
<li><a href="{{ url_for('code') }}">code</a></li>
|
||||
<li> </li>
|
||||
<li><a href="{{ url_for('logout') }}">log out</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- full menu -->
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{% if not session.logged_in %}
|
||||
<li><a href="{{ url_for('login') }}">log in</a></li>
|
||||
{% else %}
|
||||
<li><a href="{{ url_for('cards') }}">cards</a></li>
|
||||
<li><a href="{{ url_for('general') }}">general</a></li>
|
||||
<li><a href="{{ url_for('code') }}">code</a></li>
|
||||
<li><a href="{{ url_for('logout') }}">log out</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div><!-- /.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
{% for message in get_flashed_messages() %}
|
||||
<div class="alert alert-success" role="alert">{{ message }}</div>
|
||||
{% endfor %}
|
||||
@@ -51,6 +37,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-2.2.4.min.js"
|
||||
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
|
||||
<script src="https://use.fontawesome.com/8cea844162.js"></script>
|
||||
<script src="{{ url_for('static', filename='fastclick.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='general.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
{% block body %}
|
||||
<h2>Login</h2>
|
||||
{% if error %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>{% endif %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="well">
|
||||
<form action="{{ url_for('login') }}" method=post>
|
||||
|
||||
91
templates/memorize.html
Normal file
91
templates/memorize.html
Normal file
@@ -0,0 +1,91 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-center">
|
||||
<div class="btn-group btn-group-lg" role="group" aria-label="card type">
|
||||
<a href="{{ url_for('general') }}" class="btn btn-{{ "primary" if card_type == "general" else "default" }}">General</a>
|
||||
<a href="{{ url_for('code') }}" class="btn btn-{{ "primary" if card_type == "code" else "default" }}">Code</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div class="row memorizePanel">
|
||||
<!--
|
||||
<div class="col-xs-2">
|
||||
<div class="alignContainer">
|
||||
<div class="alignMiddle text-right">
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="wewetw"><i class="fa fa-chevron-left fa-5x"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<div class="col-xs-8 col-xs-offset-2">
|
||||
<div class="panel panel-default cardFront">
|
||||
<div class="panel-body">
|
||||
<div class="alignContainer">
|
||||
<div class="alignMiddle frontText">
|
||||
<h3 class="text-center">{{ card.front }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-primary cardBack">
|
||||
<div class="panel-body">
|
||||
<div class="alignContainer">
|
||||
<div class="alignMiddle frontText">
|
||||
{% if card.type == 1 %}
|
||||
{% if short_answer %}
|
||||
<div class="text-center largerText">
|
||||
{% endif %}
|
||||
{{ card.back|replace("\n", "<br />")|safe }}
|
||||
{% if short_answer %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<pre>{{ card.back|safe }}</pre>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
<div class="col-xs-2">
|
||||
<div class="alignContainer">
|
||||
<div class="alignMiddle text-left">
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="ergergerge"><i class="fa fa-chevron-right fa-5x"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-center">
|
||||
<a href="javascript:" class="btn btn-primary btn-lg flipCard">
|
||||
<i class="fa fa-exchange"></i>
|
||||
Flip Card
|
||||
</a>
|
||||
|
||||
|
||||
<a href="{{ url_for('mark_known', card_id=card.id, card_type=card_type) }}" class="btn btn-success btn-lg">
|
||||
<i class="fa fa-check"></i>
|
||||
I Know It
|
||||
</a>
|
||||
|
||||
|
||||
<a href="{{ url_for(card_type) }}" class="btn btn-primary btn-lg">
|
||||
Next Card
|
||||
<i class="fa fa-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user