Publishing from Web UI
This commit is contained in:
parent
151efdfc8a
commit
f38e020de4
2 changed files with 421 additions and 7 deletions
|
@ -3,6 +3,7 @@
|
|||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import random
|
||||
import hashlib
|
||||
import urllib.parse
|
||||
|
@ -151,6 +152,7 @@ def accounts():
|
|||
|
||||
return accounts
|
||||
|
||||
|
||||
def validate(cookie):
|
||||
|
||||
Accounts = accounts()
|
||||
|
@ -188,6 +190,31 @@ def rank(account, Accounts=None):
|
|||
|
||||
return 1 + rank(Accounts[account].get("invited_by"), Accounts)
|
||||
|
||||
def editsIn(account, tab):
|
||||
|
||||
# Determents whether the user
|
||||
# can edit an article.
|
||||
|
||||
Accounts = accounts()
|
||||
|
||||
# If the user is not registered
|
||||
# per cannot edit anything.
|
||||
if account not in Accounts:
|
||||
return False
|
||||
|
||||
# If the user is the owner of the
|
||||
# site, per can edit everything.
|
||||
if rank(account, Accounts) == 0:
|
||||
return True
|
||||
|
||||
# Not all users can edit in all
|
||||
# tabs.
|
||||
user = Accounts[account]
|
||||
if tab in user.get("editsIn", []):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def articles(tab):
|
||||
|
||||
folder = Set.Folder()+"/tabs/"+tab
|
||||
|
@ -354,6 +381,8 @@ def MainPage(server):
|
|||
|
||||
def ListPage(server, tab):
|
||||
|
||||
user = validate(server.cookie)
|
||||
|
||||
Tabs = tabs()
|
||||
Articles = articles(tab)
|
||||
config = Set.Load()
|
||||
|
@ -376,6 +405,9 @@ def ListPage(server, tab):
|
|||
|
||||
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
|
||||
|
||||
if editsIn(user.get("username", ""), tab):
|
||||
html = html + Button("New Post", "/editor?tab="+tab, icon="new")
|
||||
|
||||
# Scroll thingie
|
||||
if len(Articles) > Buffer:
|
||||
if page > 0:
|
||||
|
@ -456,8 +488,19 @@ def ArticlePage(server, url):
|
|||
html = html + '<div class="middle_section_article">'
|
||||
|
||||
html = html + '<div class="dark_box">'
|
||||
|
||||
# Edit button
|
||||
|
||||
if editsIn(user.get("username"), tab) and moderates(user.get("username"), Articles.get(article, {}).get("author", "")):
|
||||
|
||||
html = html + '<div class="login_fixed">'
|
||||
html = html + Button("Edit", "/editor?tab="+tab+"&name="+article, icon="edit")
|
||||
html = html + '</div><br><br><br>'
|
||||
|
||||
html = html +"<center><h1>"+Articles.get(article, {}).get("title", article)+"</h1></center>"
|
||||
|
||||
|
||||
|
||||
# Page author
|
||||
author = Articles.get(article, {}).get("author", "")
|
||||
if author:
|
||||
|
@ -578,6 +621,8 @@ def ArticlePage(server, url):
|
|||
|
||||
def AccountPage(server, account):
|
||||
|
||||
user = validate(server.cookie)
|
||||
|
||||
config = Set.Load()
|
||||
Accounts = accounts()
|
||||
Tabs = tabs()
|
||||
|
@ -641,8 +686,6 @@ def AccountPage(server, account):
|
|||
|
||||
# It could be mastodon url and not handle.
|
||||
|
||||
# https://social.trom.tf/@morsmortium
|
||||
|
||||
try:
|
||||
if "/" in mastodon:
|
||||
mastodon = mastodon.replace("https://", "").replace("http://", "")
|
||||
|
@ -692,6 +735,46 @@ def AccountPage(server, account):
|
|||
html = html + '</div>'
|
||||
|
||||
|
||||
# Validating this account
|
||||
|
||||
validates = []
|
||||
if user.get("username", "") != account and moderates(user.get("username", ""), account):
|
||||
for tab in Tabs:
|
||||
if editsIn(user.get("username", ""), tab):
|
||||
validates.append(tab)
|
||||
if validates:
|
||||
html = html + '<div class="dark_box"><center>'
|
||||
html = html + "You can grant publication rights to this account.<br>"
|
||||
|
||||
html = html + '</center><form action="/grant_publication_rights">'
|
||||
|
||||
html = html + '<input type="hidden" name="account" value="'+account+'">'
|
||||
|
||||
for tab in validates:
|
||||
checked = ""
|
||||
if editsIn(account, tab):
|
||||
checked = 'checked=""'
|
||||
html = html + '<div class="button">'
|
||||
html = html + '<input type="checkbox" '+checked+' name="'+tab+'">' + Tabs[tab].get("title", tab)
|
||||
html = html + '</div>'
|
||||
|
||||
|
||||
html = html + """
|
||||
|
||||
<br>
|
||||
|
||||
<button class="button" type="submit">
|
||||
<img style="vertical-align: middle" src="/icon/ok">
|
||||
Apply
|
||||
</button>
|
||||
|
||||
<br>
|
||||
|
||||
"""
|
||||
html = html + '</div>'
|
||||
|
||||
|
||||
|
||||
# Posts by this account
|
||||
|
||||
html = html + '<div class="flexity">'
|
||||
|
@ -920,7 +1003,7 @@ def SettingsPage(server):
|
|||
<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">
|
||||
<img style="vertical-align: middle" src="/icon/image_link">
|
||||
<input class="button" style="width:90%" maxlength="200" name="avatar" placeholder="Link To Profile Picture" value='"""+user.get("avatar", "")+"""'>
|
||||
|
||||
<br>
|
||||
|
@ -1033,6 +1116,112 @@ def SettingsPage(server):
|
|||
|
||||
send(server, html, 200)
|
||||
|
||||
def EditorPage(server):
|
||||
|
||||
user = validate(server.cookie)
|
||||
|
||||
# Authorization check
|
||||
if not user:
|
||||
AccessDenied(server)
|
||||
return
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
Tabs = tabs()
|
||||
f = Set.Folder()
|
||||
|
||||
# Generating <head>
|
||||
html = head(title = "Editor",
|
||||
description = "Editor",
|
||||
config = config
|
||||
)
|
||||
|
||||
html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
|
||||
|
||||
tab = server.parsed.get("tab", [""])[0]
|
||||
name = server.parsed.get("name", [""])[0]
|
||||
|
||||
if tab in Tabs:
|
||||
Articles = articles(tab)
|
||||
else:
|
||||
Articles = {}
|
||||
|
||||
url = tab+"/"+name
|
||||
article = Articles.get(name, {})
|
||||
|
||||
if article:
|
||||
try:
|
||||
text = open(f+"/tabs/"+tab+"/"+name+"/text.md")
|
||||
text = text.read()
|
||||
except:
|
||||
text = ""
|
||||
else:
|
||||
text = ""
|
||||
|
||||
html = html + """
|
||||
|
||||
<div class="middle_section_article">
|
||||
|
||||
<div class="dark_box">
|
||||
|
||||
<form action="do_edit" method="post">
|
||||
|
||||
"""
|
||||
if tab in Tabs:
|
||||
html = html + '<img style="vertical-align: middle" src="/icon/' + Tabs.get(tab, {}).get("icon", "folder")+ '"> '
|
||||
html = html + Tabs.get(tab, {}).get("title", tab)
|
||||
html = html + '<br>'
|
||||
html = html + '<input type="hidden" name="tab" value="'+tab+'">'
|
||||
|
||||
else:
|
||||
html = html + """
|
||||
<img style="vertical-align: middle" src="/icon/folder">
|
||||
<input class="button" style="width:90%" required="" name="tab" placeholder="Category ( Tab )">
|
||||
"""
|
||||
|
||||
if name:
|
||||
html = html + '<input type="hidden" name="name" value="'+name+'">'
|
||||
|
||||
html = html + """
|
||||
|
||||
<img style="vertical-align: middle" src="/icon/scene">
|
||||
<input class="button" style="width:90%" required="" name="title" placeholder="Title" value='"""+article.get("title", "")+"""'>
|
||||
|
||||
<img style="vertical-align: middle" src="/icon/image_link">
|
||||
<input class="button" style="width:90%" name="thumbnail" placeholder="Link To Thumbnail ( Optional )" value='"""+article.get("thumbnail", "")+"""'>
|
||||
|
||||
<img style="vertical-align: middle" src="/icon/copy_file">
|
||||
<input class="button" style="width:90%" list="Licenses" name="license" placeholder="License ( Optional )" value='"""+article.get("license", "")+"""'>
|
||||
<datalist id="Licenses">
|
||||
"""
|
||||
for l in Licenses:
|
||||
html = html + '<option value="'+l+'">'+l+'</option>'
|
||||
html = html + """
|
||||
</datalist>
|
||||
|
||||
<img style="vertical-align: middle" src="/icon/mus">
|
||||
<input class="button" style="width:90%" name="recording" placeholder="Link To Sound Recording ( Optional )" value='"""+article.get("recording", "")+"""'>
|
||||
|
||||
|
||||
<br>
|
||||
<center>
|
||||
<textarea class="toot" rows="30" style="width:95%" required="" name="text" placeholder="Text of your post">"""+Safe(text)+"""</textarea>
|
||||
<br>
|
||||
<textarea class="toot" rows="5" style="width:95%" name="description" placeholder="Description ( Optional )">"""+article.get("description", "")+"""</textarea>
|
||||
</center>
|
||||
|
||||
<button class="button" type="submit">
|
||||
<img style="vertical-align: middle" src="/icon/scene_new">
|
||||
Publish
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
"""
|
||||
|
||||
html = html + LoginButton(server)
|
||||
|
||||
send(server, html, 200)
|
||||
|
||||
###
|
||||
|
||||
|
@ -1624,6 +1813,81 @@ def UpdateAccount(server):
|
|||
|
||||
Redirect(server, "/settings")
|
||||
|
||||
def UpdatePublicationRights(server):
|
||||
|
||||
user = validate(server.cookie)
|
||||
|
||||
# Authorization check
|
||||
if not user:
|
||||
AccessDenied(server)
|
||||
return
|
||||
|
||||
Accounts = accounts()
|
||||
|
||||
account = server.parsed.get("account", [""])[0]
|
||||
|
||||
if account not in Accounts:
|
||||
NotFound(server)
|
||||
return
|
||||
|
||||
if not moderates(user.get("username", ""), account):
|
||||
AccessDenied(server)
|
||||
return
|
||||
|
||||
Tabs = tabs()
|
||||
Account = Accounts[account]
|
||||
if "editsIn" not in Account:
|
||||
Account["editsIn"] = []
|
||||
|
||||
granted = []
|
||||
revoked = []
|
||||
|
||||
for tab in Tabs:
|
||||
|
||||
if not editsIn(user.get("username", ""), tab):
|
||||
AccessDenied(server)
|
||||
return
|
||||
|
||||
tabOn = server.parsed.get(tab, [""])[0]
|
||||
if tabOn and tab not in Account["editsIn"]:
|
||||
Account["editsIn"].append(tab)
|
||||
granted.append(tab)
|
||||
|
||||
elif not tabOn and tab in Account["editsIn"]:
|
||||
Account["editsIn"].remove(tab)
|
||||
revoked.append(tab)
|
||||
|
||||
f = Set.Folder()
|
||||
folder = f+"/accounts"
|
||||
with open(folder+"/"+account+".json", "w") as save:
|
||||
json.dump(Account, save, indent=4)
|
||||
|
||||
Redirect(server, "/account/"+account)
|
||||
|
||||
# Notification
|
||||
|
||||
text = "@"+user.get("username", "")+" "
|
||||
if granted:
|
||||
text = text + "granted you publication rights in: "
|
||||
for n, i in enumerate(granted):
|
||||
text = text + i
|
||||
if n != len(granted)-1:
|
||||
text = text + ", "
|
||||
|
||||
if revoked:
|
||||
text = text + " and "
|
||||
|
||||
if revoked:
|
||||
text = text + "revoked your publication rights in: "
|
||||
for n, i in enumerate(revoked):
|
||||
text = text + i
|
||||
if n != len(revoked)-1:
|
||||
text = text + ", "
|
||||
|
||||
text = text + ".<br>"
|
||||
|
||||
Notify(account, "/account/"+account, text)
|
||||
|
||||
def DoComment(server):
|
||||
|
||||
user = validate(server.cookie)
|
||||
|
@ -1744,6 +2008,102 @@ def DoComment(server):
|
|||
url+placeRedirect+str(number),
|
||||
"@"+Safe(username)+" mentioned you: <br><br> <b>"+article.get("title", "")+"</b><br><br><i>"+Safe(text[:200])+"</i>")
|
||||
|
||||
def Publish(server):
|
||||
|
||||
user = validate(server.cookie)
|
||||
|
||||
# Authorization check
|
||||
if not user:
|
||||
AccessDenied(server)
|
||||
return
|
||||
|
||||
config = Set.Load()
|
||||
Tabs = tabs()
|
||||
|
||||
f = Set.Folder()
|
||||
|
||||
# Getting data to create
|
||||
tab = server.parsed.get("tab", [""])[0]
|
||||
name = server.parsed.get("name", [""])[0]
|
||||
text = server.parsed.get("text", [""])[0]
|
||||
title = server.parsed.get("title", [""])[0]
|
||||
description = server.parsed.get("description", [""])[0]
|
||||
thumbnail = server.parsed.get("thumbnail", [""])[0]
|
||||
License = server.parsed.get("license", [""])[0]
|
||||
recording = server.parsed.get("recording", [""])[0]
|
||||
|
||||
# If this tab doesn't exist, this is an error.
|
||||
if tab not in Tabs:
|
||||
AccessDenied(server)
|
||||
return
|
||||
|
||||
# Checking if the user has rights to post in here.
|
||||
if not editsIn(user.get("username"), tab):
|
||||
AccessDenied(server)
|
||||
return
|
||||
|
||||
Articles = articles(tab)
|
||||
|
||||
if not name:
|
||||
name = Simplify(title)
|
||||
|
||||
# Reading the file
|
||||
|
||||
if name in Articles:
|
||||
metadata = Articles[name]
|
||||
|
||||
else:
|
||||
metadata = {
|
||||
"title":"",
|
||||
"timestamp":time.time(),
|
||||
"description":"",
|
||||
"author":user.get("username", ""),
|
||||
"thumbnail":"",
|
||||
"license":"",
|
||||
"views":{
|
||||
"amount":0,
|
||||
"viewers":[],
|
||||
"dates":{}
|
||||
},
|
||||
"recording":"",
|
||||
"comments":{
|
||||
"comments":[],
|
||||
"requests":[]
|
||||
}
|
||||
}
|
||||
|
||||
# Checking if the user can edit the posts of the
|
||||
# author of this article.
|
||||
|
||||
if not moderates(user.get("username"), metadata.get("author", "")):
|
||||
AccessDenied(server)
|
||||
return
|
||||
|
||||
metadata["title"] = title
|
||||
metadata["description"] = description
|
||||
metadata["license"] = License
|
||||
metadata["recording"] = recording
|
||||
metadata["thumbnail"] = thumbnail
|
||||
|
||||
# Save the changes
|
||||
|
||||
try:
|
||||
os.makedirs(f+"/tabs/"+tab+"/"+name)
|
||||
except:pass
|
||||
|
||||
with open(f+"/tabs/"+tab+"/"+name+"/metadata.json", "w") as save:
|
||||
json.dump(metadata, save, indent=4)
|
||||
|
||||
with open(f+"/tabs/"+tab+"/"+name+"/text.md", "w") as save:
|
||||
|
||||
# Enabling HTML embedding only for the owner
|
||||
if rank(user.get("username", "")) == 0:
|
||||
save.write(text)
|
||||
else:
|
||||
save.write(Safe(text))
|
||||
|
||||
Redirect(server, "/"+tab+"/"+name)
|
||||
|
||||
|
||||
def DeleteComment(server):
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ class handler(BaseHTTPRequestHandler):
|
|||
q = clr["tbbl"]
|
||||
b = clr["tbbl"]
|
||||
|
||||
#print(dir(self))
|
||||
|
||||
try:
|
||||
if self.newview:
|
||||
|
@ -58,14 +57,63 @@ class handler(BaseHTTPRequestHandler):
|
|||
logfile.write((toprint+"\n").encode('utf-8'))
|
||||
logfile.close()
|
||||
|
||||
def do_POST(self):
|
||||
|
||||
self.path = self.path.replace("/..", "/")
|
||||
self.path = self.path.replace("%27", "'")
|
||||
|
||||
self.cookie = self.headers.get("Cookie")
|
||||
self.newview = False
|
||||
if self.cookie: self.cookie = self.cookie[-50:]
|
||||
|
||||
# Failing early to make sure that nobody will
|
||||
# try attacking this part of the server.
|
||||
|
||||
commands = ["do_edit"]
|
||||
found = False
|
||||
for i in commands:
|
||||
if i in self.path:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
Render.AccessDenied(self)
|
||||
return
|
||||
|
||||
|
||||
# Not allowing more than 1 MB for the sake of
|
||||
# network. A full book of Sheiny The Hacker
|
||||
# which was posted on blenderdumbass.org is
|
||||
# only about 160 KB. 1MB should be plenty.
|
||||
|
||||
length = int(self.headers["Content-Length"])
|
||||
if length < 1000000:
|
||||
try:
|
||||
text = self.rfile.read(length).decode("utf-8")
|
||||
except:
|
||||
Render.AccessDenied(self)
|
||||
return
|
||||
|
||||
else:
|
||||
Render.AccessDenied(self)
|
||||
return
|
||||
|
||||
#parsed_url = urllib.parse.urlparse(text)
|
||||
self.parsed = urllib.parse.parse_qs(text)
|
||||
|
||||
if self.path[1:].startswith("do_edit"):
|
||||
Render.Publish(self)
|
||||
|
||||
else:
|
||||
Render.NotFound(self)
|
||||
|
||||
def do_GET(self):
|
||||
|
||||
self.path = self.path.replace("/..", "/")
|
||||
self.path = self.path.replace("%27", "'")
|
||||
|
||||
parsed_url = urllib.parse.urlparse(self.path)
|
||||
|
||||
self.parsed = urllib.parse.parse_qs(parsed_url.query)
|
||||
|
||||
self.cookie = self.headers.get("Cookie")
|
||||
if self.cookie: self.cookie = self.cookie[-50:]
|
||||
|
||||
|
@ -100,6 +148,9 @@ class handler(BaseHTTPRequestHandler):
|
|||
elif self.path[1:].startswith("login"):
|
||||
Render.LoginPage(self)
|
||||
|
||||
elif self.path[1:].startswith("editor"):
|
||||
Render.EditorPage(self)
|
||||
|
||||
elif self.path[1:].startswith("register"):
|
||||
Render.RegisterPage(self)
|
||||
|
||||
|
@ -115,6 +166,9 @@ class handler(BaseHTTPRequestHandler):
|
|||
elif self.path[1:].startswith("update_account"):
|
||||
Render.UpdateAccount(self)
|
||||
|
||||
elif self.path[1:].startswith("grant_publication_rights"):
|
||||
Render.UpdatePublicationRights(self)
|
||||
|
||||
elif self.path[1:].startswith("create_invite"):
|
||||
Render.CreateInvite(self)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue