kitchen sink commit

pull/193/head
Aevann 2023-08-12 00:50:23 +03:00
parent e9edd9f89a
commit 39098fe323
54 changed files with 272 additions and 489 deletions

View File

@ -157,7 +157,7 @@ function buy() {
try {data = JSON.parse(xhr[0].response)}
catch(e) {console.error(e)}
success = xhr[0].status >= 200 && xhr[0].status < 300;
showToast(success, getMessageFromJsonData(success, data), true);
showToast(success, getMessageFromJsonData(success, data));
if (success) {
if (kind != "lootbox")
{

View File

@ -93,10 +93,6 @@ async function handleSettingSwitch(event) {
// toggle the input back if the request doesn't go through
input.checked = !input.checked;
}
let oldToast = bootstrap.Toast.getOrCreateInstance(
document.getElementById('toast-post-' + (res.ok ? 'error': 'success'))
); // intentionally reversed here: this is the old toast
oldToast.hide();
showToast(res.ok, message);
input.disabled = false;
input.classList.remove("disabled");

View File

@ -11,15 +11,13 @@ function getMessageFromJsonData(success, json) {
return message;
}
function showToast(success, message, isToastTwo=false) {
function showToast(success, message) {
const oldToast = bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-' + (success ? 'error': 'success'))); // intentionally reversed here: this is the old toast
oldToast.hide();
let element = success ? "toast-post-success" : "toast-post-error";
let textElement = element + "-text";
if (isToastTwo) {
element = element + "2";
textElement = textElement + "2";
}
if (!message) {
message = success ? "Success" : "Error, please try again later";
message = success ? "Action successful!" : "Error, please try again later";
}
document.getElementById(textElement).innerText = message;
bootstrap.Toast.getOrCreateInstance(document.getElementById(element)).show();
@ -43,18 +41,20 @@ function postToast(t, url, data, extraActionsOnSuccess, method="POST") {
}
const xhr = createXhrWithFormKey(url, method, form);
xhr[0].onload = function() {
t.disabled = false;
t.classList.remove("disabled");
const success = xhr[0].status >= 200 && xhr[0].status < 300;
if (!(extraActionsOnSuccess == reload && success)) {
t.disabled = false;
t.classList.remove("disabled");
}
let result
let message;
let success = xhr[0].status >= 200 && xhr[0].status < 300;
if (typeof result == "string") {
message = result;
} else {
message = getMessageFromJsonData(success, JSON.parse(xhr[0].response));
}
let oldToast = bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-' + (success ? 'error': 'success'))); // intentionally reversed here: this is the old toast
oldToast.hide();
showToast(success, message);
if (success && extraActionsOnSuccess) result = extraActionsOnSuccess(xhr[0]);
return success;
@ -63,13 +63,7 @@ function postToast(t, url, data, extraActionsOnSuccess, method="POST") {
}
function postToastReload(t, url, method="POST") {
postToast(t, url,
{
},
() => {
location.reload()
}
, method);
postToast(t, url, {}, reload, method);
}
function postToastSwitch(t, url, button1, button2, cls, extraActionsOnSuccess, method="POST") {
@ -280,10 +274,14 @@ function prepare_to_pause(audio) {
});
}
function reload() {
location.reload();
}
function sendFormXHR(form, extraActionsOnSuccess) {
const submit_btn = form.querySelector('[type="submit"]')
submit_btn.disabled = true;
submit_btn.classList.add("disabled");
const t = form.querySelector('[type="submit"]')
t.disabled = true;
t.classList.add("disabled");
const xhr = new XMLHttpRequest();
@ -296,24 +294,18 @@ function sendFormXHR(form, extraActionsOnSuccess) {
xhr.setRequestHeader('xhr', 'xhr');
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
let data = JSON.parse(xhr.response);
showToast(true, getMessageFromJsonData(true, data));
if (extraActionsOnSuccess) extraActionsOnSuccess(xhr);
} else {
document.getElementById('toast-post-error-text').innerText = "Error, please try again later."
try {
let data=JSON.parse(xhr.response);
bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show();
document.getElementById('toast-post-error-text').innerText = data["error"];
if (data && data["details"]) document.getElementById('toast-post-error-text').innerText = data["details"];
} catch(e) {
bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-success')).hide();
bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show();
}
const success = xhr.status >= 200 && xhr.status < 300;
if (!(extraActionsOnSuccess == reload && success)) {
t.disabled = false;
t.classList.remove("disabled");
}
submit_btn.disabled = false;
submit_btn.classList.remove("disabled");
if (xhr.status != 204) {
const data = JSON.parse(xhr.response);
showToast(success, getMessageFromJsonData(success, data));
}
if (success && extraActionsOnSuccess) extraActionsOnSuccess(xhr);
};
xhr.send(formData);
@ -331,11 +323,7 @@ function sendFormXHRSwitch(form) {
}
function sendFormXHRReload(form) {
sendFormXHR(form,
() => {
location.reload();
}
)
sendFormXHR(form, reload)
}
let sortAscending = {};

View File

@ -178,8 +178,9 @@ function submit(form) {
xhr.onload = function() {
upload_prog.classList.add("d-none")
const success = xhr.status >= 200 && xhr.status < 300
if (xhr.status >= 200 && xhr.status < 300) {
if (success) {
const res = JSON.parse(xhr.response)
const post_id = res['post_id'];
@ -200,16 +201,8 @@ function submit(form) {
location.href = "/post/" + post_id
} else {
submitButton.disabled = false;
document.getElementById('toast-post-error-text').innerText = "Error, please try again later."
try {
let data=JSON.parse(xhr.response);
bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show();
document.getElementById('toast-post-error-text').innerText = data["error"];
if (data && data["details"]) document.getElementById('toast-post-error-text').innerText = data["details"];
} catch(e) {
bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-success')).hide();
bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show();
}
const data = JSON.parse(xhr.response);
showToast(success, getMessageFromJsonData(success, data));
}
};

View File

@ -363,12 +363,6 @@ def get_error():
else:
return None
def get_msg():
if request.referrer and request.referrer.split('?')[0] == request.base_url:
return request.values.get("msg")
else:
return None
def get_page():
try: return max(int(request.values.get("page", 1)), 1)
except: return 1

View File

@ -90,12 +90,12 @@ def edit_rules_post(v):
with open(f'files/templates/rules_{SITE_NAME}.html', 'w+', encoding="utf-8") as f:
f.write(rules)
# ma = ModAction(
# kind="edit_rules",
# user_id=v.id,
# )
# g.db.add(ma)
return render_template('admin/edit_rules.html', v=v, rules=rules, msg='Rules edited successfully!')
ma = ModAction(
kind="edit_rules",
user_id=v.id,
)
g.db.add(ma)
return {"message": "Rules edited successfully!"}
@app.post("/@<username>/make_admin")
@limiter.limit('1/second', scope=rpath)
@ -472,16 +472,12 @@ def badge_grant_post(v):
usernames = request.values.get("usernames", "").strip()
if not usernames:
error = "You must enter usernames!"
if v.client: abort(400, error)
return render_template("admin/badge_admin.html", v=v, badge_types=badges, grant=True, error=error)
abort(400, "You must enter usernames!")
for username in usernames.split():
user = get_user(username, graceful=True)
if not user:
error = "User not found!"
if v.client: abort(400, error)
return render_template("admin/badge_admin.html", v=v, badge_types=badges, grant=True, error=error)
abort(400, "User not found!")
try: badge_id = int(request.values.get("badge_id"))
except: abort(400)
@ -532,10 +528,7 @@ def badge_grant_post(v):
)
g.db.add(ma)
msg = "Badge granted to users successfully!"
if v.client: return {"message": msg}
return render_template("admin/badge_admin.html", v=v, badge_types=badges, grant=True, msg=msg)
return {"message": "Badge granted to users successfully!"}
@app.post("/admin/badge_remove")
@feature_required('BADGES')
@ -549,14 +542,12 @@ def badge_remove_post(v):
usernames = request.values.get("usernames", "").strip()
if not usernames:
error = "You must enter usernames!"
if v.client: abort(400, error)
return render_template("admin/badge_admin.html", v=v, badge_types=badges, grant=False, error=error)
abort(400, "You must enter usernames!")
for username in usernames.split():
user = get_user(username, graceful=True)
if not user:
return render_template("admin/badge_admin.html", v=v, badge_types=badges, grant=False, error="User not found!")
abort(400, "User not found!")
try: badge_id = int(request.values.get("badge_id"))
except: abort(400)
@ -580,10 +571,7 @@ def badge_remove_post(v):
g.db.add(ma)
g.db.delete(badge)
msg = "Badge removed from users successfully!"
if v.client: return {"message": msg}
return render_template("admin/badge_admin.html", v=v, badge_types=badges, grant=False, msg=msg)
return {"message": "Badge removed from users successfully!"}
@app.get("/admin/alt_votes")
@ -1702,8 +1690,7 @@ def admin_distinguish_comment(c_id, v):
def admin_banned_domains(v):
banned_domains = g.db.query(BannedDomain) \
.order_by(BannedDomain.reason).all()
return render_template("admin/banned_domains.html", v=v,
banned_domains=banned_domains)
return render_template("admin/banned_domains.html", v=v, banned_domains=banned_domains)
@app.post("/admin/ban_domain")
@limiter.limit('1/second', scope=rpath)
@ -1736,7 +1723,7 @@ def ban_domain(v):
)
g.db.add(ma)
return redirect("/admin/banned_domains/")
return {"message": "Domain banned successfully!"}
@app.post("/admin/unban_domain/<path:domain>")
@ -1898,10 +1885,10 @@ def delete_media_post(v):
url = request.values.get("url")
if not url:
return render_template("admin/delete_media.html", v=v, url=url, error="No url provided!")
abort(400, "No url provided!")
if not image_link_regex.fullmatch(url) and not video_link_regex.fullmatch(url):
return render_template("admin/delete_media.html", v=v, url=url, error="Invalid url!")
abort(400, "Invalid url!")
path = url.split(SITE)[1]
@ -1909,7 +1896,7 @@ def delete_media_post(v):
path = '/videos' + path
if not os.path.isfile(path):
return render_template("admin/delete_media.html", v=v, url=url, error="File not found on the server!")
abort(400, "File not found on the server!")
os.remove(path)
@ -1921,7 +1908,7 @@ def delete_media_post(v):
g.db.add(ma)
purge_files_in_cache(url)
return render_template("admin/delete_media.html", v=v, msg="Media deleted successfully!")
return {"message": "Media deleted successfully!"}
@app.post("/admin/reset_password/<int:user_id>")
@limiter.limit('1/second', scope=rpath)
@ -1964,10 +1951,10 @@ def start_orgy(v):
create_orgy(link, title)
return redirect("/chat")
return {"message": "Orgy started successfully!"}
@app.post("/admin/stop_orgy")
@admin_level_required(PERMS['ORGIES'])
def stop_orgy(v):
end_orgy()
return redirect("/chat")
return {"message": "Orgy stopped successfully!"}

View File

@ -34,7 +34,7 @@ def submit_emojis(v):
emoji.author = g.db.query(User.username).filter_by(id=emoji.author_id).one()[0]
emoji.submitter = g.db.query(User.username).filter_by(id=emoji.submitter_id).one()[0]
return render_template("submit_emojis.html", v=v, emojis=emojis, msg=get_msg())
return render_template("submit_emojis.html", v=v, emojis=emojis)
@app.post("/submit/emojis")
@ -50,46 +50,37 @@ def submit_emoji(v):
username = request.values.get('author', '').lower().strip()
kind = request.values.get('kind', '').strip()
def error(error):
if v.admin_level >= PERMS['VIEW_PENDING_SUBMITTED_EMOJIS']: emojis = g.db.query(Emoji).filter(Emoji.submitter_id != None)
else: emojis = g.db.query(Emoji).filter(Emoji.submitter_id == v.id)
emojis = emojis.order_by(Emoji.created_utc.desc()).all()
for emoji in emojis:
emoji.author = g.db.query(User.username).filter_by(id=emoji.author_id).one()[0]
emoji.submitter = g.db.query(User.username).filter_by(id=emoji.submitter_id).one()[0]
return render_template("submit_emojis.html", v=v, emojis=emojis, error=error, name=name, kind=kind, tags=tags, username=username), 400
if kind not in EMOJI_KINDS:
return error("Invalid emoji kind!")
abort(400, "Invalid emoji kind!")
if kind in {"Platy", "Wolf", "Tay", "Carp", "Capy"} and not name.startswith(kind.lower()):
return error(f'The name of this emoji should start with the word "{kind.lower()}"')
abort(400, f'The name of this emoji should start with the word "{kind.lower()}"')
if kind == "Marsey" and not name.startswith("marsey") and not name.startswith("marcus"):
return error('The name of this emoji should start with the word "Marsey" or "Marcus"')
abort(400, 'The name of this emoji should start with the word "Marsey" or "Marcus"')
if kind == "Marsey Flags" and not name.startswith("marseyflag"):
return error('The name of this emoji should start with the word "marseyflag"')
abort(400, 'The name of this emoji should start with the word "marseyflag"')
if g.is_tor:
return error("Image uploads are not allowed through TOR!")
abort(400, "Image uploads are not allowed through TOR!")
if not file or not file.content_type.startswith('image/'):
return error("You need to submit an image!")
abort(400, "You need to submit an image!")
if not emoji_name_regex.fullmatch(name):
return error("Invalid name!")
abort(400, "Invalid name!")
existing = g.db.query(Emoji.name).filter_by(name=name).one_or_none()
if existing:
return error("Someone already submitted an emoji with this name!")
abort(400, "Someone already submitted an emoji with this name!")
if not tags_regex.fullmatch(tags):
return error("Invalid tags!")
abort(400, "Invalid tags!")
author = get_user(username, v=v, graceful=True)
if not author:
return error(f"A user with the name '{username}' was not found!")
abort(400, f"A user with the name '{username}' was not found!")
highquality = f'/asset_submissions/emojis/{name}'
file.save(highquality)
@ -101,7 +92,8 @@ def submit_emoji(v):
emoji = Emoji(name=name, kind=kind, author_id=author.id, tags=tags, count=0, submitter_id=v.id)
g.db.add(emoji)
return redirect(f"/submit/emojis?msg='{name}' submitted successfully!")
return {"message": f"'{name}' submitted successfully!"}
def verify_permissions_and_get_asset(cls, asset_type, v, name, make_lower=False):
if cls not in ASSET_TYPES: raise Exception("not a valid asset type")
@ -272,7 +264,7 @@ def submit_hats(v):
else: hats = g.db.query(HatDef).filter(HatDef.submitter_id == v.id)
hats = hats.order_by(HatDef.created_utc.desc()).all()
return render_template("submit_hats.html", v=v, hats=hats, msg=get_msg())
return render_template("submit_hats.html", v=v, hats=hats)
@app.post("/submit/hats")
@ -286,32 +278,26 @@ def submit_hat(v):
description = request.values.get('description', '').strip()
username = request.values.get('author', '').strip()
def error(error):
if v.admin_level >= PERMS['VIEW_PENDING_SUBMITTED_HATS']: hats = g.db.query(HatDef).filter(HatDef.submitter_id != None)
else: hats = g.db.query(HatDef).filter(HatDef.submitter_id == v.id)
hats = hats.order_by(HatDef.created_utc.desc()).all()
return render_template("submit_hats.html", v=v, hats=hats, error=error, name=name, description=description, username=username), 400
if g.is_tor:
return error("Image uploads are not allowed through TOR!")
abort(400, "Image uploads are not allowed through TOR!")
file = request.files["image"]
if not file or not file.content_type.startswith('image/'):
return error("You need to submit an image!")
abort(400, "You need to submit an image!")
if not hat_regex.fullmatch(name):
return error("Invalid name!")
abort(400, "Invalid name!")
existing = g.db.query(HatDef.name).filter_by(name=name).one_or_none()
if existing:
return error("A hat with this name already exists!")
abort(400, "A hat with this name already exists!")
if not description_regex.fullmatch(description):
return error("Invalid description!")
abort(400, "Invalid description!")
author = get_user(username, v=v, graceful=True)
if not author:
return error(f"A user with the name '{username}' was not found!")
abort(400, f"A user with the name '{username}' was not found!")
highquality = f'/asset_submissions/hats/{name}'
file.save(highquality)
@ -319,7 +305,7 @@ def submit_hat(v):
with Image.open(highquality) as i:
if i.width > 100 or i.height > 130:
os.remove(highquality)
return error("Images must be 100x130")
abort(400, "Images must be 100x130")
if len(list(Iterator(i))) > 1: price = 1000
else: price = 500
@ -331,7 +317,7 @@ def submit_hat(v):
hat = HatDef(name=name, author_id=author.id, description=description, price=price, submitter_id=v.id)
g.db.add(hat)
return redirect(f"/submit/hats?msg='{name}' submitted successfully!")
return {"message": f"'{name}' submitted successfully!"}
@app.post("/admin/approve/hat/<name>")
@ -434,20 +420,17 @@ def update_emoji(v):
tags = request.values.get('tags', '').lower().strip()
kind = request.values.get('kind', '').strip()
def error(error):
return render_template("admin/update_assets.html", v=v, error=error, name=name, tags=tags, kind=kind, type="Emoji")
existing = g.db.get(Emoji, name)
if not existing:
return error("An emoji with this name doesn't exist!")
abort(400, "An emoji with this name doesn't exist!")
updated = False
if file:
if g.is_tor:
return error("Image uploads are not allowed through TOR!")
abort(400, "Image uploads are not allowed through TOR!")
if not file.content_type.startswith('image/'):
return error("You need to submit an image!")
abort(400, "You need to submit an image!")
for x in IMAGE_FORMATS:
if path.isfile(f'/asset_submissions/emojis/original/{name}.{x}'):
@ -480,7 +463,7 @@ def update_emoji(v):
updated = True
if not updated:
return error("You need to actually update something!")
abort(400, "You need to actually update something!")
g.db.add(existing)
@ -494,7 +477,7 @@ def update_emoji(v):
cache.delete("emojis")
cache.delete(f"emoji_list_{existing.kind}")
return render_template("admin/update_assets.html", v=v, msg=f"'{name}' updated successfully!", name=name, tags=tags, kind=kind, type="Emoji")
return {"message": f"'{name}' updated successfully!"}
@app.get("/admin/update/hats")
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@ -514,21 +497,18 @@ def update_hat(v):
file = request.files["image"]
name = request.values.get('name', '').strip()
def error(error):
return render_template("admin/update_assets.html", v=v, error=error, type="Hat")
if g.is_tor:
return error("Image uploads are not allowed through TOR!")
abort(400, "Image uploads are not allowed through TOR!")
if not file or not file.content_type.startswith('image/'):
return error("You need to submit an image!")
abort(400, "You need to submit an image!")
if not hat_regex.fullmatch(name):
return error("Invalid name!")
abort(400, "Invalid name!")
existing = g.db.query(HatDef.name).filter_by(name=name).one_or_none()
if not existing:
return error("A hat with this name doesn't exist!")
abort(400, "A hat with this name doesn't exist!")
highquality = f"/asset_submissions/hats/{name}"
file.save(highquality)
@ -536,7 +516,7 @@ def update_hat(v):
with Image.open(highquality) as i:
if i.width > 100 or i.height > 130:
os.remove(highquality)
return error("Images must be 100x130")
abort(400, "Images must be 100x130")
format = i.format.lower()
new_path = f'/asset_submissions/hats/original/{name}.{format}'
@ -557,4 +537,4 @@ def update_hat(v):
_note=f'<a href="{SITE_FULL_IMAGES}/i/hats/{name}.webp">{name}</a>'
)
g.db.add(ma)
return render_template("admin/update_assets.html", v=v, msg=f"'{name}' updated successfully!", type="Hat")
return {"message": f"'{name}' updated successfully!"}

View File

@ -67,5 +67,4 @@ def error_500(e):
def allow_nsfw():
session["over_18_cookies"] = int(time.time()) + 3600
redir = request.values.get("redir", "/")
if is_site_url(redir): return redirect(redir)
return redirect('/')
return '', 204

View File

@ -13,7 +13,7 @@ from files.__main__ import app, limiter
@auth_required
def ping_groups(v):
groups = g.db.query(Group).order_by(Group.created_utc).all()
return render_template('groups.html', v=v, groups=groups, cost=GROUP_COST, msg=get_msg(), error=get_error())
return render_template('groups.html', v=v, groups=groups, cost=GROUP_COST)
@app.post("/create_group")
@limiter.limit('1/second', scope=rpath)
@ -27,16 +27,16 @@ def create_group(v):
name = name.strip().lower()
if name.startswith('slots') or name.startswith('remindme'):
return redirect(f"/ping_groups?error=You can't make a group with that name!")
abort(400, "You can't make a group with that name!")
if not valid_sub_regex.fullmatch(name):
return redirect(f"/ping_groups?error=Name does not match the required format!")
abort(400, "Name does not match the required format!")
if name in {'everyone', 'jannies', 'followers'} or g.db.get(Group, name):
return redirect(f"/ping_groups?error=This group already exists!")
abort(400, "This group already exists!")
if not v.charge_account('combined', GROUP_COST)[0]:
return redirect(f"/ping_groups?error=You don't have enough coins or marseybux!")
abort(403, "You don't have enough coins or marseybux!")
g.db.add(v)
if v.shadowbanned: abort(500)
@ -56,7 +56,7 @@ def create_group(v):
for admin in admins:
send_repeatable_notification(admin, f":!marseyparty: !{group} has been created by @{v.username} :marseyparty:")
return redirect(f'/ping_groups?msg=!{group} created successfully!')
return {"message": f"!{group} created successfully!"}
@app.post("/!<group_name>/apply")
@limiter.limit('1/second', scope=rpath)

View File

@ -111,8 +111,7 @@ def inject_constants():
"BADGE_THREAD":BADGE_THREAD, "SNAPPY_THREAD":SNAPPY_THREAD, "CHANGELOG_THREAD":CHANGELOG_THREAD,
"approved_embed_hosts":approved_embed_hosts, "POST_BODY_LENGTH_LIMIT":POST_BODY_LENGTH_LIMIT,
"SITE_SETTINGS":get_settings(), "EMAIL":EMAIL, "max": max, "min": min, "user_can_see":User.can_see,
"TELEGRAM_ID":TELEGRAM_ID, "EMAIL_REGEX_PATTERN":EMAIL_REGEX_PATTERN,
"TRUESCORE_DONATE_MINIMUM":TRUESCORE_DONATE_MINIMUM, "PROGSTACK_ID":PROGSTACK_ID,
"TELEGRAM_ID":TELEGRAM_ID, "TRUESCORE_DONATE_MINIMUM":TRUESCORE_DONATE_MINIMUM, "PROGSTACK_ID":PROGSTACK_ID,
"DONATE_LINK":DONATE_LINK, "DONATE_SERVICE":DONATE_SERVICE,
"HOUSE_JOIN_COST":HOUSE_JOIN_COST, "HOUSE_SWITCH_COST":HOUSE_SWITCH_COST, "IMAGE_FORMATS":','.join(IMAGE_FORMATS),
"PAGE_SIZES":PAGE_SIZES, "THEMES":THEMES, "COMMENT_SORTS":COMMENT_SORTS, "SORTS":SORTS,

View File

@ -77,13 +77,13 @@ def login_post(v):
try:
if now - int(request.values.get("time")) > 600:
return redirect('/login')
return render_template("login/login.html", failed=True, redirect=redir)
except:
abort(400)
formhash = request.values.get("hash")
if not validate_hash(f"{account.id}+{request.values.get('time')}+2fachallenge", formhash):
return redirect("/login")
return render_template("login/login.html", failed=True, redirect=redir)
if not account.validate_2fa(request.values.get("2fa_token", "").strip()):
hash = generate_hash(f"{account.id}+{now}+2fachallenge")

View File

@ -95,7 +95,7 @@ def request_api_keys(v):
push_notif(admin_ids, 'New notification', body, f'{SITE_FULL}/comment/{new_comment.id}?read=true#context')
return redirect('/settings/apps')
return {"message": "API keys requested successfully!"}
@app.post("/delete_app/<int:aid>")
@ -145,8 +145,7 @@ def edit_oauth_app(v, aid):
g.db.add(app)
return redirect('/settings/apps')
return {"message": "App edited successfully!"}
@app.post("/admin/app/approve/<int:aid>")

View File

@ -38,7 +38,7 @@ def settings(v):
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def settings_personal(v):
return render_template("settings/personal.html", v=v, error=get_error(), msg=get_msg())
return render_template("settings/personal.html", v=v, error=get_error())
@app.delete('/settings/background')
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@ -110,7 +110,7 @@ def settings_personal_post(v):
request_flag = int(time.time())
setattr(v, column_name, request_flag)
if badge_id: badge_grant(v, badge_id)
return render_template("settings/personal.html", v=v, msg=f"You have set the {friendly_name} permanently! Enjoy your new badge!")
return {"message": f"You have set the {friendly_name} permanently! Enjoy your new badge!"}
elif current_value != request_flag:
setattr(v, column_name, request_flag)
return True
@ -199,25 +199,25 @@ def settings_personal_post(v):
v.bio = None
v.bio_html = None
g.db.add(v)
return render_template("settings/personal.html", v=v, msg="Your bio has been updated!")
return {"message": "Your bio has been updated."}
elif not updated and request.values.get("sig") == "":
v.sig = None
v.sig_html = None
g.db.add(v)
return render_template("settings/personal.html", v=v, msg="Your sig has been updated!")
return {"message": "Your sig has been updated."}
elif not updated and request.values.get("friends") == "":
v.friends = None
v.friends_html = None
g.db.add(v)
return render_template("settings/personal.html", v=v, msg="Your friends list has been updated!")
return {"message": "Your friends list has been updated."}
elif not updated and request.values.get("enemies") == "":
v.enemies = None
v.enemies_html = None
g.db.add(v)
return render_template("settings/personal.html", v=v, msg="Your enemies list has been updated!")
return {"message": "Your enemies list has been updated."}
elif not updated and request.values.get("sig"):
if not v.patron:
@ -226,16 +226,12 @@ def settings_personal_post(v):
sig = request.values.get("sig")[:200].replace('\n','').replace('\r','')
sig_html = sanitize(sig, blackjack="signature")
if len(sig_html) > 1000:
return render_template("settings/personal.html",
v=v,
error="Your sig is too long")
abort(400, "Your sig is too long")
v.sig = sig[:200]
v.sig_html=sig_html
g.db.add(v)
return render_template("settings/personal.html",
v=v,
msg="Your sig has been updated.")
return {"message": "Your sig has been updated."}
elif not updated and FEATURES['USERS_PROFILE_BODYTEXT'] and request.values.get("friends"):
friends = request.values.get("friends")[:BIO_FRIENDS_ENEMIES_LENGTH_LIMIT]
@ -243,9 +239,7 @@ def settings_personal_post(v):
friends_html = sanitize(friends, blackjack="friends")
if len(friends_html) > BIO_FRIENDS_ENEMIES_HTML_LENGTH_LIMIT:
return render_template("settings/personal.html",
v=v,
error="Your friends list is too long")
abort(400, "Your friends list is too long")
friends = friends[:BIO_FRIENDS_ENEMIES_LENGTH_LIMIT]
@ -262,9 +256,7 @@ def settings_personal_post(v):
v.friends = friends
v.friends_html=friends_html
g.db.add(v)
return render_template("settings/personal.html",
v=v,
msg="Your friends list has been updated.")
return {"message": "Your friends list has been updated."}
elif not updated and FEATURES['USERS_PROFILE_BODYTEXT'] and request.values.get("enemies"):
@ -273,9 +265,7 @@ def settings_personal_post(v):
enemies_html = sanitize(enemies, blackjack="enemies")
if len(enemies_html) > BIO_FRIENDS_ENEMIES_HTML_LENGTH_LIMIT:
return render_template("settings/personal.html",
v=v,
error="Your enemies list is too long")
abort(400, "Your enemies list is too long")
enemies = enemies[:BIO_FRIENDS_ENEMIES_LENGTH_LIMIT]
@ -292,9 +282,7 @@ def settings_personal_post(v):
v.enemies = enemies
v.enemies_html=enemies_html
g.db.add(v)
return render_template("settings/personal.html",
v=v,
msg="Your enemies list has been updated.")
return {"message": "Your enemies list has been updated."}
elif not updated and FEATURES['USERS_PROFILE_BODYTEXT'] and \
@ -305,17 +293,13 @@ def settings_personal_post(v):
bio_html = sanitize(bio, blackjack="bio")
if len(bio_html) > BIO_FRIENDS_ENEMIES_HTML_LENGTH_LIMIT:
return render_template("settings/personal.html",
v=v,
error="Your bio is too long")
abort(400, "Your bio is too long")
v.bio = bio[:BIO_FRIENDS_ENEMIES_LENGTH_LIMIT]
v.bio_html=bio_html
g.db.add(v)
return render_template("settings/personal.html",
v=v,
msg="Your bio has been updated.")
return {"message": "Your bio has been updated."}
frontsize = request.values.get("frontsize")
@ -375,14 +359,15 @@ def filters(v):
filters=request.values.get("filters")[:1000].strip()
if filters == v.custom_filter_list:
return redirect("/settings/advanced?error=You didn't change anything!")
abort(400, "You didn't change anything!")
v.custom_filter_list=filters
g.db.add(v)
return redirect("/settings/advanced?msg=Your custom filters have been updated!")
return {"message": "Your custom filters have been updated!"}
def set_color(v, attr, color):
def set_color(v, attr):
color = request.values.get(attr)
current = getattr(v, attr)
color = color.strip().lower() if color else None
if color:
@ -402,7 +387,7 @@ def set_color(v, attr, color):
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def namecolor(v):
return set_color(v, "namecolor", request.values.get("namecolor"))
return set_color(v, "namecolor")
@app.post("/settings/themecolor")
@limiter.limit('1/second', scope=rpath)
@ -411,7 +396,7 @@ def namecolor(v):
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def themecolor(v):
return set_color(v, "themecolor", request.values.get("themecolor"))
return set_color(v, "themecolor")
@app.post("/settings/titlecolor")
@limiter.limit('1/second', scope=rpath)
@ -420,7 +405,7 @@ def themecolor(v):
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def titlecolor(v):
return set_color(v, "titlecolor", request.values.get("titlecolor"))
return set_color(v, "titlecolor")
@app.post("/settings/verifiedcolor")
@limiter.limit('1/second', scope=rpath)
@ -430,7 +415,7 @@ def titlecolor(v):
@auth_required
def verifiedcolor(v):
if not v.verified: abort(403, "You don't have a checkmark to edit its color!")
return set_color(v, "verifiedcolor", request.values.get("verifiedcolor"))
return set_color(v, "verifiedcolor")
@app.post("/settings/security")
@limiter.limit('1/second', scope=rpath)
@ -441,18 +426,18 @@ def verifiedcolor(v):
def settings_security_post(v):
if request.values.get("new_password"):
if request.values.get("new_password") != request.values.get("cnf_password"):
return render_template("settings/security.html", v=v, error="Passwords do not match!")
abort(400, "Passwords do not match!")
if not valid_password_regex.fullmatch(request.values.get("new_password")):
return render_template("settings/security.html", v=v, error="Password must be between 8 and 100 characters!")
abort(400, "Password must be between 8 and 100 characters!")
if not v.verifyPass(request.values.get("old_password")):
return render_template("settings/security.html", v=v, error="Incorrect password")
abort(400, "Incorrect password")
v.passhash = hash_password(request.values.get("new_password"))
g.db.add(v)
return render_template("settings/security.html", v=v, msg="Your password has been changed!")
return {"message": "Your password has been changed successfully!"}
if request.values.get("new_email"):
if not v.verifyPass(request.values.get('password')):
@ -483,29 +468,29 @@ def settings_security_post(v):
if request.values.get("2fa_token"):
if not v.verifyPass(request.values.get('password')):
return render_template("settings/security.html", v=v, error="Invalid password!")
abort(400, "Invalid password!")
secret = request.values.get("2fa_secret")
x = pyotp.TOTP(secret)
if not x.verify(request.values.get("2fa_token"), valid_window=1):
return render_template("settings/security.html", v=v, error="Invalid token!")
abort(400, "Invalid token!")
v.mfa_secret = secret
g.db.add(v)
return render_template("settings/security.html", v=v, msg="Two-factor authentication enabled!")
return {"message": "Two-factor authentication enabled!"}
if request.values.get("2fa_remove"):
if not v.verifyPass(request.values.get('password')):
return render_template("settings/security.html", v=v, error="Invalid password!")
abort(400, "Invalid password!")
token = request.values.get("2fa_remove")
if not token or not v.validate_2fa(token):
return render_template("settings/security.html", v=v, error="Invalid token!")
abort(400, "Invalid token!")
v.mfa_secret = None
g.db.add(v)
return render_template("settings/security.html", v=v, msg="Two-factor authentication disabled!")
return {"message": "Two-factor authentication disabled!"}
@app.post("/settings/log_out_all_others")
@limiter.limit('1/second', scope=rpath)
@ -516,13 +501,13 @@ def settings_security_post(v):
def settings_log_out_others(v):
submitted_password = request.values.get("password", "").strip()
if not v.verifyPass(submitted_password):
return redirect("/settings/security?error=Incorrect password!")
abort(400, "Incorrect password!")
v.login_nonce += 1
session["login_nonce"] = v.login_nonce
g.db.add(v)
return redirect("/settings/security?msg=All other devices have been logged out!")
return {"message": "All other devices have been logged out!"}
@app.post("/settings/images/profile")
@ -619,7 +604,7 @@ def settings_images_profile_background(v):
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def settings_css_get(v):
return render_template("settings/css.html", v=v, msg=get_msg(), profilecss=v.profilecss)
return render_template("settings/css.html", v=v, profilecss=v.profilecss)
@app.post("/settings/css")
@limiter.limit('1/second', scope=rpath)
@ -628,12 +613,12 @@ def settings_css_get(v):
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def settings_css(v):
if v.chud: abort(400, "Chuded users can't edit CSS!")
if v.chud:
abort(400, "Chudded users can't edit CSS!")
css = request.values.get("css", v.css).strip().replace('\\', '')[:CSS_LENGTH_LIMIT].strip()
v.css = css
g.db.add(v)
return render_template("settings/css.html", v=v, msg="Custom CSS successfully updated!", profilecss=v.profilecss)
return {"message": "Custom CSS successfully updated!"}
@app.post("/settings/profilecss")
@limiter.limit('1/second', scope=rpath)
@ -645,10 +630,10 @@ def settings_profilecss(v):
profilecss = request.values.get("profilecss", v.profilecss).replace('\\', '')[:CSS_LENGTH_LIMIT].strip()
valid, error = validate_css(profilecss)
if not valid:
return render_template("settings/css.html", error=error, v=v, profilecss=profilecss)
abort(400, error)
v.profilecss = profilecss
g.db.add(v)
return redirect("/settings/css?msg=Profile CSS successfully updated!")
return {"message": "Profile CSS successfully updated!"}
@app.get("/settings/security")
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@ -659,8 +644,6 @@ def settings_security(v):
v=v,
mfa_secret=pyotp.random_base32() if not v.mfa_secret else None,
now=int(time.time()),
error=get_error(),
msg=get_msg()
)
@app.get("/settings/blocks")
@ -726,7 +709,7 @@ def settings_apps(v):
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def settings_advanced_get(v):
return render_template("settings/advanced.html", v=v, msg=get_msg(), error=get_error())
return render_template("settings/advanced.html", v=v)
@app.post("/settings/name_change")
@limiter.limit('1/second', scope=rpath)
@ -745,9 +728,7 @@ def settings_name_change(v):
new_name=request.values.get("name").strip()
if new_name==v.username:
return render_template("settings/personal.html",
v=v,
error="You didn't change anything")
abort(400, "You didn't change anything")
if v.patron:
used_regex = valid_username_patron_regex
@ -755,9 +736,7 @@ def settings_name_change(v):
used_regex = valid_username_regex
if not used_regex.fullmatch(new_name):
return render_template("settings/personal.html",
v=v,
error="This isn't a valid username.")
abort(400, "This isn't a valid username.")
search_name = new_name.replace('\\', '').replace('_','\_').replace('%','')
@ -770,15 +749,13 @@ def settings_name_change(v):
).one_or_none()
if x and x.id != v.id:
return render_template("settings/personal.html",
v=v,
error=f"Username `{new_name}` is already in use.")
abort(400, f"Username `{new_name}` is already in use.")
v.username = new_name
v.name_changed_utc = int(time.time())
g.db.add(v)
return redirect("/settings/personal?msg=Name successfully changed!")
return {"message": "Name successfully changed!"}
@app.post("/settings/song_change_mp3")
@feature_required('USERS_PROFILE_SONG')
@ -904,13 +881,13 @@ def process_settings_plaintext(value, current, length):
value = request.values.get(value, "").strip()
if not value:
return redirect("/settings/personal?error=You didn't enter anything!")
abort(400, "You didn't enter anything!")
if len(value) > 100:
return redirect("/settings/personal?error=The value you entered exceeds the character limit (100 characters)")
abort(400, "The value you entered exceeds the character limit (100 characters)")
if value == current:
return redirect("/settings/personal?error=You didn't change anything!")
abort(400, "You didn't change anything!")
return value
@ -924,21 +901,19 @@ def process_settings_plaintext(value, current, length):
def settings_title_change(v):
if v.flairchanged: abort(403)
processed = process_settings_plaintext("title", v.customtitleplain, 100)
if not isinstance(processed, str):
return processed
customtitleplain = process_settings_plaintext("title", v.customtitleplain, 100)
customtitle = filter_emojis_only(processed)
customtitle = filter_emojis_only(customtitleplain)
customtitle = censor_slurs(customtitle, None)
if len(customtitle) > 1000:
return redirect("/settings/personal?error=Flair too long!")
abort(400, "Flair too long!")
v.customtitleplain = processed
v.customtitleplain = customtitleplain
v.customtitle = customtitle
g.db.add(v)
return redirect("/settings/personal?msg=Flair successfully updated!")
return {"message": "Flair successfully updated!"}
@app.post("/settings/pronouns_change")
@ -949,13 +924,10 @@ def settings_title_change(v):
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def settings_pronouns_change(v):
processed = process_settings_plaintext("pronouns", v.pronouns, 15)
if not isinstance(processed, str):
return processed
pronouns = process_settings_plaintext("pronouns", v.pronouns, 15)
pronouns = processed
if not pronouns_regex.fullmatch(pronouns):
return redirect("/settings/personal?error=The pronouns you entered don't match the required format!")
abort(400, "The pronouns you entered don't match the required format!")
bare_pronouns = pronouns.lower().replace('/', '')
if 'nig' in bare_pronouns: pronouns = 'BI/POC'
@ -964,7 +936,7 @@ def settings_pronouns_change(v):
v.pronouns = pronouns
g.db.add(v)
return redirect("/settings/personal?msg=Pronouns successfully updated!")
return {"message": "Pronouns successfully updated!"}
@app.post("/settings/checkmark_text")
@ -977,10 +949,6 @@ def settings_checkmark_text(v):
if not v.verified:
abort(403, "You don't have a checkmark to edit its hover text!")
processed = process_settings_plaintext("checkmark-text", v.verified, 100)
if not isinstance(processed, str):
return processed
v.verified = processed
v.verified = process_settings_plaintext("checkmark-text", v.verified, 100)
g.db.add(v)
return redirect("/settings/personal?msg=Checkmark Text successfully updated!")
return {"message": "Checkmark Text successfully updated!"}

View File

@ -260,7 +260,7 @@ def api(v):
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@auth_desired
def contact(v):
return render_template("contact.html", v=v, msg=get_msg())
return render_template("contact.html", v=v)
@app.post("/contact")
@limiter.limit('1/second', scope=rpath)
@ -304,7 +304,7 @@ def submit_contact(v):
push_notif(admin_ids, f'New modmail from @{new_comment.author_name}', new_comment.body, f'{SITE_FULL}/notifications/modmail')
return redirect("/contact?msg=Your message has been sent to the admins!")
return {"message": "Your message has been sent to the admins!"}
patron_badges = (22,23,24,25,26,27,28,257,258,259,260,261)

View File

@ -87,7 +87,7 @@ def unexile(v, sub, uid):
u = get_account(uid)
if not v.mods(sub): abort(403)
if v.shadowbanned: return redirect(f'/h/{sub}/exilees')
if v.shadowbanned: abort(403)
if u.exiler_username(sub):
exile = g.db.query(Exile).filter_by(user_id=u.id, sub=sub).one_or_none()
@ -103,11 +103,7 @@ def unexile(v, sub, uid):
)
g.db.add(ma)
if g.is_api_or_xhr:
return {"message": f"@{u.username} has been unexiled from /h/{sub} successfully!"}
return redirect(f'/h/{sub}/exilees')
return {"message": f"@{u.username} has been unexiled from /h/{sub} successfully!"}
@app.post("/h/<sub>/block")
@limiter.limit('1/second', scope=rpath)
@ -275,7 +271,7 @@ def add_mod(v, sub):
if SITE_NAME == 'WPD': abort(403)
sub = get_sub_by_name(sub).name
if not v.mods(sub): abort(403)
if v.shadowbanned: return redirect(f'/h/{sub}/mods')
if v.shadowbanned: abort(400)
user = request.values.get('user')
@ -303,7 +299,7 @@ def add_mod(v, sub):
)
g.db.add(ma)
return redirect(f'/h/{sub}/mods')
return {"message": "Mod added successfully!"}
@app.post("/h/<sub>/remove_mod")
@limiter.limit('1/second', scope=rpath)
@ -356,7 +352,7 @@ def create_sub(v):
if not v.can_create_hole:
abort(403)
return render_template("sub/create_hole.html", v=v, cost=HOLE_COST, error=get_error())
return render_template("sub/create_hole.html", v=v, cost=HOLE_COST)
@app.post("/create_hole")
@limiter.limit('1/second', scope=rpath)
@ -373,15 +369,15 @@ def create_sub2(v):
name = name.strip().lower()
if not valid_sub_regex.fullmatch(name):
return redirect(f"/create_hole?error=Name does not match the required format!")
abort(400, "Name does not match the required format!")
if not v.charge_account('combined', HOLE_COST)[0]:
return redirect(f"/create_hole?error=You don't have enough coins or marseybux!")
abort(400, "You don't have enough coins or marseybux!")
sub = get_sub_by_name(name, graceful=True)
if sub:
return redirect(f"/create_hole?error=/h/{sub} already exists!")
abort(400, f"/h/{sub} already exists!")
g.db.add(v)
if v.shadowbanned: abort(500)
@ -396,7 +392,7 @@ def create_sub2(v):
for admin in admins:
send_repeatable_notification(admin, f":!marseyparty: /h/{sub} has been created by @{v.username} :marseyparty:")
return redirect(f'/h/{sub}')
return {"message": f"/h/{sub} created successfully!"}
@app.post("/kick/<int:pid>")
@limiter.limit('1/second', scope=rpath)
@ -452,13 +448,13 @@ def sub_settings(v, sub):
def post_sub_sidebar(v, sub):
sub = get_sub_by_name(sub)
if not v.mods(sub.name): abort(403)
if v.shadowbanned: return redirect(f'/h/{sub}/settings')
if v.shadowbanned: abort(400)
sub.sidebar = request.values.get('sidebar', '')[:10000].strip()
sidebar_html = sanitize(sub.sidebar, blackjack=f"/h/{sub} sidebar")
if len(sidebar_html) > 20000:
return render_template('sub/settings.html', v=v, sidebar=sub.sidebar, sub=sub, error="Sidebar is too big!", css=sub.css)
abort(400, "Sidebar is too big! (max 20000 characters)")
sub.sidebar_html = sidebar_html
g.db.add(sub)
@ -470,7 +466,7 @@ def post_sub_sidebar(v, sub):
)
g.db.add(ma)
return render_template('sub/settings.html', v=v, sidebar=sub.sidebar, sub=sub, msg='CSS changed successfully!', css=sub.css)
return {"message": "Sidebar changed successfully!"}
@app.post('/h/<sub>/css')
@ -485,15 +481,14 @@ def post_sub_css(v, sub):
if not sub: abort(404)
if not v.mods(sub.name): abort(403)
if v.shadowbanned: return redirect(f'/h/{sub}/settings')
if v.shadowbanned: abort(400)
if len(css) > 6000:
error = "CSS is too long (max 6000 characters)"
return render_template('sub/settings.html', v=v, sidebar=sub.sidebar, sub=sub, error=error, css=css)
abort(400, "CSS is too long (max 6000 characters)")
valid, error = validate_css(css)
if not valid:
return render_template('sub/settings.html', v=v, sidebar=sub.sidebar, sub=sub, error=error, css=css)
abort(400, error)
sub.css = css
g.db.add(sub)
@ -505,7 +500,7 @@ def post_sub_css(v, sub):
)
g.db.add(ma)
return render_template('sub/settings.html', v=v, sidebar=sub.sidebar, sub=sub, msg='CSS changed successfully!', css=sub.css)
return {"message": "CSS changed successfully!"}
@app.get("/h/<sub>/css")
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)

