Figured out Templatesflask --debug run People can now change code and upload it without needing a code editor.
This commit is contained in:
Binary file not shown.
199
app/routes.py
199
app/routes.py
@ -1,9 +1,9 @@
|
|||||||
import requests
|
import requests
|
||||||
import itertools
|
import itertools
|
||||||
import pandas as pd
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import csv
|
import csv
|
||||||
|
from functools import wraps
|
||||||
from app import app
|
from app import app
|
||||||
from flask import (
|
from flask import (
|
||||||
redirect,
|
redirect,
|
||||||
@ -39,12 +39,7 @@ def key_response(response):
|
|||||||
error = response.text
|
error = response.text
|
||||||
return render_template("index.html", title="Error Home", error=error)
|
return render_template("index.html", title="Error Home", error=error)
|
||||||
if "401" in str(response):
|
if "401" in str(response):
|
||||||
error = [
|
error = "Unauthorized access error.(401)"
|
||||||
"Unauthorized access error.",
|
|
||||||
"This can mean a lot of things,",
|
|
||||||
"such as the key being changed.",
|
|
||||||
"Remember, they are paired to each educator!",
|
|
||||||
]
|
|
||||||
return render_template("index.html", title="Error Home", error=error)
|
return render_template("index.html", title="Error Home", error=error)
|
||||||
return correct_key(response)
|
return correct_key(response)
|
||||||
|
|
||||||
@ -52,16 +47,20 @@ def key_response(response):
|
|||||||
def correct_key(response):
|
def correct_key(response):
|
||||||
data = response.json()
|
data = response.json()
|
||||||
session["school"] = data["data"]["attributes"]["properties"]["name"]
|
session["school"] = data["data"]["attributes"]["properties"]["name"]
|
||||||
return render_template("bulk_add.html", title="Active Session")
|
return render_template("home.html", title="Active Session")
|
||||||
|
|
||||||
|
|
||||||
def allowed_file(filename):
|
def allowed_file(filename):
|
||||||
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
|
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||||
|
|
||||||
|
|
||||||
# DONE: Remove header for main page.
|
def key_required(check):
|
||||||
# DONE: Leave boxes but change outcome depending if file has been uploaded.
|
@wraps(check)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
if session.get('key') is None:
|
||||||
|
return redirect('/',code=302)
|
||||||
|
return check(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
@app.route("/", methods=["GET", "POST"])
|
@app.route("/", methods=["GET", "POST"])
|
||||||
def ask_key():
|
def ask_key():
|
||||||
@ -70,8 +69,6 @@ def ask_key():
|
|||||||
It also assigns the api key to the session and clears the session upon each reload.
|
It also assigns the api key to the session and clears the session upon each reload.
|
||||||
"""
|
"""
|
||||||
specials = '"!@#$%^&*()-+?_=,<>/"'
|
specials = '"!@#$%^&*()-+?_=,<>/"'
|
||||||
#if session.get("key"):
|
|
||||||
# return render_template("bulk_add.html", title="Options Home")
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
session["key"] = request.form.get("apikey")
|
session["key"] = request.form.get("apikey")
|
||||||
if (any(char in specials for char in session["key"]) or
|
if (any(char in specials for char in session["key"]) or
|
||||||
@ -91,20 +88,17 @@ def ask_key():
|
|||||||
return render_template("index.html", title="Home")
|
return render_template("index.html", title="Home")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/", methods=["GET", "POST"])
|
@app.route("/render_home", methods=["GET", "POST"])
|
||||||
|
@key_required
|
||||||
def render_home():
|
def render_home():
|
||||||
if session.get("key"):
|
if session.get("key"):
|
||||||
return render_template("bulk_add.html", title="Home")
|
return render_template("home.html", title="Home")
|
||||||
return render_template("index.html", title="Enter Key")
|
return render_template("home.html", title="Enter Key")
|
||||||
|
|
||||||
|
|
||||||
#@app.route("/options", methods=["GET", "POST"])
|
|
||||||
#@app.route("/bulk_add", methods=["GET", "POST"])
|
|
||||||
@app.route("/clear_session", methods=["GET", "POST"])
|
@app.route("/clear_session", methods=["GET", "POST"])
|
||||||
def clear_session():
|
def clear_session():
|
||||||
if session.get("key"):
|
if session.get("key"):
|
||||||
print("Session Formula")
|
|
||||||
# [session.pop(key) for key in list(session.keys())]
|
|
||||||
session.clear()
|
session.clear()
|
||||||
error="Session Cleared!"
|
error="Session Cleared!"
|
||||||
return render_template("index.html", error=error, title="Home, New session")
|
return render_template("index.html", error=error, title="Home, New session")
|
||||||
@ -113,9 +107,10 @@ def clear_session():
|
|||||||
|
|
||||||
@app.route("/table")
|
@app.route("/table")
|
||||||
def table():
|
def table():
|
||||||
return render_template("table.html", tables=[session["dfhtml"]], titles=["Table"])
|
return render_template("table.html", tables=[session["table"]], titles=["Table"])
|
||||||
|
|
||||||
@app.route("/upload_file", methods=["GET", "POST"])
|
@app.route("/upload_file", methods=["GET", "POST"])
|
||||||
|
@key_required
|
||||||
def upload_file():
|
def upload_file():
|
||||||
print("Uploading CSV")
|
print("Uploading CSV")
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
@ -126,7 +121,7 @@ def upload_file():
|
|||||||
if file.filename == "":
|
if file.filename == "":
|
||||||
print("no file exists")
|
print("no file exists")
|
||||||
flash("No file found or uploaded")
|
flash("No file found or uploaded")
|
||||||
return redirect(url_for("bulk_add_opts"))
|
return redirect(url_for("home"))
|
||||||
# return redirect(request.url)
|
# return redirect(request.url)
|
||||||
if file and allowed_file(file.filename):
|
if file and allowed_file(file.filename):
|
||||||
filename = secure_filename(file.filename)
|
filename = secure_filename(file.filename)
|
||||||
@ -136,7 +131,7 @@ def upload_file():
|
|||||||
file.save(file_path)
|
file.save(file_path)
|
||||||
file = list(csv.reader(open(file_path, "r")))
|
file = list(csv.reader(open(file_path, "r")))
|
||||||
return divide_values(file)
|
return divide_values(file)
|
||||||
return render_template("bulk_add.html", title="Bulk Add")
|
return render_template("home.html", title="Bulk Add")
|
||||||
|
|
||||||
def divide_values(file):
|
def divide_values(file):
|
||||||
emails = []
|
emails = []
|
||||||
@ -150,10 +145,9 @@ def divide_values(file):
|
|||||||
# FEAT: These two extract the groups and emails into two lists
|
# FEAT: These two extract the groups and emails into two lists
|
||||||
groups = [item for group in groups for item in group]
|
groups = [item for group in groups for item in group]
|
||||||
groups = list(set(groups))
|
groups = list(set(groups))
|
||||||
print(emails)
|
|
||||||
print(groups)
|
|
||||||
return api_csv_parse(emails, groups)
|
return api_csv_parse(emails, groups)
|
||||||
# We're good here. This can now be sent to the api functions with emails and groups.
|
# We're good here. This can now be sent to the api
|
||||||
|
# functions with emails and groups.
|
||||||
elif selection == "some-groups":
|
elif selection == "some-groups":
|
||||||
submissions = []
|
submissions = []
|
||||||
for item in file[1:]:
|
for item in file[1:]:
|
||||||
@ -183,78 +177,13 @@ def api_csv_parse(emails, groups):
|
|||||||
return api_add_groups(groups)
|
return api_add_groups(groups)
|
||||||
return render_template("bulk_add.html", table=htmlcsv, title="CSV Submission")
|
return render_template("bulk_add.html", table=htmlcsv, title="CSV Submission")
|
||||||
|
|
||||||
def api_csv_all_groups(emails, groups):
|
|
||||||
if emails and groups:
|
|
||||||
return api_add_ppl_groups(emails, groups)
|
|
||||||
elif emails:
|
|
||||||
return api_add_ppl(emails)
|
|
||||||
elif groups:
|
|
||||||
return api_add_groups(groups)
|
|
||||||
return render_template("bulk_add.html", table=htmlcsv, title="CSV Submission")
|
|
||||||
|
|
||||||
def api_csv_some_groups(emails, groups):
|
|
||||||
htmlcsv = csvData.to_html()
|
|
||||||
|
|
||||||
emails = csvData['Email'].values.tolist()
|
|
||||||
emails = [nan for nan in emails if str(nan) != 'nan']
|
|
||||||
|
|
||||||
groups = csvData['Groups'].values.tolist()
|
|
||||||
groups = [nan for nan in groups if str(nan) != 'nan']
|
|
||||||
|
|
||||||
print(emails)
|
|
||||||
print(groups)
|
|
||||||
|
|
||||||
# print(email)
|
|
||||||
# return groups
|
|
||||||
# row_list = csvData.loc[2, :].values.flatten().tolist()
|
|
||||||
|
|
||||||
return htmlcsv
|
|
||||||
|
|
||||||
@app.route("/bulk_add_opts", methods=["GET", "POST"])
|
@app.route("/bulk_add_opts", methods=["GET", "POST"])
|
||||||
|
@key_required
|
||||||
def bulk_add_opts():
|
def bulk_add_opts():
|
||||||
return render_template("bulk_add.html", titles="Bulk Add Options")
|
return render_template("bulk_add.html", titles="Bulk Add Options")
|
||||||
|
|
||||||
'''
|
|
||||||
array = []
|
|
||||||
dict_response = {}
|
|
||||||
dataframe = pd.DataFrame()
|
|
||||||
count = 0
|
|
||||||
|
|
||||||
if request.method == "POST":
|
|
||||||
if session.get("file"):
|
|
||||||
pass
|
|
||||||
#print("file exists! uploading data...")
|
|
||||||
#return "File Exists! Test Complete"
|
|
||||||
while True:
|
|
||||||
count += 1
|
|
||||||
endpoint = f"v2/groups?page={count}"
|
|
||||||
headers = {"accept": "application/json", "X-Api-Key": session["key"]}
|
|
||||||
response = requests.get(url + endpoint, headers=headers)
|
|
||||||
data = response.json()
|
|
||||||
nextlink = data["links"]
|
|
||||||
|
|
||||||
for response in data["data"]:
|
|
||||||
uuid = response["id"]
|
|
||||||
dict_response = {"id": uuid}
|
|
||||||
for keys, values in response["attributes"].items():
|
|
||||||
dict_response[keys] = values
|
|
||||||
array.append(dict_response)
|
|
||||||
dataframe = pd.DataFrame(array).drop(
|
|
||||||
"group_enrollment_link", axis=1
|
|
||||||
)
|
|
||||||
print(dataframe)
|
|
||||||
|
|
||||||
if "next" not in nextlink:
|
|
||||||
break
|
|
||||||
|
|
||||||
dfgroups = dataframe.to_html()
|
|
||||||
session["dfcsv"] = dataframe.to_csv()
|
|
||||||
return render_template("bulk_add.html", table=dfgroups, titles="Bulk Add")
|
|
||||||
else:
|
|
||||||
return "This isn't working. Let's go our own way."
|
|
||||||
'''
|
|
||||||
|
|
||||||
@app.route("/bulk_add", methods=["GET", "POST"])
|
@app.route("/bulk_add", methods=["GET", "POST"])
|
||||||
|
@key_required
|
||||||
def bulk_add():
|
def bulk_add():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
emails = request.form.get("emails")
|
emails = request.form.get("emails")
|
||||||
@ -269,10 +198,9 @@ def bulk_add():
|
|||||||
emails = [email.strip() for email in emails]
|
emails = [email.strip() for email in emails]
|
||||||
else:
|
else:
|
||||||
emails = emails.split()
|
emails = emails.split()
|
||||||
else:
|
|
||||||
emails = []
|
|
||||||
emails.append(emails)
|
|
||||||
if groups:
|
if groups:
|
||||||
|
print(groups)
|
||||||
if "\n" in groups:
|
if "\n" in groups:
|
||||||
groups = groups.split("\n")
|
groups = groups.split("\n")
|
||||||
groups = [group.strip() for group in groups]
|
groups = [group.strip() for group in groups]
|
||||||
@ -282,9 +210,6 @@ def bulk_add():
|
|||||||
groups = [group.strip() for group in groups]
|
groups = [group.strip() for group in groups]
|
||||||
else:
|
else:
|
||||||
groups = groups.split()
|
groups = groups.split()
|
||||||
else:
|
|
||||||
groups = []
|
|
||||||
groups.append(groups)
|
|
||||||
|
|
||||||
if emails and groups:
|
if emails and groups:
|
||||||
return api_add_ppl_groups(emails, groups)
|
return api_add_ppl_groups(emails, groups)
|
||||||
@ -295,11 +220,6 @@ def bulk_add():
|
|||||||
return render_template("bulk_add.html")
|
return render_template("bulk_add.html")
|
||||||
|
|
||||||
|
|
||||||
# for group in groups:
|
|
||||||
# groupdict = {}
|
|
||||||
# groupdict["name"] = group
|
|
||||||
|
|
||||||
|
|
||||||
def api_add_ppl(emails):
|
def api_add_ppl(emails):
|
||||||
payload2 = []
|
payload2 = []
|
||||||
endpoint = "v2/bulk/people"
|
endpoint = "v2/bulk/people"
|
||||||
@ -364,28 +284,87 @@ def check_response(response):
|
|||||||
error = "Hm. Looks like something was wrong with the data you added."
|
error = "Hm. Looks like something was wrong with the data you added."
|
||||||
return render_template("bulk_add.html", error=error)
|
return render_template("bulk_add.html", error=error)
|
||||||
else:
|
else:
|
||||||
error = "Shrug"
|
error = "Something went wrong, but I'm not sure what."
|
||||||
return render_template("bulk_add.html", title="Shrug", errors=error)
|
return render_template("bulk_add.html", title="Shrug", error=error)
|
||||||
|
|
||||||
|
@app.route("/load_templates", methods=["GET", "POST"])
|
||||||
|
@key_required
|
||||||
|
def load_templates():
|
||||||
|
count = 0
|
||||||
|
templates= []
|
||||||
|
|
||||||
|
while True:
|
||||||
|
count += 1
|
||||||
|
endpoint = "v2/custom_templates"
|
||||||
|
headers = {
|
||||||
|
"accept": "application/json",
|
||||||
|
"content-type": "application/json",
|
||||||
|
"X-Api-Key": session["key"],
|
||||||
|
}
|
||||||
|
response = requests.get(url+endpoint, headers=headers)
|
||||||
|
data = response.json()
|
||||||
|
nextlink = data["links"]
|
||||||
|
for response in data["data"]:
|
||||||
|
name, body = (response["attributes"]["name"], response["attributes"]["body"])
|
||||||
|
templates.append((name,body))
|
||||||
|
|
||||||
|
if "next" not in nextlink:
|
||||||
|
break
|
||||||
|
|
||||||
|
return render_template("templates.html",
|
||||||
|
title="Templates",
|
||||||
|
templates=templates
|
||||||
|
)
|
||||||
|
|
||||||
|
return render_template("options.html")
|
||||||
|
|
||||||
@app.route("/templates", methods=["GET", "POST"])
|
@app.route("/templates", methods=["GET", "POST"])
|
||||||
|
@key_required
|
||||||
def templates():
|
def templates():
|
||||||
pass
|
if request.method == "POST":
|
||||||
|
name = request.form.get('template_name')
|
||||||
|
body = request.form.get('body')
|
||||||
|
print(name)
|
||||||
|
endpoint = "v2/custom_templates"
|
||||||
|
headers = {
|
||||||
|
"accept": "application/json",
|
||||||
|
"content-type": "application/json",
|
||||||
|
"X-Api-Key": session["key"],
|
||||||
|
}
|
||||||
|
payload = {"custom_template": {
|
||||||
|
"name":name,
|
||||||
|
"body":body
|
||||||
|
}}
|
||||||
|
response = requests.post(url+endpoint, json=payload, headers=headers)
|
||||||
|
return check_templates(response)
|
||||||
|
return render_template("templates.html", error="Uh oh!")
|
||||||
|
|
||||||
|
def check_templates(response):
|
||||||
|
print(response)
|
||||||
|
response = str(response)
|
||||||
|
if "201" in response:
|
||||||
|
error = "Success! Templates Uploaded."
|
||||||
|
return render_template("templates.html", title="People Added", error=error)
|
||||||
|
elif "403" in response:
|
||||||
|
error = "Uh oh. Looks like you don't have appropriate privileges."
|
||||||
|
return render_template("templates.html", error=error)
|
||||||
|
else:
|
||||||
|
error = "Something went wrong, but I'm not sure what."
|
||||||
|
return render_template("templates.html", title="Shrug", error=error)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/bulk_courses_to_groups", methods=["GET", "POST"])
|
@app.route("/bulk_courses_to_groups", methods=["GET", "POST"])
|
||||||
|
@key_required
|
||||||
def bulk_courses_to_groups():
|
def bulk_courses_to_groups():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@app.route("/bulk_invite_ppl", methods=["GET", "POST"])
|
@app.route("/bulk_invite_ppl", methods=["GET", "POST"])
|
||||||
|
@key_required
|
||||||
def bulk_invite_ppl():
|
def bulk_invite_ppl():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# @app.teardown_request
|
|
||||||
# def clear_session():
|
|
||||||
# session.clear()
|
|
||||||
|
|
||||||
app.secret_key = "@&I\x1a?\xce\x94\xbb0w\x17\xbf&Y\xa2\xc2(A\xf5\xf2\x97\xba\xeb\xfa"
|
app.secret_key = "@&I\x1a?\xce\x94\xbb0w\x17\xbf&Y\xa2\xc2(A\xf5\xf2\x97\xba\xeb\xfa"
|
||||||
|
|
||||||
|
|||||||
@ -138,6 +138,12 @@ img {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.instructions-left
|
||||||
|
.instructions-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@ -206,7 +212,7 @@ li {
|
|||||||
}
|
}
|
||||||
ul {
|
ul {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: left;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 1.? - Card Layout in options.html only */
|
/* 1.? - Card Layout in options.html only */
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
{% include 'logo.html' %}
|
{% include 'logo.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>You're currently accessing {{ session.school }}.</h2>
|
<h2>You're currently accessing {{ session.school }}.</h2>
|
||||||
<h2>Instructions</h2>
|
<h2> </h2>
|
||||||
<div class="instructions-list">
|
<div class="instructions-list">
|
||||||
<div class="instructions-left">
|
<div class="instructions-left">
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<header class="header">
|
<header class="header">
|
||||||
|
|
||||||
<form class="navbutton" >
|
<form class="navbutton"
|
||||||
|
id="render_home"
|
||||||
|
action="{{ url_for('render_home')}}"
|
||||||
|
method="post">
|
||||||
<a class="navoption"
|
<a class="navoption"
|
||||||
href="{{ url_for('render_home')}}"
|
onclick="document.forms['render_home'].submit()"
|
||||||
style="cursor:pointer;">
|
style="cursor:pointer;">
|
||||||
<i class="ri-home-line card__icon"></i>
|
<i class="ri-home-line card__icon"></i>
|
||||||
<p class="navselection">Home</p>
|
<p class="navselection">Home</p>
|
||||||
@ -23,23 +26,11 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form class="navbutton"
|
<form class="navbutton"
|
||||||
id="bulk_courses_to_groups"
|
id="load_templates"
|
||||||
action="{{ url_for('bulk_courses_to_groups')}}"
|
action="{{ url_for('load_templates')}}"
|
||||||
method="post">
|
method="post">
|
||||||
<a class="navoption"
|
<a class="navoption"
|
||||||
onclick="document.forms['bulk_add_course_groups'].submit()"
|
onclick="document.forms['load_templates'].submit()"
|
||||||
style="cursor:pointer;">
|
|
||||||
<i class="ri-car-line card__icon"></i>
|
|
||||||
<p class="navselection">Add Courses</p>
|
|
||||||
</a>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<form class="navbutton"
|
|
||||||
id="templates"
|
|
||||||
action="{{ url_for('templates')}}"
|
|
||||||
method="post">
|
|
||||||
<a class="navoption"
|
|
||||||
onclick="document.forms['templates'].submit()"
|
|
||||||
style="cursor:pointer;">
|
style="cursor:pointer;">
|
||||||
<i class="ri-car-line card__icon"></i>
|
<i class="ri-car-line card__icon"></i>
|
||||||
<p class="navselection">Templates</p>
|
<p class="navselection">Templates</p>
|
||||||
|
|||||||
8
app/templates/home.html
Normal file
8
app/templates/home.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
{% include 'head.html' %}
|
||||||
|
{% include 'header.html' %}
|
||||||
|
{% include 'logo.html' %}
|
||||||
|
{% block content %}
|
||||||
|
<h2>Academy: {{ session.school }}.</h2>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
47
app/templates/templates.html
Normal file
47
app/templates/templates.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
{% include 'head.html' %}
|
||||||
|
{% include 'header.html' %}
|
||||||
|
{% include 'logo.html' %}
|
||||||
|
<p> </p>
|
||||||
|
{% if error %}
|
||||||
|
<h3>{{ error }}</h4>
|
||||||
|
{% endif %}
|
||||||
|
{% if templates %}
|
||||||
|
<h2> Here are the liquid templates </h2>
|
||||||
|
{% endif %}
|
||||||
|
<div class="templates_display" >
|
||||||
|
{% for templates in templates %}
|
||||||
|
<p> </p>
|
||||||
|
<div class="html_code">
|
||||||
|
<form
|
||||||
|
id="templates"
|
||||||
|
action="{{ url_for('templates')}}"
|
||||||
|
method="post">
|
||||||
|
<h2>
|
||||||
|
{{ templates[0] }}
|
||||||
|
</h2>
|
||||||
|
<textarea
|
||||||
|
placeholder={{ templates[0] }}
|
||||||
|
id="body"
|
||||||
|
name="body"
|
||||||
|
rows="35"
|
||||||
|
cols="100">
|
||||||
|
{{ templates[1] }}
|
||||||
|
</textarea>
|
||||||
|
<p> </p>
|
||||||
|
<label for="template_name">
|
||||||
|
Create New Template (optional):
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="template_name"
|
||||||
|
value="{{ templates[0] }}">
|
||||||
|
</input>
|
||||||
|
<p> </p>
|
||||||
|
<input type="submit" name="submit_template" value="Submit Template"</input>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p> </p>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
Reference in New Issue
Block a user