Basic comments working, Login working.
This commit is contained in:
parent
08e55ccdfe
commit
6a1689a239
6 changed files with 431 additions and 15 deletions
BIN
icons/lock.png
Normal file
BIN
icons/lock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 754 B |
BIN
icons/unlock.png
Normal file
BIN
icons/unlock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 844 B |
|
@ -48,11 +48,14 @@ clr = {
|
|||
"bbwh":"\033[108m" # Bright White
|
||||
}
|
||||
|
||||
def Simplify(text):
|
||||
def Simplify(text, extrasimple=True):
|
||||
|
||||
good = "qwertyuiopasdfghjklzxcvbnm.1234567890-_:"
|
||||
good = "QWERTYUIOPLKJHGFDSAZXCVBNMqwertyuiopasdfghjklzxcvbnm.1234567890-_:* "
|
||||
|
||||
text = text.lower()
|
||||
if extrasimple:
|
||||
good = "qwertyuiopasdfghjklzxcvbnm.1234567890-_:"
|
||||
text = text.lower()
|
||||
|
||||
ntext = ""
|
||||
for i in text:
|
||||
if i in good:
|
||||
|
|
|
@ -93,8 +93,8 @@ def Transfer(location):
|
|||
commentdata = json.load(o)
|
||||
|
||||
comments = {
|
||||
"comments":commentdata["comments"].get(tab+"/"+article),
|
||||
"requests":commentdata["requests"].get(tab+"/"+article)
|
||||
"comments":commentdata["comments"].get(tab+"/"+article, []),
|
||||
"requests":commentdata["requests"].get(tab+"/"+article, [])
|
||||
}
|
||||
|
||||
|
||||
|
@ -145,7 +145,7 @@ def Transfer(location):
|
|||
account["contact"] = "" # Contact info
|
||||
account["website"] = "" # Website
|
||||
account["mastodon"] = "" # Mastodon ( useful for fediverse tags )
|
||||
account["sessions"] = [] # List of cookies accosiated with this account
|
||||
account["sessions"] = {} # List of cookies accosiated with this account
|
||||
|
||||
# We are saving the accounts in a different way too.
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
import os
|
||||
import json
|
||||
import random
|
||||
import hashlib
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from modules import Set
|
||||
|
@ -45,6 +47,9 @@ def head(title="", description="", image="", config={}):
|
|||
favicon = config.get("favicon", "/icon/internet")
|
||||
|
||||
html = """
|
||||
|
||||
<head>
|
||||
|
||||
<!-- 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. -->
|
||||
|
@ -58,8 +63,8 @@ def head(title="", description="", image="", config={}):
|
|||
<!-- Now meta tags for social media -->
|
||||
|
||||
<meta property="og:site_name" content=\""""+config.get("title", "My Website")+"""">
|
||||
<meta property="og:title" content=\""""+title+"""">
|
||||
<meta property="og:description" content=\""""+description+"""">
|
||||
<meta property="og:title" content=\""""+Simplify(title, False)+"""">
|
||||
<meta property="og:description" content=\""""+Simplify(description, False)+"""">
|
||||
<meta property="og:image" content=\""""+image+"""">
|
||||
|
||||
<!-- This meta tag is needed for pretty rendering on phones -->
|
||||
|
@ -69,6 +74,7 @@ def head(title="", description="", image="", config={}):
|
|||
<body>
|
||||
|
||||
"""
|
||||
|
||||
|
||||
return html
|
||||
|
||||
|
@ -103,14 +109,35 @@ def accounts():
|
|||
try:
|
||||
with open(folder+"/"+account) as o:
|
||||
data = json.load(o)
|
||||
data["username"] = account.replace(".json","")
|
||||
accounts[account.replace(".json","")] = data
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
|
||||
return accounts
|
||||
|
||||
|
||||
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
|
||||
|
||||
if user not in Accounts:
|
||||
return True
|
||||
|
||||
if moderator == user:
|
||||
return True
|
||||
|
||||
def articles(tab):
|
||||
|
||||
folder = Set.Folder()+"/tabs/"+tab
|
||||
|
@ -209,7 +236,7 @@ def previewsToSize(text):
|
|||
# given article.
|
||||
|
||||
# A thousand character article is about 4 articles.
|
||||
return int(round(len(text)/2500))
|
||||
return len(text)/2200
|
||||
|
||||
|
||||
###
|
||||
|
@ -226,6 +253,8 @@ def MainPage(server):
|
|||
)
|
||||
|
||||
|
||||
html = html + LoginButton(server)
|
||||
|
||||
html = html + """
|
||||
|
||||
<br>
|
||||
|
@ -268,6 +297,8 @@ def MainPage(server):
|
|||
html = html + '</div>'
|
||||
|
||||
html = html + Footer()
|
||||
|
||||
html = html + LoginButton(server)
|
||||
|
||||
send(server, html, 200)
|
||||
|
||||
|
@ -292,6 +323,7 @@ def ListPage(server, tab):
|
|||
config = config
|
||||
)
|
||||
|
||||
|
||||
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
|
||||
|
||||
# Scroll thingie
|
||||
|
@ -342,11 +374,13 @@ def ListPage(server, tab):
|
|||
if To < len(Articles)-1:
|
||||
html = html + Button(str(page+1), tab+"?page="+str(page+1), "right")
|
||||
|
||||
|
||||
html = html + LoginButton(server)
|
||||
|
||||
send(server, html, 200)
|
||||
|
||||
def ArticlePage(server, url):
|
||||
|
||||
user = validate(server.cookie)
|
||||
|
||||
if url.endswith(".md"):
|
||||
url = url.replace(".md", "")
|
||||
|
@ -363,6 +397,7 @@ def ArticlePage(server, url):
|
|||
config = config
|
||||
)
|
||||
|
||||
|
||||
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"))
|
||||
|
||||
|
@ -405,8 +440,41 @@ def ArticlePage(server, url):
|
|||
html = html + markdown.convert(f+"/tabs/"+tab+"/"+article+"/text.md")
|
||||
html = html + '</div>'
|
||||
|
||||
html = html + '</div>'
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
# 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">'
|
||||
|
@ -420,6 +488,7 @@ def ArticlePage(server, url):
|
|||
|
||||
suggestions = suggestedArticles(server.cookie, random=True)
|
||||
toomuch = previewsToSize(open(f+"/tabs/"+tab+"/"+article+"/text.md").read())
|
||||
toomuch += commentsTextLength
|
||||
|
||||
for n, title in enumerate(suggestions):
|
||||
|
||||
|
@ -434,6 +503,7 @@ def ArticlePage(server, url):
|
|||
html = html + ""
|
||||
|
||||
|
||||
html = html + LoginButton(server)
|
||||
|
||||
send(server, html, 200)
|
||||
|
||||
|
@ -451,6 +521,7 @@ def AccountPage(server, account):
|
|||
config = config
|
||||
)
|
||||
|
||||
|
||||
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
|
||||
|
||||
# Name and bio
|
||||
|
@ -506,9 +577,62 @@ def AccountPage(server, account):
|
|||
html = html + '<div class="dark_box">'
|
||||
html = html + '<center>' + User(username) + '</center>\n'
|
||||
html = html + '</div>'
|
||||
|
||||
|
||||
|
||||
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 + """
|
||||
|
||||
<form action="do_login">
|
||||
|
||||
<img style="vertical-align: middle" src="/icon/user">
|
||||
<input class="button" style="width:36.5%" maxlength="500" id="user_name" required="" name="user_name" pattern="[A-Za-z0-9]*" placeholder="Username..."></input>
|
||||
<br>
|
||||
<img style="vertical-align: middle" src="/icon/lock">
|
||||
<input class="button" style="width:36.5%" 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/unlock">
|
||||
Login
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
<br>
|
||||
|
||||
Don't have an account? <a href="/register">Register</a>.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
html = html + '</center>'
|
||||
|
||||
send(server, html, 200)
|
||||
|
||||
|
||||
###
|
||||
|
||||
def Button(text, link, icon="", image=""):
|
||||
|
@ -593,7 +717,10 @@ def User(username, stretch=False):
|
|||
if not avatar: avatar = "/pictures/monkey.png"
|
||||
username = Safe(username)
|
||||
title = Safe(account.get("title", username))
|
||||
html = '<img style="height:50px;vertical-align: middle" src="'+avatar+'"> <a href="/account/'+username+'">'+title+'</a></center>\n'
|
||||
if account:
|
||||
html = '<img style="height:50px;vertical-align: middle" src="'+avatar+'"> <a href="/account/'+username+'">'+title+'</a></center>\n'
|
||||
else:
|
||||
html = '<img style="height:50px;vertical-align: middle" src="'+avatar+'"> '+title+'</center>\n'
|
||||
|
||||
return html
|
||||
|
||||
|
@ -659,3 +786,279 @@ def Graph(server, url):
|
|||
|
||||
|
||||
send(server, html, 200)
|
||||
|
||||
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+'">'
|
||||
|
||||
|
||||
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):
|
||||
|
||||
html = '<div class="dark_box" id="comment_'+str(n)+'">'
|
||||
|
||||
account = comment.get("username", "Anonymous User")
|
||||
html = html + User(account) + '<br>\n'
|
||||
|
||||
if request:
|
||||
html = html + '<center><sup><b> Pending Approval </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>'
|
||||
|
||||
html = html + '<a class="button" href="/">'
|
||||
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">'
|
||||
html = html + '<img style="vertical-align: middle" src="/icon/user"> Login'
|
||||
html = html + '</a>'
|
||||
|
||||
else:
|
||||
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"))
|
||||
html = html + '</a>'
|
||||
|
||||
html = html + '</div>'
|
||||
|
||||
return html
|
||||
|
||||
|
||||
###
|
||||
|
||||
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()
|
||||
|
||||
if username not in Accounts or hashed != Accounts[username].get("password"):
|
||||
Redirect(server, "/login?wrong=username")
|
||||
|
||||
else:
|
||||
|
||||
account = Accounts[username]
|
||||
|
||||
if "sessions" not in account:
|
||||
account["sessions"] = {}
|
||||
|
||||
account["sessions"][server.cookie] = server.headers.get("User-Agent")
|
||||
folder = Set.Folder()+"/accounts"
|
||||
with open(folder+"/"+username+".json", "w") as save:
|
||||
json.dump(account, save, indent=4)
|
||||
|
||||
Redirect(server, "/")
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
place = "requests"
|
||||
|
||||
|
||||
if not user:
|
||||
comment["cookie"] = server.cookie
|
||||
|
||||
|
||||
if not number:
|
||||
article["comments"][place].append(comment)
|
||||
number = len(article["comments"]["comments"])-1
|
||||
else:
|
||||
number = int(number)
|
||||
if moderates(user.get("username"), article["comments"]["comments"][number]["username"]):
|
||||
article["comments"]["comments"][number] = comment
|
||||
|
||||
|
||||
try:
|
||||
with open(metadata, "w") as save:
|
||||
json.dump(article, save, indent=4)
|
||||
except:
|
||||
pass
|
||||
|
||||
Redirect(server, url+"#comment_"+str(number))
|
||||
|
|
|
@ -71,6 +71,15 @@ class handler(BaseHTTPRequestHandler):
|
|||
if "?" in url: url = url[:url.find("?")]
|
||||
Render.AccountPage(self, url)
|
||||
|
||||
elif self.path[1:].startswith("login"):
|
||||
Render.LoginPage(self)
|
||||
|
||||
elif self.path[1:].startswith("comment"):
|
||||
Render.DoComment(self)
|
||||
|
||||
elif self.path[1:].startswith("do_login"):
|
||||
Render.Login(self)
|
||||
|
||||
elif self.path.startswith("/graph/"):
|
||||
url = self.path[6:]
|
||||
if "?" in url: url = url[:url.find("?")]
|
||||
|
@ -102,7 +111,8 @@ class handler(BaseHTTPRequestHandler):
|
|||
Render.headers(self, 200)
|
||||
self.wfile.write(f)
|
||||
|
||||
|
||||
else:
|
||||
Render.Redirect(self, "/")
|
||||
|
||||
config = Set.Load()
|
||||
PORT = config.get("port", 8080)
|
||||
|
|
Loading…
Add table
Reference in a new issue