View File

@ -1374,9 +1374,7 @@ def toggle_pins(sub, sort):
pins = session.get(f'{sub}_{sort}', default)
session[f'{sub}_{sort}'] = not pins
if is_site_url(request.referrer):
return redirect(request.referrer)
return redirect('/')
return {"message": "Pins toggled successfully!"}
@app.get("/badge_owners/<int:bid>")

View File

@ -63,17 +63,4 @@
</div>
</div>
<div class="toast" id="toast-post-success" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-success text-center text-white">
<i class="fas fa-comment-alt-smile mr-2"></i><span id="toast-post-success-text">Action successful!</span>
</div>
</div>
<div class="toast" id="toast-post-error" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-danger text-center text-white">
<i class="fas fa-exclamation-circle mr-2"></i><span id="toast-post-error-text">Error, please try again later.</span>
</div>
</div>
{% endblock %}

View File

@ -51,17 +51,4 @@
</div>
</div>
<div class="toast" id="toast-post-success" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-success text-center text-white">
<i class="fas fa-comment-alt-smile mr-2"></i><span id="toast-post-success-text">Action successful!</span>
</div>
</div>
<div class="toast" id="toast-post-error" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-danger text-center text-white">
<i class="fas fa-exclamation-circle mr-2"></i><span id="toast-post-error-text">Error, please try again later.</span>
</div>
</div>
{% endblock %}

