feat: Add initial tribes
All checks were successful
Build Docker / Build Main Image (push) Successful in 19s
Build Docker / Build SLDs Image (push) Successful in 20s

This commit is contained in:
Nathan Woodburn 2023-11-22 13:00:04 +11:00
parent 5d70dc862d
commit cd43dd3dc6
Signed by: nathanwoodburn
GPG Key ID: 203B000478AD0EF1
9 changed files with 762 additions and 11 deletions

136
db.py
View File

@ -14,9 +14,6 @@ dbargs = {
'database':os.getenv('DB_NAME') 'database':os.getenv('DB_NAME')
} }
def check_tables(): def check_tables():
connection = mysql.connector.connect(**dbargs) connection = mysql.connector.connect(**dbargs)
cursor = connection.cursor() cursor = connection.cursor()
@ -38,6 +35,15 @@ def check_tables():
PRIMARY KEY (id) PRIMARY KEY (id)
) )
""") """)
cursor.execute("""
CREATE TABLE IF NOT EXISTS tribes (
id INT(11) NOT NULL AUTO_INCREMENT,
tribe VARCHAR(255) NOT NULL,
data JSON,
owner VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
)
""")
cursor.close() cursor.close()
connection.close() connection.close()
print("Checked tables") print("Checked tables")
@ -221,3 +227,127 @@ def get_random_sites():
names.append(site[1]) names.append(site[1])
return names return names
def get_tribes():
connection = mysql.connector.connect(**dbargs)
cursor = connection.cursor()
cursor.execute("""
SELECT * FROM tribes
""")
data = cursor.fetchall()
cursor.close()
connection.close()
tribes = []
for tribe in data:
tribes.append(tribe[1])
return tribes
def get_user_tribes(user):
connection = mysql.connector.connect(**dbargs)
cursor = connection.cursor()
cursor.execute("""
SELECT * FROM tribes WHERE owner = %s
""", (user,))
data = cursor.fetchall()
cursor.close()
connection.close()
# Also check members
connection = mysql.connector.connect(**dbargs)
cursor = connection.cursor()
cursor.execute("""
SELECT * FROM tribes
""")
data2 = cursor.fetchall()
cursor.close()
connection.close()
for tribe in data2:
tribe = json.loads(tribe[2])
if user in tribe['members']:
data.append(tribe)
return len(data)
def get_user_owned_tribe(user):
connection = mysql.connector.connect(**dbargs)
cursor = connection.cursor()
cursor.execute("""
SELECT * FROM tribes WHERE owner = %s
""", (user,))
data = cursor.fetchall()
cursor.close()
connection.close()
return data
def create_tribe(tribe,owner):
# Get users' tribes
if (get_user_tribes(owner) > 0):
return False
connection = mysql.connector.connect(**dbargs)
cursor = connection.cursor()
data = {
"data": "",
"members": [
owner
]
}
insert_query = "INSERT INTO tribes (data,tribe,owner) VALUES (%s,%s,%s)"
cursor.execute(insert_query, (json.dumps(data), tribe, owner))
connection.commit()
cursor.close()
connection.close()
return True
def get_tribe_data_raw(tribe):
connection = mysql.connector.connect(**dbargs)
cursor = connection.cursor()
cursor.execute("""
SELECT * FROM tribes WHERE tribe = %s
""", (tribe,))
data = cursor.fetchall()
cursor.close()
connection.close()
if len(data) == 0:
return False
return json.loads(data[0][2])
def check_tribe_owner(tribe,owner):
connection = mysql.connector.connect(**dbargs)
cursor = connection.cursor()
cursor.execute("""
SELECT * FROM tribes WHERE tribe = %s
""", (tribe,))
data = cursor.fetchall()
cursor.close()
connection.close()
if len(data) == 0:
return False
if data[0][3] == owner:
return True
else:
return False
def update_tribe_data_raw(tribe,data):
connection = mysql.connector.connect(**dbargs)
cursor = connection.cursor()
# Update json object
data = json.loads(data)
update_query = "UPDATE tribes SET data = %s WHERE tribe = %s"
cursor.execute(update_query, (json.dumps(data), tribe))
connection.commit()
cursor.close()
connection.close()

256
main.py
View File

@ -65,7 +65,10 @@ def index():
for site in random_sites_names: for site in random_sites_names:
random_sites += "<a href='https://" + site + "." + CITY_DOMAIN + "' target='_blank'>" + site + "." +CITY_DOMAIN+ "</a><br>" random_sites += "<a href='https://" + site + "." + CITY_DOMAIN + "' target='_blank'>" + site + "." +CITY_DOMAIN+ "</a><br>"
tribes = db.get_tribes()
tribesHTML = ""
for tribe in tribes:
tribesHTML += "<a href='/tribe/" + tribe + "'>" + tribe + "</a><br>"
if 'token' in request.cookies: if 'token' in request.cookies:
@ -77,8 +80,8 @@ def index():
resp = make_response(redirect('/')) resp = make_response(redirect('/'))
resp.set_cookie('token', '', expires=0) resp.set_cookie('token', '', expires=0)
return resp return resp
return render_template('index.html',account=user['email'],account_link="account",account_link_name="Account",CITY_DOMAIN=CITY_DOMAIN,random_sites=random_sites) return render_template('index.html',account=user['email'],account_link="account",account_link_name="Account",CITY_DOMAIN=CITY_DOMAIN,random_sites=random_sites,tribes=tribesHTML)
return render_template('index.html',account="Login",account_link="login",account_link_name="Login",CITY_DOMAIN=CITY_DOMAIN,random_sites=random_sites) return render_template('index.html',account="Login",account_link="login",account_link_name="Login",CITY_DOMAIN=CITY_DOMAIN,random_sites=random_sites,tribes=tribesHTML)
@app.route('/signup', methods=['POST']) @app.route('/signup', methods=['POST'])
def signup(): def signup():
@ -329,6 +332,232 @@ def avatar_clear():
return redirect('/edit') return redirect('/edit')
@app.route('/tribe')
def tribe_list():
tribes = db.get_tribes()
tribesHTML = ""
for tribe in tribes:
tribesHTML += "<a href='/tribe/" + tribe + "'>" + tribe + "</a><br>"
# Add create link if user is logged in
if 'token' in request.cookies:
token = request.cookies['token']
# Verify token
user = accounts.validate_token(token)
if user:
tribesHTML += "<br><br><a class='btn btn-primary' role='button' style='width: 100%;' href='/new_tribe'>Create a tribe</a>"
return render_template('tribe.html',account="Account",account_link="account",account_link_name="Account",CITY_DOMAIN=CITY_DOMAIN,tribes=tribesHTML)
return render_template('tribe.html',account="Login",account_link="login",account_link_name="Login",CITY_DOMAIN=CITY_DOMAIN,tribes=tribesHTML)
@app.route('/new_tribe')
def new_tribe():
if 'token' not in request.cookies:
return redirect('/login')
token = request.cookies['token']
if not accounts.validate_token(token):
return error('Sorry we had an issue verifying your account')
# Verify token
user = accounts.validate_token(token)
if not user:
# Remove cookie
resp = make_response(redirect('/login'))
resp.set_cookie('token', '', expires=0)
return resp
return render_template('new_tribe.html',account=user['email'],account_link="account",account_link_name="Account",CITY_DOMAIN=CITY_DOMAIN)
@app.route('/new_tribe', methods=['POST'])
def create_tribe():
token = request.cookies['token']
if not accounts.validate_token(token):
return error('Sorry we had an issue verifying your account')
# Verify token
user = accounts.validate_token(token)
if not user:
# Remove cookie
resp = make_response(redirect('/login'))
resp.set_cookie('token', '', expires=0)
return resp
tribe = request.form['tribe'].strip().lower()
if not re.match("^[a-z0-9]*$", tribe):
return error('Sorry tribe can only contain lowercase letters and numbers')
if len(tribe) < 3:
return error('Sorry tribe must be at least 3 characters long')
if len(tribe) > 255:
return error('Sorry tribe must be less than 255 characters long')
if db.create_tribe(tribe,user['domain']):
return redirect('/tribe/' + tribe)
else:
return error('Sorry you already have a tribe')
@app.route('/tribe/<tribe>')
def tribe(tribe):
tribe = tribe.lower()
if not re.match("^[a-z0-9]*$", tribe):
return error('Sorry we couldn\'t find that tribe')
data = db.get_tribe_data_raw(tribe)
if data == None:
return error('Sorry we couldn\'t find that tribe')
html = ""
if 'data' in data:
html = data['data'].encode('utf-8').decode('unicode-escape')
html = html.replace("\n","<br>")
tribe = tribe.capitalize()
members_html = ""
members = data['members']
for member in members:
members_html += "<a href='https://" + member + "." + CITY_DOMAIN + "' target='_blank'>" + member + "." +CITY_DOMAIN+ "</a><br>"
edit = ""
# Add edit link if user is logged in
if 'token' in request.cookies:
token = request.cookies['token']
# Verify token
user = accounts.validate_token(token)
if user:
if db.check_tribe_owner(tribe,user['domain']):
edit = "<a class='btn btn-primary' role='button' style='width: 100%;' href='/edit_tribe'>Edit tribe</a>"
elif user['domain'] not in members:
edit = "<a class='btn btn-primary' role='button' style='width: 100%;' href='/join_tribe/" + tribe + "'>Join tribe</a>"
return render_template('tribe_view.html',tribe=tribe,data=html,edit=edit,members=members_html)
@app.route('/join_tribe/<tribe>')
def join_tribe(tribe):
tribe = tribe.lower()
if not re.match("^[a-z0-9]*$", tribe):
return error('Sorry we couldn\'t find that tribe')
data = db.get_tribe_data_raw(tribe)
if data == None:
return error('Sorry we couldn\'t find that tribe')
# Verify token
token = request.cookies['token']
if not accounts.validate_token(token):
# Remove cookie
resp = make_response(redirect('/login'))
resp.set_cookie('token', '', expires=0)
return resp
user = accounts.validate_token(token)
if not user:
# Remove cookie
resp = make_response(redirect('/login'))
resp.set_cookie('token', '', expires=0)
return resp
members = data['members']
if user['domain'] in members:
return error('Sorry you are already a member of this tribe')
members.append(user['domain'])
data['members'] = members
# Convert to json
data = json.dumps(data)
db.update_tribe_data_raw(tribe,data)
return redirect('/tribe/' + tribe)
@app.route('/edit_tribe')
def edit_tribe_view():
token = request.cookies['token']
if not accounts.validate_token(token):
return error('Sorry we had an issue verifying your account')
# Verify token
user = accounts.validate_token(token)
if not user:
# Remove cookie
resp = make_response(redirect('/'))
resp.set_cookie('token', '', expires=0)
return resp
tribeData = db.get_user_owned_tribe(user['domain'])
if len(tribeData) == 0:
return error('Sorry you don\'t own a tribe')
tribe = tribeData[0][1]
data = tribeData[0][2]
data = json.loads(data)
html = data['data'].encode('utf-8').decode('unicode-escape')
members = data['members']
members_html = ""
for member in members:
members_html += "<a href='https://" + member + "." + CITY_DOMAIN + "' target='_blank'>" + member + "." +CITY_DOMAIN+ "</a> <a href='/remove_member?member=" + member + "'>(Remove)</a><br>"
return render_template('edit_tribe.html',account=user['email'],account_link="account",account_link_name="Account",CITY_DOMAIN=CITY_DOMAIN,tribe=tribe,data=html,members=members_html)
@app.route('/edit_tribe', methods=['POST'])
def edit_tribe():
token = request.cookies['token']
if not accounts.validate_token(token):
return error('Sorry we had an issue verifying your account')
# Verify token
user = accounts.validate_token(token)
if not user:
# Remove cookie
resp = make_response(redirect('/'))
resp.set_cookie('token', '', expires=0)
return resp
tribeData = db.get_user_owned_tribe(user['domain'])
if len(tribeData) == 0:
return error('Sorry you don\'t own a tribe')
tribe = tribeData[0][1]
data = tribeData[0][2]
data = json.loads(data)
data['data'] = request.form['data'].strip()
# Convert to json
data = json.dumps(data)
db.update_tribe_data_raw(tribe,data)
return redirect('/edit_tribe')
@app.route('/remove_member')
def remove_member():
token = request.cookies['token']
if not accounts.validate_token(token):
return error('Sorry we had an issue verifying your account')
member = request.args.get('member')
# Verify token
user = accounts.validate_token(token)
if not user:
# Remove cookie
resp = make_response(redirect('/'))
resp.set_cookie('token', '', expires=0)
return resp
tribeData = db.get_user_owned_tribe(user['domain'])
if len(tribeData) == 0:
return error('Sorry you don\'t own a tribe')
# Verify member isn't owner
if member == user['domain']:
return error('Sorry you can\'t remove yourself from your own tribe')
tribe = tribeData[0][1]
data = tribeData[0][2]
data = json.loads(data)
members = data['members']
if member in members:
members.remove(member)
data['members'] = members
# Convert to json
data = json.dumps(data)
db.update_tribe_data_raw(tribe,data)
return redirect('/edit_tribe')
@app.route('/<path:path>') @app.route('/<path:path>')
def catch_all(path): def catch_all(path):
account = "Login" account = "Login"
@ -336,6 +565,9 @@ def catch_all(path):
account_link_name = "Login" account_link_name = "Login"
site = "Null" site = "Null"
domain = "" domain = ""
tribe_title = "Join a tribe"
tribe_link = "tribe"
if 'domain' in request.args: if 'domain' in request.args:
domain = request.args.get('domain') domain = request.args.get('domain')
if 'token' in request.cookies: if 'token' in request.cookies:
@ -351,6 +583,12 @@ def catch_all(path):
account_link = "account" account_link = "account"
account_link_name = "Account" account_link_name = "Account"
site = user['domain'] + "." + CITY_DOMAIN site = user['domain'] + "." + CITY_DOMAIN
# Check if user owns tribe
tribeData = db.get_user_owned_tribe(user['domain'])
if len(tribeData) > 0:
tribe_title = "Edit your tribe"
tribe_link = "edit_tribe"
elif path != "signup" and path != "login" and path != "empty_site": elif path != "signup" and path != "login" and path != "empty_site":
return redirect('/') return redirect('/')
@ -360,11 +598,17 @@ def catch_all(path):
# If file exists, load it # If file exists, load it
if os.path.isfile('templates/' + path): if os.path.isfile('templates/' + path):
return render_template(path,account=account,account_link=account_link,account_link_name=account_link_name,site=site,CITY_DOMAIN=CITY_DOMAIN,domain=domain) return render_template(path,account=account,account_link=account_link,
account_link_name=account_link_name,site=site,
CITY_DOMAIN=CITY_DOMAIN,domain=domain,
tribe_title=tribe_title,tribe_link=tribe_link)
# Try with .html # Try with .html
if os.path.isfile('templates/' + path + '.html'): if os.path.isfile('templates/' + path + '.html'):
return render_template(path + '.html',account=account,account_link=account_link,account_link_name=account_link_name,site=site,CITY_DOMAIN=CITY_DOMAIN,domain=domain) return render_template(path + '.html',account=account,account_link=account_link,
account_link_name=account_link_name,site=site,
CITY_DOMAIN=CITY_DOMAIN,domain=domain,
tribe_title=tribe_title,tribe_link=tribe_link)
return redirect('/') # 404 catch all return redirect('/') # 404 catch all
@ -382,4 +626,4 @@ def update_random_sites():
if __name__ == '__main__': if __name__ == '__main__':
db.check_tables() db.check_tables()
app.run(debug=False, port=5000, host='0.0.0.0') app.run(debug=True, port=5000, host='0.0.0.0')

View File

@ -7,6 +7,8 @@ import os
app = Flask(__name__) app = Flask(__name__)
template = "city" template = "city"
data = "<h1>Test site</h1><p>This is a test site</p>" data = "<h1>Test site</h1><p>This is a test site</p>"
# data = "<div style=\"text-align:center;\"><h1>Nathan.Woodburn/</h1><h2>This is fully customizable!</h2><p>You can even add custom css rules like below.<br>Try hovering over this image</p><style>img.background:hover {filter: blur(5px)}</style><div class=\"profile-container\" style=\"height: 300px;margin-bottom: 2em;\"><img class=\"profile background\" src=\"https://nathan.woodburn.au/assets/img/profile.jpg\" style=\"border-radius: 50%;width: 300px;position: absolute;left: 50%;margin-left: -150px;aspect-ratio: 1;padding-top: calc(var(--s)/5);transform: scale(1);transition: .5s;\" alt=\"My Profile\"><img class=\"profile foreground\" src=\"https://nathan.woodburn.au/assets/img/pfront.webp\" alt=\"\" style=\"border-radius: 50%;pointer-events: none;width: 300px;position: absolute;left: 50%;margin-left: -150px;aspect-ratio: 1;padding-top: calc(var(--s)/5);transform: scale(1);transition: .5s;\"></div>"
db_object = { db_object = {
'avatar': "https://woodburn.au/favicon.png", 'avatar': "https://woodburn.au/favicon.png",
'hnschat':"nathan.woodburn", 'hnschat':"nathan.woodburn",

View File

@ -41,7 +41,7 @@
<section style="margin-top: 50px;text-align: center;"> <section style="margin-top: 50px;text-align: center;">
<h1>Welcome to ShakeCities</h1> <h1>Welcome to ShakeCities</h1>
<p>Congratulations your site is ready for use.<br><br></p> <p>Congratulations your site is ready for use.<br><br></p>
<div style="max-width: 10em;margin: auto;"><a class="btn btn-primary" role="button" href="https://{{site}}" style="display: block;margin: 10px;" target="_blank">Visit your site</a><a class="btn btn-primary" role="button" href="/edit" style="display: block;margin: 10px;">Edit your site</a><a class="btn btn-primary" role="button" href="/tribe" style="display: block;margin: 10px;">Join a tribe</a><a class="btn btn-primary" role="button" href="/hnschat" style="display: block;margin: 10px;">Link HNSChat</a><a class="btn btn-primary" role="button" href="/logout" style="display: block;margin: 10px;">Logout</a></div> <div style="max-width: 10em;margin: auto;"><a class="btn btn-primary" role="button" href="https://{{site}}" style="display: block;margin: 10px;" target="_blank">Visit your site</a><a class="btn btn-primary" role="button" href="/edit" style="display: block;margin: 10px;">Edit your site</a><a class="btn btn-primary" role="button" href="/{{tribe_link}}" style="display: block;margin: 10px;">{{tribe_title}}</a><a class="btn btn-primary" role="button" href="/hnschat" style="display: block;margin: 10px;">Link HNSChat</a><a class="btn btn-primary" role="button" href="/logout" style="display: block;margin: 10px;">Logout</a></div>
</section> </section>
<section style="margin: 20px;height: 40px;text-align: center;margin-top: 40px;"> <section style="margin: 20px;height: 40px;text-align: center;margin-top: 40px;">
<footer class="text-center bg-dark"></footer> <footer class="text-center bg-dark"></footer>

102
templates/edit_tribe.html Normal file
View File

@ -0,0 +1,102 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en" style="background: black;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>ShakeCities | Tribes</title>
<meta name="twitter:description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<meta name="description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:image" content="https://shakecities.com/assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp">
<meta property="og:title" content="ShakeCities">
<meta name="twitter:title" content="ShakeCities">
<meta name="twitter:image" content="https://shakecities.com/assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp">
<meta property="og:description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNSW.png" media="(prefers-color-scheme: dark)">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNSW.png" media="(prefers-color-scheme: dark)">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/styles.min.css">
</head>
<body style="width: 95%;margin: auto;background: transparent;color: #ffffff;">
<section>
<section style="height: 15vh;background: url(&quot;assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp&quot;) bottom / cover no-repeat;min-height: 130px;"></section>
<section class="d-print-none d-lg-none d-xl-none d-xxl-none" style="width: 30vw;border-radius: 50%;background: #000000;padding: 20px;height: 30vw;margin: auto;margin-top: -15vw;display: flex;"><a href="/"><img class="img-fluid" src="assets/img/HNSW.png" width="100%"></a></section>
<section class="d-print-none d-lg-none d-xl-none d-xxl-none" style="display: block;width: 100%;text-align: center;margin-top: 10px;">
<div style="display: inline-block;margin: auto;"><a class="btn btn-primary" role="button" style="margin: 10px;" href="/signup">Create your page</a><a class="btn btn-primary" role="button" style="margin: 10px;" href="/{{account_link}}">{{account_link_name}}</a></div>
</section>
<section class="d-none d-print-block d-lg-block d-xl-block d-xxl-block" style="display: block;">
<div style="width: 100%;text-align: right;margin-top: -4em;"><a class="btn btn-primary" role="button" style="margin: 10px;" href="/{{account_link}}">{{account_link_name}}</a></div>
</section>
<section class="d-none d-print-block d-lg-block d-xl-block d-xxl-block" style="width: 15vw;border-radius: 50%;background: #000000;padding: 20px;height: 15vw;margin: auto;display: flex;margin-top: -6vw;"><a href="/"><img class="img-fluid" src="assets/img/HNSW.png" width="100%"></a></section>
<section class="d-none d-print-block d-lg-block d-xl-block d-xxl-block" style="height: 3em;"></section>
</section>
<section style="margin-top: 10px;">
<div class="row" style="margin: 0px;">
<div class="col" style="background: var(--bs-body-color);margin-right: 10px;border-radius: 10px;margin-top: 10px;margin-left: 10px;">
<h1 style="margin: 10px;">Edit {{tribe}} tribe</h1>
<form method="post" style="margin-bottom: 20px;"><input class="form-control" type="text" style="margin-top: 20px;" name="tribe" placeholder="Tribe Name" value="{{tribe}}"><textarea class="form-control" style="margin-top: 20px;" rows="5" placeholder="Content" name="data">{{data}}</textarea>
<p style="margin-top: 20px;">{{members|safe}}</p><input class="btn btn-primary" type="submit" style="margin-top: 20px;" value="Edit">
</form>
</div>
</div>
</section>
<section>
<section class="d-print-none d-md-none d-lg-none d-xl-none d-xxl-none" style="background: var(--bs-body-color);text-align: center;padding-top: 10px;padding-bottom: 10px;margin-bottom: 25px;margin-top: 40px;border-radius: 10px;">
<div style="margin: 20px;">
<h1>Contact</h1>
<div class="list-group list-group-horizontal" data-bs-theme="dark" style="margin: auto;width: 80%;"><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://discord.com">Discord</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="mailto:email@example.com">Email</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://twitter.com">Twitter</a></button></div>
</div>
<div style="margin: 20px;">
<h1>Donate</h1>
<p>hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y</p>
</div>
</section>
<section class="d-none d-print-block d-md-block d-lg-block d-xl-block d-xxl-block" style="background: var(--bs-body-color);text-align: center;padding-top: 10px;padding-bottom: 10px;margin-bottom: 25px;margin-top: 40px;border-radius: 10px;">
<div style="margin: 20px;">
<h1>Contact</h1>
<div class="list-group list-group-horizontal" data-bs-theme="dark" style="margin: auto;width: 50%;"><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://l.woodburn.au/discord">Discord</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="mailto:shakecities@woodburn.au">Email</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://hnschat#channel:woodburn">HNSChat</a></button></div>
</div>
<div style="margin: 20px;">
<h1>Donate</h1>
<p data-bs-toggle="tooltip" data-bss-tooltip="" id="hns_address" title="Copy">hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y</p><script>
function copyToClipboard(element) {
const textToCopy = element.textContent;
const textArea = document.createElement('textarea');
textArea.value = textToCopy;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
}
const hns = document.getElementById('hns_address');
hns.addEventListener('click', () => {
copyToClipboard(hns);
});
</script>
</div>
</section>
</section>
<section style="margin: 20px;height: 40px;text-align: center;margin-top: 40px;">
<footer class="text-center bg-dark"></footer>
<div style="display: inline-block;">
<ul class="list-inline">
<li class="list-inline-item me-4" style="color: rgb(255,255,255);">Copyright © ShakeCities 2023</li>
<li class="list-inline-item me-4"><iframe src='https://uptime.woodburn.au/api/badge/54/uptime?labelPrefix=Main+Server+' style="height:1.3em;margin-bottom:-.3em;width: 13em;"></iframe></li>
<li class="list-inline-item me-4"><iframe src='https://uptime.woodburn.au/api/badge/55/uptime?labelPrefix=Cities+' style="height:1.3em;margin-bottom:-.3em;width:11em;"></iframe></li>
</ul>
</div>
</section>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/script.min.js"></script>
</body>
</html>

View File

@ -57,6 +57,12 @@
<p style="margin: 10px;font-size: 20px;">{{random_sites|safe}}</p> <p style="margin: 10px;font-size: 20px;">{{random_sites|safe}}</p>
</div> </div>
</div> </div>
<div class="row" style="margin: 0px;">
<div class="col" style="background: var(--bs-body-color);margin-right: 10px;border-radius: 10px;margin-top: 10px;margin-left: 10px;">
<h1 style="margin: 10px;">Tribes</h1>
<p style="margin: 10px;">{{tribes|safe}}</p>
</div>
</div>
</section> </section>
<section> <section>
<section class="d-print-none d-md-none d-lg-none d-xl-none d-xxl-none" style="background: var(--bs-body-color);text-align: center;padding-top: 10px;padding-bottom: 10px;margin-bottom: 25px;margin-top: 40px;border-radius: 10px;"> <section class="d-print-none d-md-none d-lg-none d-xl-none d-xxl-none" style="background: var(--bs-body-color);text-align: center;padding-top: 10px;padding-bottom: 10px;margin-bottom: 25px;margin-top: 40px;border-radius: 10px;">

100
templates/new_tribe.html Normal file
View File

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en" style="background: black;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>ShakeCities | Tribes</title>
<meta name="twitter:description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<meta name="description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:image" content="https://shakecities.com/assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp">
<meta property="og:title" content="ShakeCities">
<meta name="twitter:title" content="ShakeCities">
<meta name="twitter:image" content="https://shakecities.com/assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp">
<meta property="og:description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNSW.png" media="(prefers-color-scheme: dark)">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNSW.png" media="(prefers-color-scheme: dark)">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/styles.min.css">
</head>
<body style="width: 95%;margin: auto;background: transparent;color: #ffffff;">
<section>
<section style="height: 15vh;background: url(&quot;assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp&quot;) bottom / cover no-repeat;min-height: 130px;"></section>
<section class="d-print-none d-lg-none d-xl-none d-xxl-none" style="width: 30vw;border-radius: 50%;background: #000000;padding: 20px;height: 30vw;margin: auto;margin-top: -15vw;display: flex;"><a href="/"><img class="img-fluid" src="assets/img/HNSW.png" width="100%"></a></section>
<section class="d-print-none d-lg-none d-xl-none d-xxl-none" style="display: block;width: 100%;text-align: center;margin-top: 10px;">
<div style="display: inline-block;margin: auto;"><a class="btn btn-primary" role="button" style="margin: 10px;" href="/signup">Create your page</a><a class="btn btn-primary" role="button" style="margin: 10px;" href="/{{account_link}}">{{account_link_name}}</a></div>
</section>
<section class="d-none d-print-block d-lg-block d-xl-block d-xxl-block" style="display: block;">
<div style="width: 100%;text-align: right;margin-top: -4em;"><a class="btn btn-primary" role="button" style="margin: 10px;" href="/{{account_link}}">{{account_link_name}}</a></div>
</section>
<section class="d-none d-print-block d-lg-block d-xl-block d-xxl-block" style="width: 15vw;border-radius: 50%;background: #000000;padding: 20px;height: 15vw;margin: auto;display: flex;margin-top: -6vw;"><a href="/"><img class="img-fluid" src="assets/img/HNSW.png" width="100%"></a></section>
<section class="d-none d-print-block d-lg-block d-xl-block d-xxl-block" style="height: 3em;"></section>
</section>
<section style="margin-top: 10px;">
<div class="row" style="margin: 0px;">
<div class="col" style="background: var(--bs-body-color);margin-right: 10px;border-radius: 10px;margin-top: 10px;margin-left: 10px;">
<h1 style="margin: 10px;">Create a tribe</h1>
<form method="post"><input class="form-control" type="text" style="margin-top: 20px;" name="tribe" placeholder="Tribe Name"><input class="btn btn-primary" type="submit" style="margin-top: 20px;" value="Create"></form>
</div>
</div>
</section>
<section>
<section class="d-print-none d-md-none d-lg-none d-xl-none d-xxl-none" style="background: var(--bs-body-color);text-align: center;padding-top: 10px;padding-bottom: 10px;margin-bottom: 25px;margin-top: 40px;border-radius: 10px;">
<div style="margin: 20px;">
<h1>Contact</h1>
<div class="list-group list-group-horizontal" data-bs-theme="dark" style="margin: auto;width: 80%;"><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://discord.com">Discord</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="mailto:email@example.com">Email</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://twitter.com">Twitter</a></button></div>
</div>
<div style="margin: 20px;">
<h1>Donate</h1>
<p>hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y</p>
</div>
</section>
<section class="d-none d-print-block d-md-block d-lg-block d-xl-block d-xxl-block" style="background: var(--bs-body-color);text-align: center;padding-top: 10px;padding-bottom: 10px;margin-bottom: 25px;margin-top: 40px;border-radius: 10px;">
<div style="margin: 20px;">
<h1>Contact</h1>
<div class="list-group list-group-horizontal" data-bs-theme="dark" style="margin: auto;width: 50%;"><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://l.woodburn.au/discord">Discord</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="mailto:shakecities@woodburn.au">Email</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://hnschat#channel:woodburn">HNSChat</a></button></div>
</div>
<div style="margin: 20px;">
<h1>Donate</h1>
<p data-bs-toggle="tooltip" data-bss-tooltip="" id="hns_address" title="Copy">hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y</p><script>
function copyToClipboard(element) {
const textToCopy = element.textContent;
const textArea = document.createElement('textarea');
textArea.value = textToCopy;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
}
const hns = document.getElementById('hns_address');
hns.addEventListener('click', () => {
copyToClipboard(hns);
});
</script>
</div>
</section>
</section>
<section style="margin: 20px;height: 40px;text-align: center;margin-top: 40px;">
<footer class="text-center bg-dark"></footer>
<div style="display: inline-block;">
<ul class="list-inline">
<li class="list-inline-item me-4" style="color: rgb(255,255,255);">Copyright © ShakeCities 2023</li>
<li class="list-inline-item me-4"><iframe src='https://uptime.woodburn.au/api/badge/54/uptime?labelPrefix=Main+Server+' style="height:1.3em;margin-bottom:-.3em;width: 13em;"></iframe></li>
<li class="list-inline-item me-4"><iframe src='https://uptime.woodburn.au/api/badge/55/uptime?labelPrefix=Cities+' style="height:1.3em;margin-bottom:-.3em;width:11em;"></iframe></li>
</ul>
</div>
</section>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/script.min.js"></script>
</body>
</html>

104
templates/tribe.html Normal file
View File

@ -0,0 +1,104 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en" style="background: black;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>ShakeCities | Tribes</title>
<meta name="twitter:description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<meta name="description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:image" content="https://shakecities.com/assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp">
<meta property="og:title" content="ShakeCities">
<meta name="twitter:title" content="ShakeCities">
<meta name="twitter:image" content="https://shakecities.com/assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp">
<meta property="og:description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNSW.png" media="(prefers-color-scheme: dark)">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNSW.png" media="(prefers-color-scheme: dark)">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/styles.min.css">
</head>
<body style="width: 95%;margin: auto;background: transparent;color: #ffffff;">
<section>
<section style="height: 15vh;background: url(&quot;assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp&quot;) bottom / cover no-repeat;min-height: 130px;"></section>
<section class="d-print-none d-lg-none d-xl-none d-xxl-none" style="width: 30vw;border-radius: 50%;background: #000000;padding: 20px;height: 30vw;margin: auto;margin-top: -15vw;display: flex;"><a href="/"><img class="img-fluid" src="assets/img/HNSW.png" width="100%"></a></section>
<section class="d-print-none d-lg-none d-xl-none d-xxl-none" style="display: block;width: 100%;text-align: center;margin-top: 10px;">
<div style="display: inline-block;margin: auto;"><a class="btn btn-primary" role="button" style="margin: 10px;" href="/signup">Create your page</a><a class="btn btn-primary" role="button" style="margin: 10px;" href="/{{account_link}}">{{account_link_name}}</a></div>
</section>
<section class="d-none d-print-block d-lg-block d-xl-block d-xxl-block" style="display: block;">
<div style="width: 100%;text-align: right;margin-top: -4em;"><a class="btn btn-primary" role="button" style="margin: 10px;" href="/{{account_link}}">{{account_link_name}}</a></div>
</section>
<section class="d-none d-print-block d-lg-block d-xl-block d-xxl-block" style="width: 15vw;border-radius: 50%;background: #000000;padding: 20px;height: 15vw;margin: auto;display: flex;margin-top: -6vw;"><a href="/"><img class="img-fluid" src="assets/img/HNSW.png" width="100%"></a></section>
<section class="d-none d-print-block d-lg-block d-xl-block d-xxl-block" style="height: 3em;"></section>
</section>
<section style="margin-top: 10px;">
<div class="row" style="margin: 0px;">
<div class="col" style="background: var(--bs-body-color);margin-right: 10px;border-radius: 10px;margin-top: 10px;margin-left: 10px;">
<h1 style="margin: 10px;">About Tribes</h1>
<p style="margin: 10px;">ShakeCities is a site offering free personalizable&nbsp;websites. This is a perfect way to start your journey into Handshake domains! Step into a new experience where Handshake domains become instant sites, and where everyone can create their own unique decentralized page.<br>Secured with HTTPS powered by DANE (DNS-based Authentication of Named Entities).<br>But that's not all ShakeCities goes beyond, allowing you to seamlessly integrate crypto addresses into your site, providing an easy way to get paid. Simply send crypto to <code>@yourname.{{CITY_DOMAIN}}</code><br>Your ShakeCity domain can be used to chat on <a href="https://hns.chat" target="_blank">HNSChat</a>&nbsp;or to authenticate on any site using Varo auth<br>Join us in building a decentralized, secure, and innovative internet landscape. Embrace the future with ShakeCities where Handshake domains meet simplicity.</p>
</div>
<div class="col" style="background: var(--bs-body-color);margin-left: 10px;border-radius: 10px;margin-top: 10px;margin-right: 10px;">
<h1 style="margin: 10px;">Find a tribe</h1>
<p style="margin: 10px;font-size: 20px;">{{tribes|safe}}</p>
</div>
</div>
</section>
<section>
<section class="d-print-none d-md-none d-lg-none d-xl-none d-xxl-none" style="background: var(--bs-body-color);text-align: center;padding-top: 10px;padding-bottom: 10px;margin-bottom: 25px;margin-top: 40px;border-radius: 10px;">
<div style="margin: 20px;">
<h1>Contact</h1>
<div class="list-group list-group-horizontal" data-bs-theme="dark" style="margin: auto;width: 80%;"><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://discord.com">Discord</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="mailto:email@example.com">Email</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://twitter.com">Twitter</a></button></div>
</div>
<div style="margin: 20px;">
<h1>Donate</h1>
<p>hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y</p>
</div>
</section>
<section class="d-none d-print-block d-md-block d-lg-block d-xl-block d-xxl-block" style="background: var(--bs-body-color);text-align: center;padding-top: 10px;padding-bottom: 10px;margin-bottom: 25px;margin-top: 40px;border-radius: 10px;">
<div style="margin: 20px;">
<h1>Contact</h1>
<div class="list-group list-group-horizontal" data-bs-theme="dark" style="margin: auto;width: 50%;"><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://l.woodburn.au/discord">Discord</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="mailto:shakecities@woodburn.au">Email</a></button><button class="list-group-item list-group-item-action" style="width: 33%;padding: 0px;margin: 10px;border-style: none;"><a class="btn btn-primary" role="button" style="width: 100%;" target="_blank" href="https://hnschat#channel:woodburn">HNSChat</a></button></div>
</div>
<div style="margin: 20px;">
<h1>Donate</h1>
<p data-bs-toggle="tooltip" data-bss-tooltip="" id="hns_address" title="Copy">hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y</p><script>
function copyToClipboard(element) {
const textToCopy = element.textContent;
const textArea = document.createElement('textarea');
textArea.value = textToCopy;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
}
const hns = document.getElementById('hns_address');
hns.addEventListener('click', () => {
copyToClipboard(hns);
});
</script>
</div>
</section>
</section>
<section style="margin: 20px;height: 40px;text-align: center;margin-top: 40px;">
<footer class="text-center bg-dark"></footer>
<div style="display: inline-block;">
<ul class="list-inline">
<li class="list-inline-item me-4" style="color: rgb(255,255,255);">Copyright © ShakeCities 2023</li>
<li class="list-inline-item me-4"><iframe src='https://uptime.woodburn.au/api/badge/54/uptime?labelPrefix=Main+Server+' style="height:1.3em;margin-bottom:-.3em;width: 13em;"></iframe></li>
<li class="list-inline-item me-4"><iframe src='https://uptime.woodburn.au/api/badge/55/uptime?labelPrefix=Cities+' style="height:1.3em;margin-bottom:-.3em;width:11em;"></iframe></li>
</ul>
</div>
</section>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/script.min.js"></script>
</body>
</html>

63
templates/tribe_view.html Normal file
View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html style="width: 100vw;height: 99vh;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>ShakeCities</title>
<meta name="twitter:description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<meta name="description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:image" content="https://shakecities.com/assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp">
<meta property="og:title" content="ShakeCities">
<meta name="twitter:title" content="ShakeCities">
<meta name="twitter:image" content="https://shakecities.com/assets/img/alexander-slattery-LI748t0BK8w-unsplash.webp">
<meta property="og:description" content="Unlock web ownership's future with ShakeCities! Create your free, secure Handshake domain site. Integrate crypto payments, explore HNSChat, and join us in shaping the decentralized web!">
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNSW.png" media="(prefers-color-scheme: dark)">
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNSW.png" media="(prefers-color-scheme: dark)">
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/assets/css/styles.min.css">
</head>
<body style="height: 99vh;width: 100vw;color: #ffffff;background: #000000;overflow-x: hidden;">
<h1 style="margin: auto;text-align: center;font-size: 5em;">{{tribe}}</h1>
<div style="padding: 2%;margin: 22px;text-align: center;">
<p style="font-size: 24px;">
{{data|safe}} <!-- This needs the safe filter to allow HTML to be rendered -->
</p>
</div>
<div style="padding: 2%;margin: 22px;text-align: center;">
<h2>Tribe members</h2>
<p style="font-size: 24px;">
{{members|safe}} <!-- This needs the safe filter to allow HTML to be rendered -->
</p>
</div>
<!-- Edit footer -->
<div style="height: 100px;"></div>
<div style="position: fixed;bottom: 10px;right:25px;">
<p style="color:#ffffff;">{{edit|safe}}</p>
</div>
</div>
<!-- Footer to display the powered by ShakeCities. -->
<div style="height: 100px;"></div>
<div style="position: fixed;bottom: 10px;left:25px;">
<div style="display: inline-block; background: #ffffff;border-radius: 50%;padding: 10px;">
<img src="/assets/img/HNS.png" width="50px" height="50px">
</div>
<p style="margin-left: 25px; display:inline; color:#ffffff;">Powered by <a style="color: inherit;" href="https://shakecities.com" target="_blank">ShakeCities</a></p>
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/script.min.js"></script>
</body>
</html>