BDServer/modules/Render.py

1870 lines
53 KiB
Python
Raw Normal View History

2024-11-19 13:47:45 +02:00
# AGPL 3 or any later version
# (C) J.Y.Amihud ( Blender Dumbass )
import os
import json
import random
2024-11-23 22:34:11 +02:00
import hashlib
2024-11-24 02:53:16 +02:00
import urllib.parse
2024-11-23 22:34:11 +02:00
from datetime import datetime
2024-11-19 13:47:45 +02:00
from modules import Set
from modules import markdown
from modules.Common import *
2024-11-19 13:47:45 +02:00
def guess_type(path):
if "/json/" in path or ".json" in path:
return "application/json"
if "/css" in path or ".css" in path:
return "text/css"
if "/icon" in path or path.endswith(".png"):
return "image/png"
if path.endswith("jpg"):
return "image/jpg"
return "text/html"
def headers(server, code):
# Basic cookie for logins to work
2024-11-19 13:47:45 +02:00
server.send_response(code)
server.send_header("Content-type", guess_type(server.path))
2024-11-25 00:04:12 +02:00
if not server.cookie:
server.send_header("Set-Cookie", "temp_id="+RandString())
2024-11-19 13:47:45 +02:00
server.end_headers()
2024-11-25 23:53:31 +02:00
def head(title="", description="", image="", config={}, author=""):
2024-11-19 13:47:45 +02:00
if image.startswith("/"): image = config.get("url","")+image
2024-11-25 23:53:31 +02:00
favicon = config.get("favicon", "/icon/internet")
2024-11-19 13:47:45 +02:00
html = """
2024-11-23 22:34:11 +02:00
<head>
2024-11-19 13:47:45 +02:00
<!-- The head. Part of the HTML code where metadata about the page is storred.
Including the metadata readable by social media websites, when generating link
previwews, which are called mata-tags in HTML. -->
<meta charset="utf-8">
<title>"""+title+"""</title> <!-- Title in the browser tab -->
<link media="all" href="/css" type="text/css" rel="stylesheet" /> <!-- CSS theme link -->
<link rel="icon" href=\""""+favicon+""""> <!-- Tiny image in the tab -->
2024-11-19 13:47:45 +02:00
<!-- Now meta tags for social media -->
<meta property="og:site_name" content=\""""+config.get("title", "My Website")+"""">
2024-11-23 22:34:11 +02:00
<meta property="og:title" content=\""""+Simplify(title, False)+"""">
<meta property="og:description" content=\""""+Simplify(description, False)+"""">
2024-11-19 13:47:45 +02:00
<meta property="og:image" content=\""""+image+"""">
2024-11-25 23:53:31 +02:00
"""
# Author tags.
if author:
account = accounts().get(author, {})
name = account.get("title", account)
mastodon = account.get("mastodon", "")
try:
if "/" in mastodon:
mastodon = mastodon.replace("https://", "").replace("http://", "")
mastodon = mastodon.split("/")[1]+"@"+mastodon.split("/")[0]
except:
pass
if mastodon:
html = html + """
<meta name=article:author content=\""""+name+"""">
<meta name=fediverse:creator content=\""""+mastodon+"""">
"""
html = html + """
<!-- This meta tag is needed for pretty rendering on phones -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
2024-11-19 13:47:45 +02:00
</head>
<body>
"""
2024-11-23 22:34:11 +02:00
2024-11-19 13:47:45 +02:00
return html
def send(server, html, code):
# Add headers
headers(server, code)
server.wfile.write(html.encode("utf-8"))
def tabs():
folder = Set.Folder()+"/tabs"
tabs = {}
for tab in sorted(list(os.walk(folder))[0][1]):
try:
with open(folder+"/"+tab+"/config.json") as o:
data = json.load(o)
tabs[tab] = data
except Exception as e:
print(e)
pass
return tabs
def accounts():
folder = Set.Folder()+"/accounts"
accounts = {}
for account in sorted(list(os.walk(folder))[0][2]):
try:
with open(folder+"/"+account) as o:
data = json.load(o)
2024-11-23 22:34:11 +02:00
data["username"] = account.replace(".json","")
accounts[account.replace(".json","")] = data
except Exception as e:
print(e)
pass
return accounts
2024-11-23 22:34:11 +02:00
def validate(cookie):
Accounts = accounts()
for account in Accounts:
if cookie in Accounts[account].get("sessions", []):
return Accounts[account]
return {}
def moderates(moderator, user):
Accounts = accounts()
if moderator not in Accounts:
return False
2024-11-23 22:34:11 +02:00
if user not in Accounts:
return True
2024-11-23 22:34:11 +02:00
if moderator == user:
return True
2024-11-24 23:28:19 +02:00
if rank(moderator, Accounts) < rank(user, Accounts):
return True
def rank(account, Accounts=None):
if not Accounts:
Accounts = accounts()
if account not in Accounts:
return 1000000
if not Accounts[account].get("invited_by") or Accounts[account].get("invited_by") == account:
return 0
return 1 + rank(Accounts[account].get("invited_by"), Accounts)
2024-11-23 22:34:11 +02:00
def articles(tab):
folder = Set.Folder()+"/tabs/"+tab
articles = {}
for article in list(os.walk(folder))[0][1]:
try:
with open(folder+"/"+article+"/metadata.json") as o:
data = json.load(o)
data["tab"] = tab
data["url"] = "/"+tab+"/"+article
articles[article] = data
except Exception as e:
print(e)
pass
# Sorting articles based on timestamp
articles = {k:articles[k] for k in sorted(articles, key=lambda y: articles[y]["timestamp"], reverse=True)}
return articles
def allArticles():
articles = {}
f = Set.Folder()
for tab in list(os.walk(f+"/tabs/"))[0][1]:
folder = f+"/tabs/"+tab
for article in list(os.walk(folder))[0][1]:
try:
with open(folder+"/"+article+"/metadata.json") as o:
data = json.load(o)
data["tab"] = tab
data["url"] = "/"+tab+"/"+article
articles[article] = data
except Exception as e:
print(e)
pass
# Sorting articles based on timestamp
articles = {k:articles[k] for k in sorted(articles, key=lambda y: articles[y]["timestamp"], reverse=True)}
return articles
def randomArticles():
articles = {}
f = Set.Folder()
for tab in list(os.walk(f+"/tabs/"))[0][1]:
folder = f+"/tabs/"+tab
for article in list(os.walk(folder))[0][1]:
try:
with open(folder+"/"+article+"/metadata.json") as o:
data = json.load(o)
data["tab"] = tab
data["url"] = "/"+tab+"/"+article
articles[article] = data
except Exception as e:
print(e)
pass
# Randomizing Articles.
newarticles = {}
while articles:
article = random.choice(list(articles.keys()))
newarticles[article] = articles.pop(article)
return newarticles
def suggestedArticles(cookie, random=False):
if not random:
articles = allArticles()
else:
articles = randomArticles()
# Suggesting unread articles.
newarticles = {}
move = []
for article in articles:
if cookie not in articles[article].get("views", {}).get("viewers", []):
move.append(article)
for article in move:
newarticles[article] = articles[article]
for article in articles:
if article not in move:
newarticles[article] = articles[article]
return newarticles
def previewsToSize(text):
# Calculates roughly how many previews to fit any
# given article.
# A thousand character article is about 4 articles.
2024-11-23 22:34:11 +02:00
return len(text)/2200
###
2024-11-19 13:47:45 +02:00
def MainPage(server):
# Reading config
config = Set.Load()
# Generating <head>
html = head(title = config.get("title", "Website"),
description = config.get("description", "Description"),
config = config
)
2024-11-23 22:34:11 +02:00
html = html + LoginButton(server)
html = html + """
<br>
<br>
<center>
<img src=\""""+config.get("favicon", "icon/internet")+"""" alt="[LOGO]" style="height:150px;vertical-align: middle">
<br>
<br>
<h2>"""+config.get("title", "My Website")+"""</h2>
"""+config.get("tagline", "")+"""
<br>
<br>
"""
Tabs = tabs()
for tab in Tabs:
html = html + Button(Tabs[tab].get("title", tab),
"/"+tab,
Tabs[tab].get("icon", "folder"))
html = html + "</center>"
# Trending articles
html = html + '<div class="flexity">'
trends = suggestedArticles(server.cookie)
Buffer = 20
for n, article in enumerate(trends):
if n >= Buffer: break
article = trends[article]
html = html + ArticlePreview(article, Tabs, server.cookie)
html = html + '</div>'
html = html + Footer()
2024-11-23 22:34:11 +02:00
html = html + LoginButton(server)
send(server, html, 200)
def ListPage(server, tab):
Tabs = tabs()
Articles = articles(tab)
config = Set.Load()
try: page = int(server.parsed.get("page", ["0"])[0])
except Exception as e:
print(e)
page = 0
Buffer = 16
From = Buffer*page
To = From+Buffer
# Generating <head>
html = head(title = Tabs.get(tab, {}).get("title", tab),
description = "",
config = config
)
2024-11-23 22:34:11 +02:00
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
# Scroll thingie
if len(Articles) > Buffer:
if page > 0:
html = html + Button(str(page-1), tab+"?page="+str(page-1), "left")
html = html + '<div class="button">'+str(page)+'</div>'
if To < len(Articles)-1:
html = html + Button(str(page+1), tab+"?page="+str(page+1), "right")
html = html + """
<br>
<br>
<!-- Article previews are neatly positioned into a grid here -->
<div class="flexity">
"""
rendered = 0
for n, article in enumerate(Articles):
if n < From: continue
if n >= To: break
html = html + ArticlePreview(Articles[article], Tabs, server.cookie)
rendered += 1
html = html + '</div><br>'
# Bottom pannel for large dialogs
if rendered >= Buffer/2:
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
# Scroll thingie
if len(Articles) > Buffer:
if page > 0:
html = html + Button(str(page-1), tab+"?page="+str(page-1), "left")
html = html + '<div class="button">'+str(page)+'</div>'
if To < len(Articles)-1:
html = html + Button(str(page+1), tab+"?page="+str(page+1), "right")
2024-11-23 22:34:11 +02:00
html = html + LoginButton(server)
send(server, html, 200)
def ArticlePage(server, url):
2024-11-23 22:34:11 +02:00
user = validate(server.cookie)
if url.endswith(".md"):
url = url.replace(".md", "")
config = Set.Load()
tab, article = url.split("/")
Tabs = tabs()
Articles = articles(tab)
f = Set.Folder()
# Generating <head>
html = head(title = Articles.get(article, {}).get("title", article),
description = Articles.get(article, {}).get("description", ""),
2024-11-25 23:53:31 +02:00
image = Articles.get(article, {}).get("thumbnail", ""),
config = config,
author = Articles.get(article, {}).get("author")
)
2024-11-23 22:34:11 +02:00
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
html = html + Button(Tabs.get(tab, {}).get("title", tab), "/"+tab, Tabs.get(tab, {}).get("icon", "folder"))
# The article itself
html = html + '<div class="middle_section_article">'
html = html + '<div class="dark_box">'
html = html +"<center><h1>"+Articles.get(article, {}).get("title", article)+"</h1></center>"
# Page author
author = Articles.get(article, {}).get("author", "")
if author:
html = html + '<center>'+User( author )+'</center>'
# Page views
# Views are calculated using an iframe which only loads when the page is
# rendered in a browser. It is also looking at whether the same cookie renders
# the same page, to avoid double counting from the same person.
# The iframe itself shows a graph of views.
views = str(Articles.get(article, {}).get("views", {}).get("amount", 0))
html = html + '<br><center><details><summary class="button">👁 '+views+'</summary>'
html = html + """
<iframe width="100%" height="500px" style="border:none; border-radius:25px;" src="/graph"""+server.path+""""></iframe>
<br><br>
</details>
"""
html = html + '</div>'
License = Articles.get(article, {}).get("license", "")
if License:
html = html + '<div class="dark_box"> <center>'
html = html + 'License:<br><img style="vertical-align: middle" src="/icon/'+License+'"> '
html = html + '<a href="'+Licenses.get(License, {}).get("link")+'">'
html = html + Licenses.get(License, {}).get("name")+'</a>'
html = html + '</center></div>'
# Audio recording of the article
recording = Articles.get(article, {}).get("recording", "")
if recording:
html = html + '<audio controls="controls" style="min-width:100%;" src="'+recording+'"></audio>'
html = html + '<div class="dark_box">'
html = html + markdown.convert(f+"/tabs/"+tab+"/"+article+"/text.md")
html = html + '</div>'
2024-11-23 22:34:11 +02:00
# Comments
html = html + CommentInput(server, url)
comments = Articles.get(article, {}).get("comments", {}).get("comments", [])
commentsTextLength = 0
comment_edit = server.parsed.get("comment_edit", [""])[0]
if comments:
for n, comment in enumerate(comments):
if str(n) == comment_edit and moderates(user.get("username"), comment.get("username")):
html = html + CommentEditInput(server, comment, url, n, user)
else:
html = html + Comment(comment, url, n, user)
2024-11-23 22:34:11 +02:00
# Needed to extend the suggestion for pages with many comments
commentsTextLength += previewsToSize(comment.get("text", ""))
# Requests
requests = Articles.get(article, {}).get("comments", {}).get("requests", [])
if requests:
for n, comment in enumerate(requests):
if comment.get("cookie") == server.cookie:
html = html + Comment(comment, url, n, user, request=True)
elif moderates(user.get("username"), comment.get("username")):
html = html + CommentEditInput(server, comment, url, n, user, request=str(n))
html = html + '</div>'
# Thumbnail and suggestions
html = html + '<div class="checklist_section_article">'
thumbnail = Articles.get(article, {}).get("thumbnail")
if thumbnail:
html = html + '<div class="article_box"><br>'
html = html + '<img style="min-width:100%; width:100%" src="'+thumbnail+'">'
html = html + '<br><br></div>'
suggestions = suggestedArticles(server.cookie, random=True)
toomuch = previewsToSize(open(f+"/tabs/"+tab+"/"+article+"/text.md").read())
2024-11-23 22:34:11 +02:00
toomuch += commentsTextLength
for n, title in enumerate(suggestions):
if server.path in suggestions[title].get("url") :
continue
if n > toomuch:
break
article = suggestions[title]
html = html + ArticlePreview(article, Tabs, server.cookie)
html = html + ""
2024-11-23 22:34:11 +02:00
html = html + LoginButton(server)
2024-11-19 13:47:45 +02:00
send(server, html, 200)
def AccountPage(server, account):
config = Set.Load()
Accounts = accounts()
Tabs = tabs()
Articles = allArticles()
f = Set.Folder()
# Generating <head>
html = head(title = Safe(Accounts.get(account, {}).get("title", account)),
description = Safe(Accounts.get(account, {}).get("bio" , "")),
config = config
)
2024-11-23 22:34:11 +02:00
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
# Name and bio
html = html + '<div class="middle_section_article">'
2024-11-19 13:47:45 +02:00
html = html + '<div class="dark_box">'
2024-11-24 23:28:19 +02:00
html = html +"<center><h1>"+Accounts.get(account, {}).get("title", account)+"</h1>"
2024-11-24 16:13:01 +02:00
2024-11-24 23:28:19 +02:00
# Rank
Rank = rank(account)
html = html + Button("Rank "+str(Rank), "", icon="analytics")
html = html + '</center>'
2024-11-24 16:13:01 +02:00
2024-11-25 23:53:31 +02:00
# Protecting emails and stuff from scrubbers
if len(str(server.cookie)) == 50:
# Website
2024-11-24 16:13:01 +02:00
2024-11-25 23:53:31 +02:00
website = Safe(Accounts.get(account, {}).get("website" , ""))
if website:
2024-11-24 16:13:01 +02:00
2024-11-25 23:53:31 +02:00
webtitle = website.replace("https://", "").replace("http://", "")
if not website.startswith("http"): website = "http://"+website
2024-11-24 16:13:01 +02:00
2024-11-25 23:53:31 +02:00
html = html + '<center>'
html = html + '<img style="vertical-align: middle" src="/icon/internet">'
html = html + '<a href="'+website+'"> '+webtitle+'</a>'
html = html + '</center>'
2024-11-24 16:13:01 +02:00
2024-11-25 23:53:31 +02:00
# Email
2024-11-24 16:13:01 +02:00
2024-11-25 23:53:31 +02:00
email = Safe(Accounts.get(account, {}).get("email" , ""))
if email:
2024-11-24 23:28:19 +02:00
html = html + '<center>'
2024-11-25 23:53:31 +02:00
html = html + '<img style="vertical-align: middle" src="/icon/frase">'
html = html + '<a href="mailto:'+email+'"> '+email+'</a>'
2024-11-24 23:28:19 +02:00
html = html + '</center>'
2024-11-24 16:13:01 +02:00
2024-11-25 23:53:31 +02:00
# Mastodon
2024-11-24 16:13:01 +02:00
2024-11-25 23:53:31 +02:00
mastodon = Safe(Accounts.get(account, {}).get("mastodon" , ""))
if mastodon:
2024-11-24 16:13:01 +02:00
2024-11-25 23:53:31 +02:00
# It could be mastodon url and not handle.
try:
if "/" in mastodon:
mastodon = mastodon.replace("https://", "").replace("http://", "")
mastodon = mastodon.split("/")[1]+"@"+mastodon.split("/")[0]
mastolink = "https://"+mastodon[1:].split("@")[1]+"/@"+mastodon[1:].split("@")[0]
html = html + '<center>'
html = html + '<img style="vertical-align: middle" src="/icon/mastodon">'
html = html + '<a href="'+mastolink+'"> '+mastodon+'</a>'
html = html + '</center>'
except:
pass
# Matrix
matrix = Safe(Accounts.get(account, {}).get("matrix" , ""))
if matrix:
# Matrix could be the matrix.to link
if "/" in matrix:
matrix = matrix[matrix.rfind("/")+1:]
matrixlink = "https://matrix.to/#/"+matrix
html = html + '<center>'
html = html + '<img style="vertical-align: middle" src="/icon/element">'
html = html + '<a href="'+matrixlink+'"> '+matrix+'</a>'
html = html + '</center>'
2024-11-24 16:13:01 +02:00
2024-11-25 23:53:31 +02:00
if any((website, email, mastodon, matrix)):
html = html + '<br>'
2024-11-24 16:13:01 +02:00
html = html + '</div>'
2024-11-24 23:28:19 +02:00
invited_by = Accounts.get(account, {}).get("invited_by", "")
if invited_by:
html = html + '<div class="dark_box">'
html = html +"<center>Invited by:<br>"+User(invited_by)+"</center>"
html = html + '</div>'
2024-11-24 16:13:01 +02:00
bio = Safe(Accounts.get(account, {}).get("bio" , ""))
if bio:
html = html + '<div class="dark_box">'
2024-11-24 16:13:01 +02:00
html = html + markdown.convert(bio, False)+'<br>'
html = html + '</div>'
# Posts by this account
html = html + '<div class="flexity">'
for article in Articles:
if Articles[article].get("author") != account:
continue
html = html + ArticlePreview(Articles[article], Tabs, server.cookie)
html = html + '</div>'
html = html + '</div>'
# Thumbnail and suggestions
html = html + '<div class="checklist_section_article">'
avatar = Safe(Accounts.get(account, {}).get("avatar" , ""))
if avatar:
html = html + '<div class="article_box"><br>'
html = html + '<img style="min-width:100%; width:100%" src="'+avatar+'">'
html = html + '<br><br></div>'
# Invited
invited = Accounts.get(account, {}).get("invited", [])
if invited:
html = html + '<center><div class="button">Invited:</div></center>'
for username in invited:
2024-11-24 16:13:01 +02:00
if username in Accounts:
html = html + '<div class="dark_box">'
html = html + '<center>' + User(username) + '</center>\n'
html = html + '</div>'
2024-11-23 22:34:11 +02:00
html = html + LoginButton(server)
send(server, html, 200)
def LoginPage(server):
config = Set.Load()
Accounts = accounts()
f = Set.Folder()
wrongname = server.parsed.get("wrong", [""])[0]
# Generating <head>
html = head(title = "Login",
description = "Login",
config = config
)
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
html = html + '<center>'
if wrongname:
html = html + '\n<br>Wrong Username / Password<br>\n'
html = html + """
2024-11-24 23:28:19 +02:00
<div class="middle_section_article">
<div class="dark_box">
2024-11-23 22:34:11 +02:00
<form action="do_login">
<img style="vertical-align: middle" src="/icon/user">
2024-11-24 23:28:19 +02:00
<input class="button" style="width:90%" maxlength="500" id="user_name" required="" name="user_name" pattern="[A-Za-z0-9\.\-\_\]*" placeholder="Username..."></input>
2024-11-23 22:34:11 +02:00
<br>
<img style="vertical-align: middle" src="/icon/lock">
2024-11-24 23:28:19 +02:00
<input class="button" style="width:90%" maxlength="500" id="password" type="password" required="" name="password" placeholder="Password..."></input><br>
2024-11-23 22:34:11 +02:00
<button class="button" type="submit">
<img style="vertical-align: middle" src="/icon/unlock">
Login
</button>
</form>
2024-11-24 23:28:19 +02:00
</div></div>
<div class="checklist_section_article">
2024-11-23 22:34:11 +02:00
2024-11-24 23:28:19 +02:00
<div class="dark_box">
2024-11-23 22:34:11 +02:00
2024-11-24 23:28:19 +02:00
Don't have an account? <br>
2024-11-23 22:34:11 +02:00
"""
2024-11-24 23:28:19 +02:00
html = html + Button("Register", "/register", icon="user_new")
2024-11-23 22:34:11 +02:00
2024-11-24 23:28:19 +02:00
send(server, html, 200)
2024-11-23 22:34:11 +02:00
2024-11-24 23:28:19 +02:00
def RegisterPage(server):
user = validate(server.cookie)
config = Set.Load()
Accounts = accounts()
f = Set.Folder()
code = server.parsed.get("code", [""])[0]
userexists = server.parsed.get("userexists", [""])[0]
wrongcode = server.parsed.get("wrongcode", [""])[0]
# Generating <head>
2024-11-25 00:04:12 +02:00
html = head(title = "Register",
description = "Register",
2024-11-24 23:28:19 +02:00
config = config
)
2024-11-23 22:34:11 +02:00
2024-11-24 23:28:19 +02:00
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
html = html + '<center>'
html = html + """
<div class="middle_section_article">
<div class="dark_box">
<form action="do_register">
"""
if wrongcode:
html = html + "Invalid Invite Code.<br><br>"
if not code and not user:
html = html + """
<img style="vertical-align: middle" src="/icon/user_link">
<input class="button" style="width:90%" maxlength="500" id="code" required="" name="code" placeholder="Invite code..."></input>
"""
username = ""
else:
for account in Accounts:
if code in Accounts[account].get("invite_codes", []):
username = Simplify(Accounts[account]["invite_codes"][code], "file")
html = html + '<center>Invited by:<br><br>'
html = html + User(account)+'<br>'
html = html + '<input type="hidden" name="code" value="'+code+'">'
break
if not user:
if userexists:
html = html + "Username is taken.<br><br>"
html = html + """<img style="vertical-align: middle" src="/icon/user">
<input class="button" style="width:90%" maxlength="500" id="user_name" required="" name="user_name" pattern="[A-Za-z0-9\.\-\_\]*" placeholder="Username..." value=\""""+username+""""></input>
<br>
<img style="vertical-align: middle" src="/icon/lock">
<input class="button" style="width:90%" maxlength="500" id="password" type="password" required="" name="password" placeholder="Password..."></input><br>
<button class="button" type="submit">
<img style="vertical-align: middle" src="/icon/user_new">
Register
</button>
"""
elif code:
html = html + "Share this page with those you invited."
else:
html = html + "<br>You already registered and logged in."
html = html + """
</form>
</div></div>
<div class="checklist_section_article">
<div class="dark_box">
Have an account? <br>
"""
html = html + Button("Login", "/login", icon="unlock")
send(server, html, 200)
2024-11-24 16:13:01 +02:00
def SettingsPage(server):
user = validate(server.cookie)
2024-11-24 23:28:19 +02:00
2024-11-25 23:01:02 +02:00
# Authorization check
2024-11-24 23:28:19 +02:00
if not user:
2024-11-25 23:01:02 +02:00
AccessDenied(server)
2024-11-24 23:28:19 +02:00
return
2024-11-24 16:13:01 +02:00
config = Set.Load()
# Generating <head>
html = head(title = "Settings",
description = "Settings",
config = config
)
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
html = html + Button("Public Profile", "/account/"+user.get("username", ""), image="/pictures/monkey.png")
# Main settings
html = html + """
<div class="middle_section_article">
<div class="dark_box">
<center><h2>Public Info</h2></center>
<form action="update_account">
<img style="vertical-align: middle" src="/icon/user">
<input class="button" style="width:90%" maxlength="200" name="title" placeholder="Visible Name" value='"""+user.get("title", "")+"""'>
<img style="height:40px; vertical-align: middle" src="/pictures/monkey.png">
<input class="button" style="width:90%" maxlength="200" name="avatar" placeholder="Link To Profile Picture" value='"""+user.get("avatar", "")+"""'>
<br>
<center>
<img style="vertical-align: middle" src="/icon/scene">
<b>Bio</b><br>
<textarea class="toot" rows="10" style="width:95%" maxlength="10000" name="bio">"""+Safe(user.get("bio", ""))+"""</textarea>
</center>
<img style="vertical-align: middle" src="/icon/internet">
<input class="button" style="width:90%" maxlength="200" name="website" placeholder="Personal Website" value='"""+user.get("website", "")+"""'>
<img style="vertical-align: middle" src="/icon/frase">
<input class="button" style="width:90%" maxlength="200" name="email" placeholder="Publicly Visible Email" value='"""+user.get("email", "")+"""'>
<img style="vertical-align: middle" src="/icon/mastodon">
<input class="button" style="width:90%" maxlength="200" name="mastodon" placeholder="Mastodon Handle" value='"""+user.get("mastodon", "")+"""'>
<img style="vertical-align: middle" src="/icon/element">
<input class="button" style="width:90%" maxlength="200" name="matrix" placeholder="Matrix Handle" value='"""+user.get("matrix", "")+"""'>
<button class="button" type="submit">
<img style="vertical-align: middle" src="/icon/ok">
Save
</button>
2024-11-23 22:34:11 +02:00
2024-11-24 16:13:01 +02:00
</form>
</div>
"""
2024-11-24 23:28:19 +02:00
# Current Logged in Sessions
sessions = user.get("sessions", {})
html = html + '<div class="dark_box" id="sessions">'
html = html + '<center><h2>Active Sessions</h2>'
2024-11-24 16:13:01 +02:00
2024-11-24 23:28:19 +02:00
for cookie in sessions:
session = sessions[cookie]
CancelButton = Button("Log Out", "/log_out?cookie="+cookie, icon="cancel")
if server.cookie == cookie:
html = html + '<br><img title="This Session" style="vertical-align: middle" src="/icon/checked">'
else:
html = html + '<br><img title="Other Browser" style="vertical-align: middle" src="/icon/unchecked">'
html = html + '<input class="button" style="width:50%" value="' + session + '">'+CancelButton
html = html + '<br><br>'
html = html + '</center>'
html = html + '</div>'
# Invites and Invite codes
invite_codes = user.get("invite_codes", {})
html = html + '<div class="dark_box" id="invites">'
html = html + '<center><h2>Invites</h2></center>'
for code in invite_codes:
nick = invite_codes[code]
Open = ""
if code == server.parsed.get("code", [""])[0]:
Open = "open"
html = html + '<details '+Open+' class="dark_box"><summary id="invite_'+code+'" class="button">'
html = html + '<img style="vertical-align: middle" src="/icon/user">'+nick
html = html + '</summary><br>'
html = html + '<img style="vertical-align: middle" src="/icon/user_link">'
html = html + '<input class="button" style="width:90%" value="' + code + '">'
html = html + Button("Share Link", "/register?code="+code, icon="link")
html = html + Button("Cancel", "/cancel_invite?code="+code, icon="cancel")
html = html + '</details>'
html = html + """
<form action="/create_invite">
<input name="nick" class="button" required="" style="width:50%" placeholder="Name of the person you invite">
<button class="button" type="submit">
<img style="vertical-align: middle" src="/icon/user_new">
Invite
</button>
</form>
2024-11-25 23:01:02 +02:00
</div>
</div>
2024-11-24 23:28:19 +02:00
"""
2024-11-25 23:01:02 +02:00
notifications = user.get("notifications","")
if notifications:
html = html + '<div class="checklist_section_article">'
for notification in notifications:
html = html + '<div class="article_box">'
html = html + '<br><a href="/read_notification?code='+notification.get("code", "")+'">'
html = html + notification.get("text", "Notification")
html = html + '</a><br><br>'
html = html + '</div>'
2024-11-24 16:13:01 +02:00
send(server, html, 200)
2024-11-24 23:28:19 +02:00
2024-11-24 16:13:01 +02:00
###
def Button(text, link, icon="", image=""):
2024-11-19 13:47:45 +02:00
html = """
<a class="button" href=\""""+link+"""">"""
if icon:
html = html + """
<img src="/icon/"""+icon+"""" style="vertical-align: middle">"""
if image:
html = html + """
<img src=\""""+image+"""" style="height:40px;vertical-align: middle">"""
html = html + """
"""+text+"""</a>
"""
return html
2024-11-19 13:47:45 +02:00
def ArticlePreview(article, Tabs, cookie=""):
html = """
<div class="article_box">
"""
url, tab = article.get("url", ""), article.get("tab","")
sup = ""
if cookie not in article.get("views", {}).get("viewers", ""):
sup = '<center><sup><b> &nbsp; Unread &nbsp; </b></sup></center><br>'
html = html + '<a href="'+url+'"><h1>'
html = html + '<img src="/icon/'+Tabs.get(tab, {}).get("icon", "folder")+'" style="vertical-align: middle">'
html = html + article.get("title", "")+"</h1></a>"+sup+"\n"
if "thumbnail" in article:
html = html + '<center><a href="'+url+'"><img src="'+article["thumbnail"]+'"></a></center>'
author = article.get("author", "")
if author:
html = html + '<br><center>'+User( author )+'</center>'
views = str(article.get("views", {}).get("amount", 0))
try: comments = str(len(article.get("comments", {}).get("comments")))
except: comments = "0"
html = html +'<br><center> 👁 '+views+' 💬 '+comments+' </center>'
html = html + "<br>"+markdown.convert(article.get("description", ""), False)+"<br><br>"
html = html + '</div>\n'
return html
def Footer():
html = """
<center>
"""
html = html + Button("Powered with BDServer", "https://codeberg.org/blenderdumbass/BDServer/", "codeberg")
html = html + """
</center>
"""
return html
def User(username, stretch=False):
try:
with open(Set.Folder()+"/accounts/"+username+".json") as o:
account = json.load(o)
except:
account = {}
# We are doing a lot of reductions in case somebody sneaks html code.
avatar = Safe(account.get("avatar", ""))
if not avatar: avatar = "/pictures/monkey.png"
username = Safe(username)
title = Safe(account.get("title", username))
2024-11-23 22:34:11 +02:00
if account:
html = '<img style="height:50px;vertical-align: middle" src="'+avatar+'">&nbsp;&nbsp;<a href="/account/'+username+'">'+title+'</a></center>\n'
else:
html = '<img style="height:50px;vertical-align: middle" src="'+avatar+'">&nbsp;&nbsp;'+title+'</center>\n'
return html
def Graph(server, url):
2024-11-24 16:13:01 +02:00
user = validate(server.cookie)
html = """
<head>
<link media="all" href="/css" type="text/css" rel="stylesheet" /> <!-- CSS theme link -->
</head>
<style>
html {
background-color: none;
background-image: none;
}
</style>
"""
if url.endswith(".md"):
url = url.replace(".md", "")
try:
with open(Set.Folder()+"/tabs"+url+"/metadata.json") as o:
article = json.load(o)
except:
article = {}
dateformat = "%Y-%m-%d"
dates = article.get("views", {}).get("dates", {})
if dates:
largest = max(dates.values())
startdate = datetime.strptime(sorted(list(dates.keys()), reverse=True)[0], dateformat)
enddate = datetime.strptime(sorted(list(dates.keys()), reverse=True)[-1], dateformat)
alldays = int((startdate - enddate).days)
for n, date in enumerate(sorted(dates, reverse=True)):
amount = dates[date]
width = 100 / (alldays+1)
height = 60 * (amount / largest)
cd = datetime.strptime(date, dateformat)
nd = int((startdate - cd).days)
html = html + '<div style="top:'+str(60-height)+'%;right:'+str((nd)*(width))+'%; width:'+str(max(width-0.5,0.5))+'%; height:'+str(height)+'%" title="'+str(date)+' - '+str(amount)+' views" class="graph_line"></div>\n'
# Saving the view
2024-11-24 16:13:01 +02:00
cookies = [server.cookie]
if user:
# If user is logged in, we want to record
# per reading the article, on all pers
# sessions.
2024-11-24 16:13:01 +02:00
for cookie in user.get("sessions"):
if cookie not in cookies:
cookies.append(cookie)
for cookie in cookies:
if cookie and cookie not in article.get("views", {}).get("viewers", []):
2024-11-24 16:13:01 +02:00
article["views"]["amount"] += 1
2024-11-24 16:13:01 +02:00
article["views"]["viewers"].append(cookie)
dates = article["views"]["dates"]
date = datetime.now().strftime(dateformat)
dates[date] = dates.get(date, 0) + 1
2024-11-25 23:17:19 +02:00
server.newview = True
2024-11-24 16:13:01 +02:00
with open(Set.Folder()+"/tabs"+url+"/metadata.json", "w") as save:
json.dump(article, save, indent=4)
send(server, html, 200)
2024-11-23 22:34:11 +02:00
def CommentInput(server, url):
user = validate(server.cookie)
html = """
<div class="dark_box" id="comments">
<br><center>
<form action="/comment">
<input type="hidden" id="url" name="url" value=\""""+url+"""">
"""
if not user:
html = html + '<img style="vertical-align: middle" src="/icon/user">'
html = html + '<input class="button" style="width:90%" maxlength="200" name="username" placeholder="Optional Nick Name">'
else:
html = html + User(user.get("username", ""))+'<center>'
html = html + """
<br>
<img style="vertical-align: middle" src="/icon/bug">
<input class="button" maxlength="200" style="width:90%" name="warning" placeholder="Optional Trigger Warning">
<textarea class="toot" rows="10" required="" style="width:95%" maxlength="10000" name="text">In my opinion </textarea>
<br>
<button class="button" type="submit">
<img style="vertical-align: middle" src="/icon/frase">
Post
</button>
</form>
</center>
</div>
"""
return html
def CommentEditInput(server, comment, url, n=0, user=None, request=None):
Accounts = accounts()
html = '<div class="dark_box" id="comment_'+str(n)+'">'
html = html + """
<form action="/comment">
<input type="hidden" id="url" name="url" value=\""""+url+"""">
<input type="hidden" id="number" name="number" value=\""""+str(n)+"""">
"""
if request:
html = html + '<input type="hidden" id="request" name="request" value=\"'+request+'">'
2024-11-24 02:53:16 +02:00
html = html + '<br><center><sup><b> &nbsp; Pending Approval &nbsp; </b></sup></center><br>'
2024-11-23 22:34:11 +02:00
account = comment.get("username", "")
if account in accounts():
html = html + User(Safe(account))
elif account:
html = html + '<img style="vertical-align: middle" src="/icon/user">'
html = html + '<input class="button" style="width:90%" maxlength="200" name="username" placeholder="Optional Nick Name" value="'+account+'">'
warning = comment.get("warning", "")
html = html + """
<br>
<img style="vertical-align: middle" src="/icon/bug">
<input class="button" maxlength="200" style="width:90%" name="warning" placeholder="Optional Trigger Warning" value=\""""+warning+"""">
"""
html = html + """<textarea class="toot" rows="20" required="" style="width:95%" maxlength="10000" name="text">"""
html = html + Safe(comment.get("text", ""))
html = html + '</textarea>'
html = html + """
<button class="button" type="submit">
<img style="vertical-align: middle" src="/icon/ok">
Save
</button>
"""
html = html + '</form>'
html = html + '</div>'
return html
def Comment(comment, url, n=0, user=None, request=False):
2024-11-24 02:53:16 +02:00
if not request:
html = '<div class="dark_box" id="comment_'+str(n)+'">'
else:
html = '<div class="dark_box" id="request_'+str(n)+'">'
2024-11-23 22:34:11 +02:00
account = comment.get("username", "Anonymous User")
html = html + User(account) + '<br>\n'
if request:
html = html + '<center><sup><b> &nbsp; Pending Approval &nbsp; </b></sup></center><br>'
warning = comment.get("warning", "")
if warning:
html = html + '<details>'
html = html + '<summary class="button" title="Click to show anyway.">'
html = html + '<img src="/icon/bug" style="vertical-align: middle">'
html = html + warning
html = html + '</summary>'
html = html + markdown.convert(Safe(comment.get("text", "")), False)+'<br>'
if warning:
html = html + '</details>'
if moderates(user.get("username"), account):
html = html + '<a class="button" href="/'+url+'?comment_edit='+str(n)+'#comment_'+str(n)+'">'
html = html + '<img src="/icon/edit" style="vertical-align: middle"> Edit'
html = html + '</a>'
2024-11-24 02:53:16 +02:00
html = html + '<a class="button" href="/delete_comment?url='+urllib.parse.quote_plus(url)+'&number='+str(n)+'">'
2024-11-23 22:34:11 +02:00
html = html + '<img src="/icon/cancel" style="vertical-align: middle"> Delete'
html = html + '</a>'
html = html + '</div>\n'
return html
def LoginButton(server):
user = validate(server.cookie)
html = '<div class="login_fixed">'
if not user:
html = html + '<a class="button" href="/login">'
2024-11-24 23:28:19 +02:00
html = html + '<img style="vertical-align: middle" src="/icon/unlock"> Login'
2024-11-23 22:34:11 +02:00
html = html + '</a>'
else:
2024-11-25 23:01:02 +02:00
notifications = len(user.get("notifications", []))
sup = ""
if notifications:
sup = '<sup>'+str(notifications)+'</sup>'
2024-11-23 22:34:11 +02:00
html = html + '<a class="button" href="/settings">'
avatar = Safe(user.get("avatar", ""))
if not avatar: avatar = "/pictures/monkey.png"
html = html + '<img style="height:40px;vertical-align: middle" src="'+avatar+'"> '
html = html + user.get("title", user.get("username", "Anonymous"))
2024-11-25 23:01:02 +02:00
html = html + sup
2024-11-23 22:34:11 +02:00
html = html + '</a>'
html = html + '</div>'
return html
2024-11-25 23:01:02 +02:00
def NotFound(server):
config = Set.Load()
html = head(title = "404 Not Found",
description = "404 Not Found",
config = config
)
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
html = html + """
<center>
<div class="article_box">
<h1>404 Not Found</h1>
</center>
"""
send(server, html, 404)
def AccessDenied(server):
config = Set.Load()
html = head(title = "403 Access Denied",
description = "403 Access Denied",
config = config
)
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
html = html + """
<center>
<div class="article_box">
<h1>403 Access Denied</h1>
</center>
"""
send(server, html, 404)
2024-11-23 22:34:11 +02:00
###
def Redirect(server, url):
html = """<meta http-equiv="Refresh" content="0; url='"""+url+"""'" />"""
send(server, html, 200)
def Login(server):
username = server.parsed.get("user_name", [""])[0]
password = server.parsed.get("password" , [""])[0]
hashed = hashlib.sha512(password.encode("utf-8")).hexdigest()
Accounts = accounts()
2024-11-24 16:13:01 +02:00
# Failed authentication
2024-11-23 22:34:11 +02:00
if username not in Accounts or hashed != Accounts[username].get("password"):
Redirect(server, "/login?wrong=username")
2024-11-24 16:13:01 +02:00
# Succesfull authentication
2024-11-23 22:34:11 +02:00
else:
account = Accounts[username]
if "sessions" not in account:
account["sessions"] = {}
account["sessions"][server.cookie] = server.headers.get("User-Agent")
2024-11-24 16:13:01 +02:00
f = Set.Folder()
folder = f+"/accounts"
2024-11-23 22:34:11 +02:00
with open(folder+"/"+username+".json", "w") as save:
json.dump(account, save, indent=4)
2024-11-24 16:13:01 +02:00
# Move the cookie arround
# When a login happens, we want to make the server know
# which articles the person already read and stuff. So
# we want to come all the cookies. Arround.
articles = allArticles()
for title in articles:
article = articles[title]
for cookie in account["sessions"]:
if cookie != server.cookie:
# Making it so it knows what you were watching previously.
if cookie in article.get("views", {}).get("viewers", [])\
and server.cookie not in article["views"]["viewers"]:
article["views"]["viewers"].append(server.cookie)
# Making it so previously logged in account would know.
# what you were watching thus far from this cookie.
if server.cookie in article.get("views", {}).get("viewers", [])\
and cookie not in article["views"]["viewers"]:
article["views"]["viewers"].append(cookie)
with open(f+"/tabs"+article.get("url")+"/metadata.json", "w") as save:
json.dump(article, save, indent=4)
2024-11-23 22:34:11 +02:00
2024-11-24 16:13:01 +02:00
2024-11-23 22:34:11 +02:00
Redirect(server, "/")
2024-11-24 23:28:19 +02:00
def Register(server):
2024-11-24 16:13:01 +02:00
2024-11-24 23:28:19 +02:00
# If by mistake we are logged in
2024-11-24 16:13:01 +02:00
user = validate(server.cookie)
2024-11-24 23:28:19 +02:00
if user:
Redirect(server, "/register")
username = Simplify(server.parsed.get("user_name", [""])[0], "file")
code = server.parsed.get("code", [""])[0]
password = server.parsed.get("password" , [""])[0]
hashed = hashlib.sha512(password.encode("utf-8")).hexdigest()
Accounts = accounts()
# We avoid username swappage
if username in Accounts or not username:
if code:
Redirect(server, "/register?code="+code+"&userexists=True#user_name")
else:
Redirect(server, "/register?userexists=True#user_name")
return
# Validating the invite code
invited_by = ""
for account in Accounts:
if code in Accounts[account].get("invite_codes", []):
invited_by = account
break
if not invited_by:
Redirect(server, "/register?wrongcode=True")
return
# Now we can finally make our account.
# New account first
account = {
"username":username,
"bio":"",
"invite_codes":{},
"invited":[],
"invited_by":invited_by,
"password":hashed,
"title":username,
"email":"",
"website":"",
"mastodon":"",
"matrix":"",
"sessions":{
server.cookie:server.headers.get("User-Agent")
}
}
f = Set.Folder()
folder = f+"/accounts"
with open(folder+"/"+username+".json", "w") as save:
json.dump(account, save, indent=4)
# Now the invitor changes
2024-11-24 16:13:01 +02:00
2024-11-24 23:28:19 +02:00
account = Accounts[invited_by]
del account["invite_codes"][code]
account["invited"].append(username)
with open(folder+"/"+account.get("username", "")+".json", "w") as save:
json.dump(account, save, indent=4)
Redirect(server, "/settings")
2024-11-25 23:01:02 +02:00
# Notification
Notify(invited_by, "/account/"+Safe(username), "@"+Safe(username)+" has registered from your invitation.")
2024-11-24 23:28:19 +02:00
def LogOut(server):
user = validate(server.cookie)
2024-11-25 23:01:02 +02:00
# Authorization check
if not user:
AccessDenied(server)
return
2024-11-24 23:28:19 +02:00
cookie = server.parsed.get("cookie", [""])[0]
# This might be an attack. So we don't want that.
if cookie not in user.get("sessions",{}):
Redirect(server, "/")
return
del user["sessions"][cookie]
f = Set.Folder()
folder = f+"/accounts"
with open(folder+"/"+user.get("username", "")+".json", "w") as save:
json.dump(user, save, indent=4)
# If the user logged out this session
if cookie == server.cookie:
Redirect(server, "/")
else:
Redirect(server, "/settings#sessions")
def UpdateAccount(server):
user = validate(server.cookie)
2024-11-25 23:01:02 +02:00
# Authorization check
2024-11-24 23:28:19 +02:00
if not user:
2024-11-25 23:01:02 +02:00
AccessDenied(server)
2024-11-24 23:28:19 +02:00
return
2024-11-24 16:13:01 +02:00
keys = [
"title",
"avatar",
"bio",
"website",
"email",
"mastodon",
"matrix"
]
for key in keys:
data = server.parsed.get(key, [""])[0]
user[key] = Safe(data)
f = Set.Folder()
folder = f+"/accounts"
with open(folder+"/"+user.get("username", "")+".json", "w") as save:
json.dump(user, save, indent=4)
Redirect(server, "/settings")
2024-11-23 22:34:11 +02:00
def DoComment(server):
user = validate(server.cookie)
Accounts = accounts()
url = server.parsed.get("url", ["/"])[0]
if not url.startswith("/"): url = "/" + url
text = server.parsed.get("text", [""])[0]
nick = server.parsed.get("username", [""])[0]
warn = server.parsed.get("warning", [""])[0]
number = server.parsed.get("number", [""])[0]
request = server.parsed.get("request", [""])[0]
2024-11-25 23:01:02 +02:00
wasnumber = number
2024-11-23 22:34:11 +02:00
metadata = Set.Folder()+"/tabs"+url+"/metadata.json"
try:
with open(metadata) as o:
article = json.load(o)
except:
Redirect(server, "/")
return
if "comments" not in article:
article["comments"] = {}
if "comments" not in article["comments"]:
article["comments"]["comments"] = []
if "requests" not in article["comments"]:
article["comments"]["requests"] = []
comment = {
"text":text
}
2024-11-24 02:53:16 +02:00
placeRedirect = "#comment_"
2024-11-23 22:34:11 +02:00
if warn:
comment["warning"] = warn
if not nick and user:
comment["username"] = user.get("username", "")
place = "comments"
elif request:
if nick in Accounts or not nick:
nick = "Anonymous Guest"
comment["username"] = nick
del article["comments"]["requests"][int(request)]
place = "comments"
number = ""
else:
if nick in Accounts or not nick:
nick = "Anonymous Guest"
comment["username"] = nick
2024-11-24 02:53:16 +02:00
placeRedirect = "#request_"
2024-11-23 22:34:11 +02:00
place = "requests"
if not user:
comment["cookie"] = server.cookie
if not number:
article["comments"][place].append(comment)
2024-11-24 02:53:16 +02:00
number = len(article["comments"][place])-1
2024-11-23 22:34:11 +02:00
else:
number = int(number)
if moderates(user.get("username"), article["comments"]["comments"][number]["username"]):
2024-11-24 23:28:19 +02:00
# Making sure moderators done get credit for small edits
# in comments.
originalcommet = article["comments"]["comments"][number]
if originalcommet.get("username") in Accounts:
comment["username"] = originalcommet["username"]
2024-11-23 22:34:11 +02:00
article["comments"]["comments"][number] = comment
try:
with open(metadata, "w") as save:
json.dump(article, save, indent=4)
except:
pass
2024-11-24 02:53:16 +02:00
if not number:
placeRedirect = "#comments"
number = ""
2024-11-25 23:01:02 +02:00
2024-11-24 02:53:16 +02:00
Redirect(server, url+placeRedirect+str(number))
2024-11-25 23:01:02 +02:00
if not wasnumber:
# Notification
username = user.get("username", nick)
if username != article.get("author"):
Notify(article.get("author"), url+placeRedirect+str(number), "@"+Safe(username)+" commented: <br><br> <b>"+article.get("title", "")+"</b><br><br><i>"+Safe(text[:200])+"</i>")
# Mention notifications
searchText = text.lower()
for account in Accounts:
# The author already got the notification.
if account == article.get("author"):
continue
name = Accounts[account].get("title", account)
if account.lower() in searchText or name.lower() in searchText:
Notify(account,
url+placeRedirect+str(number),
"@"+Safe(username)+" mentioned you: <br><br> <b>"+article.get("title", "")+"</b><br><br><i>"+Safe(text[:200])+"</i>")
2024-11-24 02:53:16 +02:00
def DeleteComment(server):
user = validate(server.cookie)
2024-11-25 23:01:02 +02:00
# Authorization check
if not user:
AccessDenied(server)
return
2024-11-24 02:53:16 +02:00
url = server.parsed.get("url", ["/"])[0]
if not url.startswith("/"): url = "/" + url
number = int(server.parsed.get("number", ["0"])[0])
metadata = Set.Folder()+"/tabs"+url+"/metadata.json"
2024-11-23 22:34:11 +02:00
2024-11-24 02:53:16 +02:00
try:
with open(metadata) as o:
article = json.load(o)
except:
Redirect(server, "/")
return
comment = article["comments"]["comments"][number]
if moderates(user.get("username", ""), comment.get("username", "")):
del article["comments"]["comments"][number]
try:
with open(metadata, "w") as save:
json.dump(article, save, indent=4)
except:
pass
if number:
redirect = "#comment_"+str(number-1)
else:
redirect = "#comments"
Redirect(server, url+redirect)
2024-11-24 23:28:19 +02:00
def CancelInvite(server):
user = validate(server.cookie)
2024-11-25 23:01:02 +02:00
# Authorization check
if not user:
AccessDenied(server)
return
2024-11-24 23:28:19 +02:00
code = server.parsed.get("code", [""])[0]
if user:
del user["invite_codes"][code]
f = Set.Folder()
folder = f+"/accounts"
with open(folder+"/"+user.get("username", "")+".json", "w") as save:
json.dump(user, save, indent=4)
Redirect(server, "/settings#invites")
else:
Redirect(server, "/")
def CreateInvite(server):
user = validate(server.cookie)
2024-11-25 23:01:02 +02:00
# Authorization check
if not user:
AccessDenied(server)
return
2024-11-24 23:28:19 +02:00
nick = server.parsed.get("nick", [""])[0]
if not nick: nick = "Unknown"
code = RandString()
if user:
user["invite_codes"][code] = nick
f = Set.Folder()
folder = f+"/accounts"
with open(folder+"/"+user.get("username", "")+".json", "w") as save:
json.dump(user, save, indent=4)
Redirect(server, "/settings?code="+code+"#invite_"+code)
else:
Redirect(server, "/")
2024-11-25 23:01:02 +02:00
def Notify(username, link, text):
Accounts = accounts()
try:
account = Accounts[username]
if "notifications" not in account:
account["notifications"] = []
notification = {
"link":link,
"text":text,
"code":RandString(20)
}
account["notifications"].append(notification)
f = Set.Folder()
folder = f+"/accounts"
with open(folder+"/"+account.get("username", "")+".json", "w") as save:
json.dump(account, save, indent=4)
except Exception as e:
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Unable to set notification!", e, link, text)
def ReadNotification(server):
user = validate(server.cookie)
# Authorization check
if not user:
AccessDenied(server)
return
code = server.parsed.get("code", [""])[0]
try:
# Apparently I'm stupid to use a link here.
# But I already commited to it and I'm editing
# on a live server. So here we go... O.o
for n, notification in enumerate(user.get("notifications")):
if notification.get("code") == code:
break
n = user["notifications"].pop(n)
f = Set.Folder()
folder = f+"/accounts"
with open(folder+"/"+user.get("username", "")+".json", "w") as save:
json.dump(user, save, indent=4)
Redirect(server, n.get("link", "/"))
except Exception as e:
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Unable to read notification!", e)