View File

@ -5,12 +5,9 @@
<link rel="stylesheet" href="{{('css/admin/badges.css') | asset}}">
{% if error %}{{macros.alert(error, true)}}{% endif %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
{% set form_action = "/admin/badge_grant" if grant else "/admin/badge_remove" %}
<form action="{{form_action}}" method="post">
<form action="{{form_action}}" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<div class="overflow-x-auto mt-4"><table class="table table-striped">

View File

@ -29,7 +29,7 @@
</table>
<form action="/admin/ban_domain" method="post">
<form action="/admin/ban_domain" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<input autocomplete="off" name="domain" placeholder="Enter domain here.." class="form-control" required>
<input autocomplete="off" name="reason" placeholder="Enter ban reason here.." data-nonce="{{g.nonce}}" data-undisable_element="ban-submit" class="form-control mt-2">

View File

@ -3,9 +3,7 @@
{% block pagetitle %}Delete Media{% endblock %}
{% block content %}
{% if error %}{{macros.alert(error, true)}}{% endif %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
<form class="mt-3" action="/admin/delete_media" method="post">
<form class="mt-3" action="/admin/delete_media" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<div class="container">
<div class="row justify-content-center mb-4 pb-6">
<div class="col col-md-6 p-3 py-md-0">

View File

@ -3,7 +3,6 @@
{% block pagetitle %}Edit {{SITE_NAME}}'s rules{% endblock %}
{% block content %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
<div class="row my-5">
<div class="col">
<div class="settings mx-3">
@ -13,7 +12,7 @@
</div>
<div class="body d-lg-flex">
<div class="w-lg-100">
<form id="edit_rules" action="/admin/edit_rules" method="post">
<form id="edit_rules" action="/admin/edit_rules" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<textarea autocomplete="off" maxlength="10000" class="form-control rounded" placeholder="Enter rules here..." rows="50" name="rules" form="edit_rules">{% if rules %}{{rules}}{% endif %}</textarea>

View File

@ -3,7 +3,6 @@
{% block pagetitle %}Orgy Control Panel{% endblock %}
{% block content %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
<div class="row my-5">
<div class="col">
<div class="settings mx-3">
@ -14,7 +13,7 @@
<div class="body d-lg-flex">
<div class="w-lg-100">
{%if not orgy%}
<form id="orgy" action="/admin/start_orgy" method="post">
<form id="orgy" action="/admin/start_orgy" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<div class="d-lg-flex border-bottom">
<div class="title w-lg-25">
<label for="title">Title</label>
@ -37,7 +36,7 @@
</div>
</form>
{%else%}
<form id="orgy" action="/admin/stop_orgy" method="post">
<form id="orgy" action="/admin/stop_orgy" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<div class="d-flex mt-2">
<input autocomplete="off" class="btn btn-primary ml-auto" type="submit" value="Stop Orgy">

View File

@ -2,15 +2,12 @@
{% block pagetitle %}Update {{type}}{% endblock %}
{% block pagetype %}message{% endblock %}
{% block content %}
{% if error %}{{macros.alert(error, true)}}{% endif %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
<div class="mx-4">
<h2 class="mt-5">Update {{type}}</h2>
<div class="settings-section rounded">
<div class="d-lg-flex">
<div class="body w-lg-100">
<form action="{{request.path}}" method="post" enctype="multipart/form-data">
<form action="{{request.path}}" method="post" enctype="multipart/form-data" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<label class="mt-3" for="name">{{type}} Name (Required)</label>

View File

@ -28,17 +28,6 @@
{{macros.chat_users_list()}}
</div>
<div class="toast" id="toast-post-success" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-success text-center text-white">
<i class="fas fa-comment-alt-smile mr-2"></i><span id="toast-post-success-text">Action successful!</span>
</div>
</div>
<div class="toast" id="toast-post-error" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-danger text-center text-white">
<i class="fas fa-exclamation-circle mr-2"></i><span id="toast-post-error-text">Error, please try again later.</span>
</div>
</div>
<input id="vid" hidden value="{{v.id}}">
<input id="site_name" hidden value="{{SITE_NAME}}">

View File

@ -24,35 +24,30 @@
{% endif %}
{% if c.is_blocking and not c.ghost or (c.is_banned or c.deleted_utc) and not (v and v.admin_level >= PERMS['POST_COMMENT_MODERATION']) and not (v and v.id==c.author_id) %}
<div id="comment-{{c.id}}" class="comment {% if not (v and v.id == author_id) and not (v and v.admin_level) and not comment_info %}collapsed{% endif %}">
<span class="comment-collapse-desktop" style="border-color: #{{c.author.name_color}}" data-nonce="{{g.nonce}}" data-onclick="collapse_comment('{{c.id}}')"></span>
<div class="comment-body">
<div id="comment-{{c.id}}-only" class="{% if c.award_count('tilt', v) %}tilt-comment{% endif %} {% if c.award_count('glowie', v) %}glow{% endif %} comment-{{c.id}}-only">
<div class="user-info">
<span class="comment-collapse-icon" data-nonce="{{g.nonce}}" data-onclick="collapse_comment('{{c.id}}')"></span>
{% if standalone and c.over_18 %}<span class="badge badge-danger">+18</span> {% endif %}
{% if c.is_banned %}removed by @{{c.ban_reason}} (Site Admin){% elif c.deleted_utc %}Deleted by author{% elif c.is_blocking %}You are blocking @{{c.author_name}}{% endif %}
</div>
</div>
{% if render_replies %}
<div id="replies-of-{{c.fullname}}">
{% if level<9 or request.path.startswith('/notifications') %}
{% for reply in replies %}
{{single_comment(reply, level=level+1)}}
{% endfor %}
{% elif replies %}
<button type="button" id="btn-{{c.id}}" class="d-mob-none btn btn-primary mt-3" data-nonce="{{g.nonce}}" data-onclick="more_comments('{{c.id}}', '{{sort}}')">More comments</button>
<a class="d-md-none py-3" href="{{c.more_comments}}">More comments <i class="fas fa-long-arrow-right ml-1"></i></a>
{% endif %}
<span class="comment-collapse-desktop" style="border-color: #{{c.author.name_color}}" data-nonce="{{g.nonce}}" data-onclick="collapse_comment('{{c.id}}')"></span>
<div class="comment-body">
<div id="comment-{{c.id}}-only" class="{% if c.award_count('tilt', v) %}tilt-comment{% endif %} {% if c.award_count('glowie', v) %}glow{% endif %} comment-{{c.id}}-only">
<div class="user-info">
<span class="comment-collapse-icon" data-nonce="{{g.nonce}}" data-onclick="collapse_comment('{{c.id}}')"></span>
{% if standalone and c.over_18 %}<span class="badge badge-danger">+18</span> {% endif %}
{% if c.is_banned %}removed by @{{c.ban_reason}} (Site Admin){% elif c.deleted_utc %}Deleted by author{% elif c.is_blocking %}You are blocking @{{c.author_name}}{% endif %}
</div>
</div>
{% endif %}
{% if render_replies %}
<div id="replies-of-{{c.fullname}}">
{% if level<9 or request.path.startswith('/notifications') %}
{% for reply in replies %}
{{single_comment(reply, level=level+1)}}
{% endfor %}
{% elif replies %}
<button type="button" id="btn-{{c.id}}" class="d-mob-none btn btn-primary mt-3" data-nonce="{{g.nonce}}" data-onclick="more_comments('{{c.id}}', '{{sort}}')">More comments</button>
<a class="d-md-none py-3" href="{{c.more_comments}}">More comments <i class="fas fa-long-arrow-right ml-1"></i></a>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
{% else %}
{% set score=c.score %}

View File

@ -1,10 +1,9 @@
{% extends "default.html" %}
{% block pagetitle %}Contact{% endblock %}
{% block content %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
<h1 class="article-title">Contact {{SITE_NAME}} Admins</h1>
{% if v %}
<form id="contactform" action="/contact" method="post" enctype="multipart/form-data">
<form id="contactform" action="/contact" method="post" enctype="multipart/form-data" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<label for="input-message" class="mt-3">Your message</label>
<input hidden name="formkey" value="{{v|formkey}}">

View File

@ -100,16 +100,6 @@
<i class="fas fa-check-circle text-success mr-2"></i>Link copied to clipboard
</div>
</div>
<div class="toast" id="toast-post-success" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-success text-center text-white">
<i class="fas fa-comment-alt-smile mr-2"></i><span id="toast-post-success-text">Action successful!</span>
</div>
</div>
<div class="toast" id="toast-post-error" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-danger text-center text-white">
<i class="fas fa-exclamation-circle mr-2"></i><span id="toast-post-error-text">Error, please try again later.</span>
</div>
</div>
<script defer src="{{'js/vendor/lozad.js' | asset}}"></script>
<script defer src="{{'js/vendor/lite-youtube.js' | asset}}"></script>

View File

@ -9,15 +9,13 @@
<h5>Are you over 18?</h5>
<p class="mb-3">This post is rated +18 (Adult-Only). You must be 18 or older to continue. Are you sure you want to proceed?</p>
<div class="btn-toolbar justify-content-center mb-4">
<form action="/allow_nsfw" method="post">
<form action="/allow_nsfw" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<input hidden name="redir" value="{{request.full_path}}">
<input type="submit" class="btn btn-danger mr-2" value="Yes, I am +18">
</form>
<div class="mt-3"><a href="/" class="btn btn-secondary">No</a></div>
<div class="mt-5"><a href="/settings/advanced#over_18_toggle" class="btn btn-secondary">Never show me this warning again</a></div>
</div>
</div>
</div>
</div>

View File

@ -1,8 +1,6 @@
{% extends "default.html" %}
{% block pagetitle %}Ping Groups{% endblock %}
{% block content %}
{% if error %}{{macros.alert(error, true)}}{% endif %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
<div class="px-3">
<h3 class="my-3">Ping Groups</h3>
<div class="overflow-x-auto">
@ -43,7 +41,7 @@
</div>
<h3 class="my-3">Create Ping Group</h3>
<form class="mt-3" action="/create_group" method="post">
<form class="mt-3" action="/create_group" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<div class="container pb-0" style="background-color: transparent !important">
<div class="row mb-4 pb-6">
<div class="col col-md-6 px-0 py-3 py-md-0">
@ -54,11 +52,6 @@
<small class="form-text text-muted">3-25 characters, including letters, numbers, _ , and -</small>
<div class="footer">
<div class="d-flex">
{% if error %}
<p class="mb-0">
<span class="text-danger" style="vertical-align: sub;">{{error}}</span>
</p>
{% endif %}
<button type="submit" class="btn btn-primary ml-auto" {% if v.marseybux + v.coins < cost %}disabled{% endif %}>Create Group</button>
</div>
<p class="mt-2 mr-1" style="float: right"><b>Cost</b>: {{cost}} coins or marseybux</p>

View File

@ -77,7 +77,7 @@
{% block navbar %}
<div class="d-flex align-items-center">
{% set pcolor = "primary" if pins else "secondary" %}
<form action="/toggle_pins/{{sub}}/{{sort}}" method="post">
<form action="/toggle_pins/{{sub}}/{{sort}}" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<button type="submit" class="btn btn-{{pcolor}} text-{{pcolor}} mx-2"><i type="submit" class="fas fas fa-thumbtack fa-rotate--45 mr-2"></i>Pins</button>
</form>

View File

@ -2,32 +2,31 @@
{% block pagetitle %}Login{% endblock %}
{% block pagetype %}login{% endblock %}
{% block template_config %}
{% set root_scope.js = false %}
{% set root_scope.include_user_css = false %}
{% set root_scope.include_seo = false %}
{% set root_scope.include_cf_2fa_verify = false %}
{% endblock %}
{% block body %}
<div class="container-fluid position-absolute h-100 p-0">
<div class="row no-gutters h-100">
<div class="col-12 col-md-6 my-auto p-3 bg-background">
<div class="row justify-content-center">
<div class="col-10 col-md-7">
<h2>{% block authtitle %}{% endblock %}</h2>
<p class="text-muted mb-md-2">{% block authtext %}{% endblock %}</p>
{% if error %}{{macros.alert(error, true)}}{% endif %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
{% block content %}{% endblock %}
</div>
</div>
</div>
<div class="col-12 col-md-6 d-mob-none">
<div class="splash-wrapper">
<div class="splash-overlay"></div>
<img alt="cover" loading="lazy" class="splash-img" src="{{'cover.webp' | asset_siteimg}}"></img>
</div>
</div>
<div class="row no-gutters h-100">
<div class="col-12 col-md-6 my-auto p-3 bg-background">
<div class="row justify-content-center">
<div class="col-10 col-md-7">
<h2>{% block authtitle %}{% endblock %}</h2>
<p class="text-muted mb-md-2">{% block authtext %}{% endblock %}</p>
{% if error %}{{macros.alert(error, true)}}{% endif %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
{% block content %}{% endblock %}
</div>
</div>
</div>
<div class="col-12 col-md-6 d-mob-none">
<div class="splash-wrapper">
<div class="splash-overlay"></div>
<img alt="cover" loading="lazy" class="splash-img" src="{{'cover.webp' | asset_siteimg}}"></img>
</div>
</div>
</div>
{% block scripts %}{% endblock %}
</div>
{% block scripts %}{% endblock %}
{% endblock %}

View File

@ -2,7 +2,6 @@
{% block pagetitle %}Login{% endblock %}
{% block pagetype %}login{% endblock %}
{% block template_config %}
{% set root_scope.js = true %}
{% set root_scope.include_user_css = false %}
{% set root_scope.include_seo = false %}
{% set root_scope.include_cf_2fa_verify = false %}

View File

@ -2,7 +2,6 @@
{% block pagetitle %}Login{% endblock %}
{% block pagetype %}login{% endblock %}
{% block template_config %}
{% set root_scope.js = false %}
{% set root_scope.include_user_css = false %}
{% set root_scope.include_seo = false %}
{% set root_scope.include_cf_2fa_verify = false %}

View File

@ -10,7 +10,7 @@
<label for="email" class="mt-3">Password</label>
<input autocomplete="off" class="form-control" id="password" type="password" name="password" required>
<label for="email" class="mt-3">Email</label>
<input autocomplete="off" class="form-control" id="password" type="email" pattern='{{EMAIL_REGEX_PATTERN}}' name="email" required{% if v %} value="{{v.email}}" disabled{% endif %}>
<input autocomplete="off" class="form-control" id="password" type="email" name="email" required{% if v %} value="{{v.email}}" disabled{% endif %}>
<input autocomplete="off" class="btn btn-primary login w-100 mt-3" type="submit" value="Send recovery link">
</form>
</div>

View File

@ -5,7 +5,6 @@
{% block pagetype %}login{% endblock %}
{% block template_config %}
{% set root_scope.js = true %}
{% set root_scope.include_user_css = false %}
{% set root_scope.include_seo = false %}
{% set root_scope.include_cf_2fa_verify = false %}

View File

@ -2,7 +2,6 @@
{% block pagetitle %}Sign Up: Error{% endblock %}
{% block pagetype %}login{% endblock %}
{% block template_config %}
{% set root_scope.js = true %}
{% set root_scope.include_user_css = false %}
{% set root_scope.include_seo = true %}
{% set root_scope.include_cf_2fa_verify = false %}

View File

@ -68,15 +68,4 @@
</div>
</div>
</div>
<div class="toast" id="toast-post-success2" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 1000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-success text-center text-white">
<i class="fas fa-comment-alt-smile mr-2"></i><span id="toast-post-success-text2">Action successful!</span>
</div>
</div>
<div class="toast" id="toast-post-error2" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 1000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-danger text-center text-white">
<i class="fas fa-exclamation-circle mr-2"></i><span id="toast-post-error-text2">Error, please try again later.</span>
</div>
</div>
</div>

View File

@ -1,6 +1,5 @@
{% set root_scope = namespace() %}
{% block template_config %}
{% set root_scope.js = true %}
{% set root_scope.include_user_css = true %}
{% set root_scope.include_seo = true %}
{% set root_scope.include_cf_2fa_verify = false %}
@ -21,7 +20,7 @@
<meta charset="utf-8">
{% block title %}<title>{% block pagetitle %}if you see this pls report it as a bug &lt;3{% endblock %} - {{SITE_NAME}}</title>{% endblock %}
{{html_head.page_meta(self.pagetitle() or none)}}
{{html_head.javascript() if root_scope.js}}
{{html_head.javascript()}}
{{html_head.stylesheets(root_scope.include_user_css)}}
{{html_head.seo() if root_scope.include_seo}}
{{html_head.cf_2fa_verify() if root_scope.include_2fa_verify}}
@ -45,5 +44,15 @@
<script defer src="{{'js/cursormarsey.js' | asset}}"></script>
{% endif %}
<script defer src="{{'js/pwa_pulltorefresh.js' | asset}}"></script>
<div class="toast" id="toast-post-success" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-success text-center text-white">
<i class="fas fa-comment-alt-smile mr-2"></i><span id="toast-post-success-text">Action successful!</span>
</div>
</div>
<div class="toast" id="toast-post-error" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-danger text-center text-white">
<i class="fas fa-exclamation-circle mr-2"></i><span id="toast-post-error-text">Error, please try again later.</span>
</div>
</div>
</body>
</html>

View File

@ -50,16 +50,6 @@
</div>
</div>
{% endblock %}
<div class="toast" id="toast-post-success" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-success text-center text-white">
<i class="fas fa-comment-alt-smile mr-2"></i><span id="toast-post-success-text">Action successful!</span>
</div>
</div>
<div class="toast" id="toast-post-error" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-danger text-center text-white">
<i class="fas fa-exclamation-circle mr-2"></i><span id="toast-post-error-text">Error, please try again later.</span>
</div>
</div>
{% block onload %}{% endblock %}
<script defer src="{{'js/vendor/clipboard.js' | asset}}"></script>
{% endblock %}

View File

@ -140,7 +140,7 @@
<label for="custom-filter">Custom Filters</label>
</div>
<div class="body w-lg-100">
<form id="custom-filter" action="/settings/filters" method="post">
<form id="custom-filter" action="/settings/filters" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<small>Hides matching posts from the frontpage and collapses matching comments.</small>
<textarea autocomplete="off" class="form-control rounded" id="filters-text"

View File

@ -4,10 +4,12 @@
<div class="row">
<div class="col col-lg-8">
<div class="settings">
<h5><a href="/api">API Guide</a></h5>
<h5 class="mt-1">Your API Applications</h5>
<h5>API Guide</h5>
<a style="font-size:18px" href="/api">{{SITE_FULL}}/api</a>
<h5 class="mt-6">Your API Applications</h5>
{% for app in v.applications if app.client_id %}
<form id="edit-app-{{app.id}}" action="/edit_app/{{app.id}}" method="post">
<form id="edit-app-{{app.id}}" action="/edit_app/{{app.id}}" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<div class="settings-section rounded">
<div class="d-lg-flex">
<div class="title w-lg-25">
@ -41,9 +43,9 @@
<p>None</p>
{% endfor %}
<h5>API Applications Awaiting Approval</h5>
<h5 class="mt-6">API Applications Awaiting Approval</h5>
{% for app in v.applications if not app.client_id %}
<form id="edit-app-{{app.id}}" action="/edit_app/{{app.id}}" method="post">
<form id="edit-app-{{app.id}}" action="/edit_app/{{app.id}}" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<div class="settings-section rounded">
<div class="d-lg-flex">
<div class="title w-lg-25">
@ -76,7 +78,7 @@
<p>None</p>
{% endfor %}
<h5>Your Authorized Applications</h5>
<h5 class="mt-6">Your Authorized Applications</h5>
{% for auth in v.authorizations %}
<div id="auth-{{auth.id}}" class="settings-section rounded">
<div class="d-lg-flex">
@ -99,9 +101,9 @@
<p>None</p>
{% endfor %}
<h2>Request API Keys</h2>
<h5 class="mt-6">Request API Keys</h2>
<form id="api-key-request-form" action="/api_keys" method="post">
<form id="api-key-request-form" action="/api_keys" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<div class="settings-section rounded">
<div class="d-lg-flex">
<div class="body w-lg-100">

View File

@ -56,7 +56,7 @@
<div class="body d-lg-flex border-bottom">
<label class="text-black w-lg-25">{{section_title}}</label>
<div class="w-lg-100">
<form id="{{id}}-form" action="{{form_action}}" method="post">
<form id="{{id}}-form" action="{{form_action}}" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<input minlength={{minlength}} maxlength={{maxlength}} pattern="{{pattern}}" autocomplete="off" id="{{id}}-body" type="text" name="{{form_name}}" class="form-control" placeholder='{{placeholder_text}}' value="{% if contents %}{{contents}}{% endif %}" {% if disabled %}disabled{% endif %}>
<div class="d-flex mt-2">
@ -75,7 +75,7 @@
<div class="body d-lg-flex border-bottom">
<label class="text-black w-lg-25">{{section_title}}</label>
<div class="w-lg-100">
<form id="{{id}}-form" action="{{form_action}}" method="post" enctype="multipart/form-data">
<form id="{{id}}-form" action="{{form_action}}" method="post" enctype="multipart/form-data" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<textarea autocomplete="off" id="{{id}}-text" class="file-ta form-control rounded" placeholder="{{placeholder_text}}" rows="3" name="{{form_name}}" form="{{id}}-form" maxlength="{{maxlength}}">{% if contents %}{{contents}}{% endif %}</textarea>
{% if show_extras %}

View File

@ -9,7 +9,7 @@
<p class="text-small text-muted pt-2 pl-3">Edit your custom CSS for the site</p>
<div class="body d-lg-flex border-bottom">
<div class="w-lg-100">
<form id="profile-settings-css" action="/settings/css" method="post">
<form id="profile-settings-css" action="/settings/css" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<textarea autocomplete="off" class="form-control rounded" id="css-textarea" placeholder="Custom CSS" rows="3" name="css" form="profile-settings-css" maxlength="{{CSS_LENGTH_LIMIT}}">{% if v.css %}{{v.csslazy}}{% endif %}</textarea>
<small>Limit of {{CSS_LENGTH_LIMIT}} characters</small>
@ -27,7 +27,7 @@
<p class="text-small text-muted pt-2 pl-3">Edit your profile CSS</p>
<div class="body d-lg-flex border-bottom">
<div class="w-lg-100">
<form id="profile-settings-profilecss" action="/settings/profilecss" method="post">
<form id="profile-settings-profilecss" action="/settings/profilecss" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<textarea autocomplete="off" class="form-control rounded" id="profilecss-textarea" placeholder="Custom profile CSS" rows="3" name="profilecss" form="profile-settings-profilecss" maxlength="{{CSS_LENGTH_LIMIT}}">{% if profilecss %}{{profilecss}}{% endif %}</textarea>
<small>Limit of {{CSS_LENGTH_LIMIT}} characters</small>

View File

@ -16,10 +16,8 @@
</div>
<div class="body w-lg-100">
<p>You're a {{TIER_TO_NAME[v.patron] if v.patron else "freeloader"}}!</p>{% if v.patron %} Thanks ily! &lt;3{% endif %}
{% if not v.patron and v.truescore >= TRUESCORE_DONATE_MINIMUM %}
<p class="font-italic">To stop freeloading, first <a href="/settings/security#new_email">verify your email</a>, support us on <a href="{{DONATE_LINK}}">{{DONATE_SERVICE}}</a> with the same email, and click "Claim {{patron}} Rewards"</p>
{% elif not v.patron %}
<p class="font-italic">To stop freeloading, you can <a href="/donate">donate via crypto</a>. Please let us know first beforehand by <a href="/contact">sending us a modmail.</a> Thanks!</p>
{% if not v.patron %}
<p class="font-italic">To stop freeloading, support us <a href="/donate">here</a></p>
{% endif %}
</div>
</div>
@ -186,7 +184,7 @@
</div>
<div class="body w-lg-100">
<p>Your original username will always stay reserved for you: <code>{{v.original_username}}</code></p>
<form action="/settings/name_change" method="post">
<form action="/settings/name_change" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<input id="name-body" autocomplete="off" type="text" name="name" class="form-control" value="{{v.username}}" {% if v.namechanged %}disabled{% endif %}>
<small>{% if v.patron %}1{% else %}3{% endif %}-25 characters, including letters, numbers, _ , and -</small>
@ -332,7 +330,7 @@
{% if FEATURES['USERS_PERMANENT_WORD_FILTERS'] -%}
<div class="modal fade" id="modal-{{id}}" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<form class="m-auto" action="{{form_action}}" id="{{id}}-form" method="post">
<form class="m-auto" action="{{form_action}}" id="{{id}}-form" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Make {{friendly_name}} Permanent</h5>

View File

@ -15,7 +15,7 @@
<div class="d-lg-flex">
<label for="new-email" class="w-lg-25">Email</label>
<div class="w-lg-100">
<input autocomplete="off" class="form-control" id="new_email" {% if v.email %}placeholder="{{v.email}}"{% else %}placeholder="Your email"{% endif %} type="email" pattern='{{EMAIL_REGEX_PATTERN}}' name="new_email" required>
<input autocomplete="off" class="form-control" id="new_email" {% if v.email %}placeholder="{{v.email}}"{% else %}placeholder="Your email"{% endif %} type="email" name="new_email" required>
{% if v.email and not v.email_verified %}
<div class="text-danger mt-1" id="email-verify-text">Email not verified. You will not be able to recover your account with this email until you verify it. <u><button type="button" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/verify_email');emailVerifyText()" class="text-primary font-weight-bold ml-1">Verify now.</button></u></div>
{% elif not v.email %}
@ -58,7 +58,7 @@
<section id="site-settings-password-section" class="settings-section-section">
<h5>Password</h5>
<div class="settings-section rounded">
<form action="/settings/security" method="post">
<form action="/settings/security" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<div class="body">
<div class="d-lg-flex">
<label for="old_password" class="mb-0 w-lg-25">Old Password</label>
@ -106,8 +106,8 @@
</section>
<section id="site-settings-logout-everywhere-section" class="settings-section-section">
<h5>Log Out Everywhere</h5>
<form action="/settings/log_out_all_others" method="post">
<input hidden name="formkey" value="{{v|formkey}}">
<form action="/settings/log_out_all_others" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<div class="body">
<div class="d-lg-flex">
<label for="forcelog-password" class="w-lg-25" id="email-password-label">Password</label>
@ -145,7 +145,7 @@
<span><i class="far fa-times"></i></span>
</button>
</div>
<form action="/settings/security" method="post">
<form action="/settings/security" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<input hidden name="2fa_secret" value="{{mfa_secret}}">
<div class="modal-body">

View File

@ -91,17 +91,6 @@
<i class="fas fa-exclamation-circle mr-2"></i>Unable to copy link
</div>
</div>
<div class="toast" id="toast-post-success" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-success text-center text-white">
<i class="fas fa-comment-alt-smile mr-2"></i><span id="toast-post-success-text">Action successful!</span>
</div>
</div>
<div class="toast" id="toast-post-error" style="position: fixed; bottom: 1.5rem; margin: 0 auto; left: 0; right: 0; width: 275px; z-index: 100000" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-body bg-danger text-center text-white">
<i class="fas fa-exclamation-circle mr-2"></i><span id="toast-post-error-text">Error, please try again later.</span>
</div>
</div>
{% block mobilenavbar %}{% include "mobile_navigation_bar.html" %}{% endblock %}
{% block scripts %}{% endblock %}
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "default.html" %}
{% block pagetitle %}Create a {{HOLE_NAME}}{% endblock %}
{% block content %}
<form class="mt-3" id="submitform" action="/create_hole" method="post">
<form class="mt-3" id="submitform" action="/create_hole" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<div class="container">
<div class="row justify-content-center mb-4 pb-6">
<div class="col col-md-6 p-3 py-md-0">
@ -17,11 +17,6 @@
{% endif %}
<div class="footer">
<div class="d-flex">
{% if error %}
<p class="mb-0">
<span class="text-danger" style="vertical-align: sub;">{{error}}</span>
</p>
{% endif %}
<button type="submit" class="btn btn-primary ml-auto" {% if cost > v.coins + v.marseybux %}disabled{% endif %}>Create {{HOLE_NAME|capitalize}}</button>
</div>
<p class="mt-2 mr-1" style="float: right"><b>Cost</b>: {{cost}} coins or marseybux</p>

View File

@ -24,7 +24,7 @@
<td {% if exile.created_utc %}data-time="{{exile.created_utc}}"{% endif %}></td>
<td>
{% if v.mods(sub.name) %}
<form action="/h/{{sub}}/unexile/{{user.id}}" method="post">
<form action="/h/{{sub}}/unexile/{{user.id}}" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<input autocomplete="off" class="btn btn-primary ml-auto" type="submit" value="Unexile">
</form>

View File

@ -34,7 +34,7 @@
</table>
{% if v.mods(sub.name) %}
<form action="/h/{{sub}}/add_mod" method="post">
<form action="/h/{{sub}}/add_mod" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<input class="form-control" style="display:inline;width:250px" autocomplete="off" type="text" name="user" class="form-control" placeholder="Enter username..">
<input autocomplete="off" class="btn btn-primary ml-auto" type="submit" value="Add Mod" style="margin-bottom: 5px">

View File

@ -2,8 +2,6 @@
{% block pagetitle %}/h/{{sub}} Settings{% endblock %}
{% block content %}
{% if error %}{{macros.alert(error, true)}}{% endif %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
<div class="title mt-5">
<label class="text-lg" for="stealth">Stealth Mode</label>
@ -82,7 +80,7 @@
</div>
<div class="body d-lg-flex">
<div class="w-lg-100">
<form id="sidebar" action="/h/{{sub}}/sidebar" method="post">
<form id="sidebar" action="/h/{{sub}}/sidebar" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<textarea autocomplete="off" maxlength="10000" class="form-control rounded" id="bio-text" placeholder="Enter sidebar here..." rows="10" name="sidebar" form="sidebar">{% if sub.sidebar %}{{sub.sidebar}}{% endif %}</textarea>
<div class="d-flex mt-2">
@ -103,7 +101,7 @@
</div>
<div class="body d-lg-flex">
<div class="w-lg-100">
<form id="css" action="/h/{{sub}}/css" method="post">
<form id="css" action="/h/{{sub}}/css" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<textarea autocomplete="off" maxlength="6000" class="form-control rounded" id="bio-text" placeholder="Enter css here..." rows="10" name="css" form="css">{% if css %}{{css}}{% endif %}</textarea>
<div class="d-flex mt-2">

View File

@ -2,7 +2,6 @@
{% block pagetitle %}Create a Post{% endblock %}
{% block pagetype %}submit{% endblock %}
{% block template_config %}
{% set root_scope.js = true %}
{% set root_scope.include_user_css = true %}
{% set root_scope.include_seo = false %}
{% set root_scope.include_cf_2fa_verify = false %}

View File

@ -2,14 +2,12 @@
{% block pagetitle %}Submit Emojis{% endblock %}
{% block pagetype %}message{% endblock %}
{% block content %}
{% if error %}{{macros.alert(error, true)}}{% endif %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
<div class="mx-4">
<h2 class="mt-5">Submit Emoji</h2>
<div class="settings-section rounded">
<div class="d-lg-flex">
<div class="body w-lg-100">
<form action="/submit/emojis" method="post" enctype="multipart/form-data">
<form action="/submit/emojis" method="post" enctype="multipart/form-data" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<div id="image-upload-block">

View File

@ -2,15 +2,12 @@
{% block pagetitle %}Submit Hats{% endblock %}
{% block pagetype %}message{% endblock %}
{% block content %}
{% if error %}{{macros.alert(error, true)}}{% endif %}
{% if msg %}{{macros.alert(msg, false)}}{% endif %}
<div class="mx-4">
<h2 class="mt-5">Submit Hat</h2>
<div class="settings-section rounded">
<div class="d-lg-flex">
<div class="body w-lg-100">
<form action="/submit/hats" method="post" enctype="multipart/form-data">
<form action="/submit/hats" method="post" enctype="multipart/form-data" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<input hidden name="formkey" value="{{v|formkey}}">
<div id="image-upload-block">