2024-11-19 13:47:45 +02:00
# AGPL 3 or any later version
# (C) J.Y.Amihud ( Blender Dumbass )
2024-11-19 19:04:03 +02:00
import os
import json
2024-11-28 05:30:15 +02:00
import time
2024-12-01 02:59:19 +02:00
import email
2024-11-23 00:48:29 +02:00
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
2024-11-20 14:40:56 +02:00
from datetime import datetime
2024-11-19 19:04:03 +02:00
2024-11-19 13:47:45 +02:00
from modules import Set
2024-12-05 15:54:16 +02:00
from modules import API
2024-11-20 14:40:56 +02:00
from modules import markdown
2024-11-19 19:04:03 +02:00
from modules . Common import *
2024-11-19 13:47:45 +02:00
2024-12-01 02:59:19 +02:00
2024-11-29 16:48:40 +02:00
KnownCookies = [ ]
2024-11-30 14:27:26 +02:00
RecentArticles = { }
2024-11-30 19:43:35 +02:00
RefferedArticles = { }
2024-12-01 02:59:19 +02:00
ProblematicRefreshes = [ ]
2024-11-29 16:48:40 +02:00
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 "
2024-11-27 17:02:54 +02:00
if " /rss " in path or " .rss " in path :
return " application/rss+xml "
2024-11-19 13:47:45 +02:00
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 ) :
2024-11-19 19:04:03 +02:00
# 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-19 19:04:03 +02:00
2024-11-25 00:04:12 +02:00
if not server . cookie :
2024-11-30 22:03:24 +02:00
cookie = RandString ( 200 )
2024-11-29 16:48:40 +02:00
KnownCookies . append ( cookie )
server . send_header ( " Set-Cookie " , " temp_id= " + cookie )
2024-11-19 19:04:03 +02:00
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
2024-11-19 19:04:03 +02:00
favicon = config . get ( " favicon " , " /icon/internet " )
2024-11-19 13:47:45 +02:00
2024-11-19 19:04:03 +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 - - >
2024-11-19 19:04:03 +02:00
< 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-27 17:02:54 +02:00
< meta property = " og:title " content = \""" " +Safe(title)+ """ " >
< meta property = " og:description " content = \""" " +Safe(description)+ """ " >
2024-11-19 13:47:45 +02:00
< meta property = " og:image " content = \""" " +image+ """ " >
2024-11-20 14:40:56 +02:00
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+ """ " >
"""
2024-11-27 17:25:07 +02:00
# Tor tags.
tor = config . get ( " tor " , " " )
if tor :
if not tor . startswith ( " http:// " ) : tor = " http:// " + tor
html = html + ' <meta http-equiv= " onion-location " content= " ' + tor + ' " /> '
2024-11-25 23:53:31 +02:00
html = html + """
2024-11-20 14:40:56 +02:00
< ! - - 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 " ) )
2024-11-19 19:04:03 +02:00
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
2024-11-20 14:40:56 +02:00
2024-11-23 00:48:29 +02:00
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 " , " " )
2024-11-23 00:48:29 +02:00
accounts [ account . replace ( " .json " , " " ) ] = data
except Exception as e :
print ( e )
pass
return accounts
2024-11-23 22:34:11 +02:00
2024-11-28 05:30:15 +02:00
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 00:48:29 +02:00
2024-11-23 22:34:11 +02:00
if user not in Accounts :
return True
2024-11-23 00:48:29 +02:00
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
2024-11-28 05:30:15 +02:00
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
2024-11-20 14:40:56 +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 )
2024-11-23 00:48:29 +02:00
data [ " tab " ] = tab
data [ " url " ] = " / " + tab + " / " + article
2024-11-20 14:40:56 +02:00
articles [ article ] = data
2024-11-23 00:48:29 +02:00
2024-11-20 14:40:56 +02:00
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 ) }
2024-11-19 19:04:03 +02:00
2024-11-20 14:40:56 +02:00
return articles
2024-11-23 00:48:29 +02:00
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
2024-12-04 14:25:58 +02:00
articles [ data [ " url " ] ] = data
2024-11-23 00:48:29 +02:00
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-23 00:48:29 +02:00
2024-11-20 14:40:56 +02:00
###
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 )
2024-11-19 19:04:03 +02:00
html = html + """
< br >
< br >
< center >
< img src = \""" " +config.get( " favicon " , " icon/internet " )+ """ " alt= " [ LOGO ] " style= " height : 150 px ; vertical - align : middle " >
< br >
< br >
< h2 > """ +config.get( " title " , " My Website " )+ """ < / h2 >
""" +config.get( " tagline " , " " )+ """
< br >
< br >
2024-11-29 16:20:48 +02:00
< form action = " /search " >
2024-11-30 22:03:24 +02:00
< input name = " text " class = " button " title = " Search text " placeholder = " Search... " >
2024-11-29 16:20:48 +02:00
2024-11-30 22:03:24 +02:00
< button title = " Start the search " class = " button " type = " submit " >
< img alt = " [icon search] " style = " vertical-align: middle " src = " /icon/search " >
2024-11-29 16:20:48 +02:00
Search
< / button >
2024-11-19 19:04:03 +02:00
2024-11-29 16:20:48 +02:00
< / form >
< br >
2024-11-19 19:04:03 +02:00
"""
Tabs = tabs ( )
for tab in Tabs :
html = html + Button ( Tabs [ tab ] . get ( " title " , tab ) ,
" / " + tab ,
Tabs [ tab ] . get ( " icon " , " folder " ) )
2024-11-20 14:40:56 +02:00
html = html + " </center> "
2024-11-23 00:48:29 +02:00
# 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> '
2024-11-20 14:40:56 +02:00
html = html + Footer ( )
2024-11-23 22:34:11 +02:00
html = html + LoginButton ( server )
2024-11-20 14:40:56 +02:00
send ( server , html , 200 )
def ListPage ( server , tab ) :
2024-11-28 05:30:15 +02:00
user = validate ( server . cookie )
2024-11-20 14:40:56 +02:00
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
2024-11-20 14:40:56 +02:00
html = html + Button ( config . get ( " title " , " My Website " ) , " / " , image = config . get ( " favicon " , " /icon/internet " ) )
2024-11-28 05:30:15 +02:00
if editsIn ( user . get ( " username " , " " ) , tab ) :
html = html + Button ( " New Post " , " /editor?tab= " + tab , icon = " new " )
2024-11-20 14:40:56 +02:00
# 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
2024-11-23 00:48:29 +02:00
html = html + ArticlePreview ( Articles [ article ] , Tabs , server . cookie )
2024-11-20 14:40:56 +02:00
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 )
2024-11-20 14:40:56 +02:00
send ( server , html , 200 )
def ArticlePage ( server , url ) :
2024-11-23 22:34:11 +02:00
user = validate ( server . cookie )
2024-11-30 19:43:35 +02:00
referrer = server . headers . get ( " referer " , " " )
2024-11-20 14:40:56 +02:00
if url . endswith ( " .md " ) :
url = url . replace ( " .md " , " " )
2024-11-30 14:27:26 +02:00
# Recording when was the last time
# the article loaded.
RecentArticles [ " / " + url ] = time . time ( )
2024-11-30 19:43:35 +02:00
RefferedArticles [ " / " + url ] = referrer
2024-11-30 14:27:26 +02:00
2024-11-20 14:40:56 +02:00
config = Set . Load ( )
2024-11-27 17:02:54 +02:00
tab , article , * rest = url . split ( " / " )
2024-11-20 14:40:56 +02:00
Tabs = tabs ( )
Articles = articles ( tab )
2024-11-23 00:48:29 +02:00
f = Set . Folder ( )
2024-11-20 14:40:56 +02:00
# Generating <head>
2024-11-23 00:48:29 +02:00
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-20 14:40:56 +02:00
)
2024-11-23 22:34:11 +02:00
2024-11-20 14:40:56 +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 " > '
2024-11-28 05:30:15 +02:00
# 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> '
2024-11-20 14:40:56 +02:00
html = html + " <center><h1> " + Articles . get ( article , { } ) . get ( " title " , article ) + " </h1></center> "
2024-11-28 05:30:15 +02:00
2024-11-20 14:40:56 +02:00
# Page author
author = Articles . get ( article , { } ) . get ( " author " , " " )
if author :
html = html + ' <center> ' + User ( author ) + ' </center> '
2024-11-29 19:56:59 +02:00
timestamp = Articles . get ( article , { } ) . get ( " timestamp " , " " )
arttime = str ( datetime . fromtimestamp ( timestamp ) . strftime ( " % B %d , % Y " ) )
html = html + " <br><small><center> " + str ( arttime ) + " </center></small> "
2024-11-20 14:40:56 +02:00
# 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>
2024-11-30 19:43:35 +02:00
< br > < br > """
referrers = Articles . get ( article , { } ) . get ( " views " , { } ) . get ( " referrers " , { } )
if referrers :
for referrer in referrers :
2024-11-30 22:03:24 +02:00
# Filtering out probably bad links.
2024-12-02 16:43:03 +02:00
if ( " https:// " in referrer and " .onion " not in referrer ) or rank ( user . get ( " username " , " " ) ) == 0 :
2024-11-30 22:03:24 +02:00
html = html + Safe ( referrer ) + " : 👁 " + str ( referrers [ referrer ] ) + " <br> \n "
2024-11-30 19:43:35 +02:00
2024-11-30 22:03:24 +02:00
2024-11-30 19:43:35 +02:00
html = html + """
2024-11-20 14:40:56 +02:00
< / details >
"""
html = html + ' </div> '
2024-11-27 00:33:53 +02:00
2024-12-04 14:25:58 +02:00
# Petition
petition = Articles . get ( article , { } ) . get ( " petition " , " " )
if petition :
2024-12-05 15:54:16 +02:00
petition_error = False
if petition . get ( " api " ) :
try :
API . Petition ( Articles [ article ] )
except :
petition_error = True
2024-12-04 14:25:58 +02:00
html = html + ' <div class= " dark_box " > <center> '
2024-12-05 15:54:16 +02:00
html = html + ' <h2> '
2024-12-04 14:25:58 +02:00
html = html + ' Petition</h2></center> '
try :
frac = petition . get ( " signed " , 0 ) / int ( petition . get ( " goal " , 1 ) )
except :
frac = 0
html = html + ProgressBar ( frac )
html = html + " <br><center> " + str ( petition . get ( " signed " , 0 ) ) + " / " + Safe ( str ( petition . get ( " goal " , 1 ) ) ) + " Signatures "
2024-12-05 15:54:16 +02:00
# Last update
if petition . get ( " api " ) :
lastUpdate = petition . get ( " api " , { } ) . get ( " timestamp " , { } )
nowTime = time . time ( )
html = html + ' <br><br><small>Last updated: ' + TimeDifference ( lastUpdate , nowTime ) + ' </small><br><br> '
2024-12-04 14:25:58 +02:00
2024-12-05 15:54:16 +02:00
if not petition . get ( " api " ) :
2024-12-04 14:25:58 +02:00
2024-12-05 15:54:16 +02:00
html = html + """
2024-12-04 14:25:58 +02:00
2024-12-05 15:54:16 +02:00
< details >
< summary class = " button " >
< img alt = " [icon petition] " style = " vertical-align: middle " src = " /icon/petition " >
Sign
< / summary >
< form action = " /sign_petition " >
< input type = " hidden " name = " article " value = ' / " " " +tab+ " / " +article+ " " " ' >
< img style = " vertical-align: middle " src = " /icon/frase " >
< input class = " button " style = " width:90 % " required maxlength = " 200 " name = " email " placeholder = " Email " >
< button class = " button " type = " submit " >
< img style = " vertical-align: middle " src = " /icon/ok " >
Submit
< / button >
< / form >
< / details >
"""
else :
html = html + """
< details >
< summary class = " button " >
< img alt = " [icon petition] " style = " vertical-align: middle " src = " /icon/petition " >
Sign
< / summary >
< center >
< br >
This petition is signed by increasing the number of < br >
< i > """ +petition.get( " api " , {} ).get( " title " , " " )+ """ < / i >
< br > < br >
""" +Button( " Continue " , petition.get( " api " , {} ).get( " link " , " " ), " ok " )+ """
< / center >
"""
2024-12-04 14:25:58 +02:00
html = html + ' </div> '
# License
2024-11-27 00:33:53 +02:00
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> '
2024-11-20 14:40:56 +02:00
# Audio recording of the article
recording = Articles . get ( article , { } ) . get ( " recording " , " " )
if recording :
2024-11-30 14:27:26 +02:00
html = html + ' <div class= " dark_box " > <center> '
html = html + ' <b>If you are going to skim, better listen to it instead.</b><br><br> '
2024-11-20 14:40:56 +02:00
html = html + ' <audio controls= " controls " style= " min-width:100 % ; " src= " ' + recording + ' " ></audio> '
2024-11-30 14:27:26 +02:00
html = html + ' <br><br></center></div> '
2024-11-27 17:02:54 +02:00
2024-11-20 14:40:56 +02:00
html = html + ' <div class= " dark_box " > '
2024-11-23 00:48:29 +02:00
html = html + markdown . convert ( f + " /tabs/ " + tab + " / " + article + " /text.md " )
2024-11-20 14:40:56 +02:00
html = html + ' </div> '
2024-11-27 17:02:54 +02:00
# RSS
2024-11-29 16:20:48 +02:00
rsslink = " https:// " + config . get ( " domain " , " " ) + " /rss "
2024-11-30 22:03:24 +02:00
authorrsslink = rsslink + " ?author= " + Safe ( Articles . get ( article , { } ) . get ( " author " , " " ) )
2024-11-29 16:20:48 +02:00
2024-11-27 17:02:54 +02:00
html = html + """
2024-11-23 22:34:11 +02:00
2024-11-27 17:02:54 +02:00
< div class = " dark_box " >
< center >
2024-11-29 16:20:48 +02:00
< details >
< summary class = " button " >
< img style = " vertical-align: middle " src = " /icon/rss " >
Subscribe RSS
< / summary >
< br >
< img style = " vertical-align: middle " src = " /icon/user " >
< input class = " button " style = " width:50 % " value = ' " " " +authorrsslink+ " " " ' >
""" +Button( " Author " , authorrsslink, " link " )+ """
< br >
< img style = " vertical-align: middle " src = " /icon/internet " >
< input class = " button " style = " width:50 % " value = ' " " " +rsslink+ " " " ' >
""" +Button( " Website " , rsslink, " link " )+ """
2024-11-27 17:02:54 +02:00
2024-11-29 16:20:48 +02:00
< / details >
2024-11-27 17:02:54 +02:00
< / center >
< / 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-20 14:40:56 +02:00
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> '
2024-11-20 14:40:56 +02:00
# Thumbnail and suggestions
2024-11-23 00:48:29 +02:00
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
2024-11-23 00:48:29 +02:00
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-20 14:40:56 +02:00
2024-11-23 22:34:11 +02:00
html = html + LoginButton ( server )
2024-11-19 13:47:45 +02:00
send ( server , html , 200 )
2024-11-23 00:48:29 +02:00
def AccountPage ( server , account ) :
2024-11-28 05:30:15 +02:00
user = validate ( server . cookie )
2024-11-23 00:48:29 +02:00
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
2024-11-23 00:48:29 +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
2024-11-23 00:48:29 +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
2024-11-29 16:48:40 +02:00
if server . cookie in KnownCookies :
2024-11-25 23:53:31 +02:00
# 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-12-03 00:30:02 +02:00
if Accounts . get ( account , { } ) . get ( " email_verified " ) :
html = html + ' <img title= " Email Verified! " style= " vertical-align: middle " src= " /icon/ok " > '
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.
2024-11-27 21:57:44 +02:00
2024-11-25 23:53:31 +02:00
try :
if " / " in mastodon :
mastodon = mastodon . replace ( " https:// " , " " ) . replace ( " http:// " , " " )
mastodon = mastodon . split ( " / " ) [ 1 ] + " @ " + mastodon . split ( " / " ) [ 0 ]
2024-11-27 21:57:44 +02:00
if not mastodon . startswith ( " @ " ) : mastodon = " @ " + mastodon
2024-11-25 23:53:31 +02:00
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
2024-11-23 00:48:29 +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
2024-11-23 00:48:29 +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> '
2024-11-23 00:48:29 +02:00
html = html + ' </div> '
2024-11-28 05:30:15 +02:00
# 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> '
2024-11-23 00:48:29 +02:00
# 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 ]
2024-12-02 16:43:03 +02:00
redirect = server . parsed . get ( " redirect " , [ " " ] ) [ 0 ]
2024-11-23 22:34:11 +02:00
# 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-30 19:43:35 +02:00
< form action = " do_login " method = " post " >
2024-11-23 22:34:11 +02:00
< 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-12-03 00:30:02 +02:00
< div class = " button " >
< input type = " checkbox " name = " logout " >
< label > Log Out Other Sessions < / label >
< / div >
< br > < br >
2024-11-23 22:34:11 +02:00
2024-12-02 16:43:03 +02:00
"""
if redirect :
html = html + ' <input type= " hidden " name= " redirect " value= " ' + Safe ( redirect ) + ' " > '
html = html + """
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 " >
2024-11-30 19:43:35 +02:00
< form action = " do_register " method = " post " >
2024-11-24 23:28:19 +02:00
"""
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 " )
2024-11-23 00:48:29 +02:00
send ( server , html , 200 )
2024-11-20 14:40:56 +02:00
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 " , " " )+ " " " ' >
2024-11-28 05:30:15 +02:00
< img style = " vertical-align: middle " src = " /icon/image_link " >
2024-11-24 16:13:01 +02:00
< 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-12-03 00:30:02 +02:00
2024-11-24 16:13:01 +02:00
"""
2024-12-03 00:30:02 +02:00
if user . get ( " email " ) :
if not user . get ( " email_verified " ) :
html = html + """
< div class = " dark_box " id = " email " >
< center > < h2 > Email Settings < / h2 >
< br >
Email """ +Safe(user.get( " email " ))+ """ is not verified .
< br > < br >
""" +Button( " Verify " , " /email_verify " , " ok " )+ """
< / center >
< / div >
"""
else :
html = html + """
< div class = " dark_box " id = " email " >
< form action = " email_update " >
< center > < h2 > Email Settings < / h2 >
< br >
Email """ +Safe(user.get( " email " ))+ """ is verified !
< br > < br >
< / center >
< / 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 >
2024-12-03 00:30:02 +02:00
< div class = " dark_box " >
< center > < h2 > Change Password < / h2 > < / center >
< form action = " change_password " method = " post " >
< img style = " vertical-align: middle " src = " /icon/unlock " >
< input class = " button " style = " width:90 % " maxlength = " 200 " required type = " password " name = " password " title = " Old Password " placeholder = " Old Password " >
< img style = " vertical-align: middle " src = " /icon/lock " >
< input class = " button " style = " width:90 % " maxlength = " 200 " required type = " password " name = " new_password " title = " New Password " placeholder = " New Password " >
< button class = " button " type = " submit " >
< img style = " vertical-align: middle " src = " /icon/ok " >
Change
< / button >
< / div >
2024-11-25 23:01:02 +02:00
< / 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-28 05:30:15 +02:00
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 :
2024-11-30 14:27:26 +02:00
html = html + ' <input type= " hidden " name= " name " value= " ' + name . replace ( " ' " , " ' " ) + ' " > '
2024-11-28 05:30:15 +02:00
html = html + """
< img style = " vertical-align: middle " src = " /icon/scene " >
2024-11-30 14:27:26 +02:00
< input class = " button " style = " width:90 % " required = " " name = " title " placeholder = " Title " value = ' " " " +article.get( " title " , " " ).replace( " ' " , " & apos ; " )+ " " " ' >
2024-11-28 05:30:15 +02:00
< img style = " vertical-align: middle " src = " /icon/image_link " >
2024-11-30 14:27:26 +02:00
< input class = " button " style = " width:90 % " name = " thumbnail " placeholder = " Link To Thumbnail ( Optional ) " value = ' " " " +article.get( " thumbnail " , " " ).replace( " ' " , " & apos ; " )+ " " " ' >
2024-11-28 05:30:15 +02:00
< img style = " vertical-align: middle " src = " /icon/copy_file " >
2024-11-30 14:27:26 +02:00
< input class = " button " style = " width:90 % " list = " Licenses " name = " license " placeholder = " License ( Optional ) " value = ' " " " +article.get( " license " , " " ).replace( " ' " , " & apos ; " )+ " " " ' >
2024-11-28 05:30:15 +02:00
< 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 " >
2024-11-30 14:27:26 +02:00
< input class = " button " style = " width:90 % " name = " recording " placeholder = " Link To Sound Recording ( Optional ) " value = ' " " " +article.get( " recording " , " " ).replace( " ' " , " & apos ; " )+ " " " ' >
2024-12-01 02:59:19 +02:00
"""
2024-12-04 14:25:58 +02:00
# Optional Petition
2024-12-01 02:59:19 +02:00
if rank ( user . get ( " username " , " " ) ) == 0 :
2024-12-04 14:25:58 +02:00
2024-12-05 15:54:16 +02:00
petition = article . get ( " petition " , { } )
petition_goal = petition . get ( " goal " , " " )
petition_api = petition . get ( " api " , { } ) . get ( " api " , " " )
petition_api_key = petition . get ( " api " , { } ) . get ( " keys " , [ ] )
petition_api_title = petition . get ( " api " , { } ) . get ( " title " , " " )
petition_api_link = petition . get ( " api " , { } ) . get ( " link " , " " )
if len ( petition_api_key ) == 1 :
petition_api_key = petition_api_key [ 0 ]
else :
key = petition_api_key [ 0 ]
for n , i in petition_api_key :
if n != 0 :
key = key + " / " + i
petition_api_key = key
2024-12-04 14:25:58 +02:00
2024-12-01 02:59:19 +02:00
html = html + """
2024-11-28 05:30:15 +02:00
2024-12-01 02:59:19 +02:00
< details >
< summary class = " button " >
2024-12-04 14:25:58 +02:00
< img alt = " [icon petition] " style = " vertical-align: middle " src = " /icon/petition " >
Petition
2024-12-01 02:59:19 +02:00
< / summary >
< div class = " dark_box " >
< br >
2024-12-04 14:25:58 +02:00
< img alt = " [icon ok] " style = " vertical-align: middle " src = " /icon/ok " >
< input class = " button " type = " number " style = " width:90 % " name = " petition_goal " placeholder = " Goal ( How Many Signatures ) " value = ' " " " +str(petition_goal)+ " " " ' >
2024-12-01 02:59:19 +02:00
< br >
2024-12-04 14:25:58 +02:00
< details >
< summary class = " button " >
< img alt = " [icon analytics] " style = " vertical-align: middle " src = " /icon/analytics " >
From API
< / summary >
2024-12-01 02:59:19 +02:00
< br >
< img alt = " [icon link] " style = " vertical-align: middle " src = " /icon/link " >
2024-12-05 15:54:16 +02:00
< input class = " button " style = " width:90 % " name = " petition_api " placeholder = " API to check ( should be JSON ) " value = ' " " " +petition_api+ " " " ' >
2024-12-01 02:59:19 +02:00
< br >
< img alt = " [icon checlist] " style = " vertical-align: middle " src = " /icon/checklist " >
2024-12-05 15:54:16 +02:00
< input class = " button " style = " width:90 % " name = " petition_api_key " placeholder = " Key To Look For ( split with / for nested deep data ) " value = ' " " " +petition_api_key+ " " " ' >
2024-12-01 02:59:19 +02:00
< br >
< img alt = " [icon scene] " style = " vertical-align: middle " src = " /icon/scene " >
2024-12-05 15:54:16 +02:00
< input class = " button " style = " width:90 % " name = " petition_api_title " placeholder = " Statistic ' s Name " value = ' " " " +petition_api_title+ " " " ' >
2024-12-04 14:25:58 +02:00
< br >
< img alt = " [icon link] " style = " vertical-align: middle " src = " /icon/link " >
2024-12-05 15:54:16 +02:00
< input class = " button " style = " width:90 % " name = " petition_api_link " placeholder = " Action Link ( where people can increase the number in the API ) " value = ' " " " +petition_api_link+ " " " ' >
2024-12-01 02:59:19 +02:00
< br >
< / div >
< / details >
"""
html = html + """
2024-11-28 05:30:15 +02:00
< 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 >
2024-12-01 02:59:19 +02:00
2024-11-28 05:30:15 +02:00
< 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 )
2024-12-01 02:59:19 +02:00
2024-11-24 16:13:01 +02:00
2024-11-20 14:40:56 +02:00
###
def Button ( text , link , icon = " " , image = " " ) :
2024-11-19 13:47:45 +02:00
2024-11-19 19:04:03 +02:00
html = """
< a class = " button " href = \""" " +link+ """ " > " " "
if icon :
html = html + """
2024-11-30 22:03:24 +02:00
< img alt = " [icon " " " + icon + """ ] " src= " /icon/ """ + icon + """ " style= " vertical-align: middle " > """
2024-11-20 14:40:56 +02:00
if image :
html = html + """
2024-11-30 22:03:24 +02:00
< img alt = " [icon " " " + icon + """ ] " src= \" """ + image + """ " style= " height:40px;vertical-align: middle " > """
2024-11-20 14:40:56 +02:00
2024-11-19 19:04:03 +02:00
html = html + """
""" +text+ """ < / a >
"""
return html
2024-11-19 13:47:45 +02:00
2024-11-23 00:48:29 +02:00
def ArticlePreview ( article , Tabs , cookie = " " ) :
2024-11-20 14:40:56 +02:00
html = """
< div class = " article_box " >
"""
2024-11-23 00:48:29 +02:00
url , tab = article . get ( " url " , " " ) , article . get ( " tab " , " " )
sup = " "
if cookie not in article . get ( " views " , { } ) . get ( " viewers " , " " ) :
sup = ' <center><sup><b> Unread </b></sup></center><br> '
2024-11-20 14:40:56 +02:00
html = html + ' <a href= " ' + url + ' " ><h1> '
2024-11-30 22:03:24 +02:00
html = html + ' <img alt= " [icon ' + tab + ' ] " src= " /icon/ ' + Tabs . get ( tab , { } ) . get ( " icon " , " folder " ) + ' " style= " vertical-align: middle " > '
2024-11-23 00:48:29 +02:00
html = html + article . get ( " title " , " " ) + " </h1></a> " + sup + " \n "
2024-11-20 14:40:56 +02:00
2024-12-01 02:59:19 +02:00
if article . get ( " thumbnail " ) :
2024-11-30 22:03:24 +02:00
html = html + ' <center><a href= " ' + url + ' " ><img alt= " [thumbnail] " src= " ' + article [ " thumbnail " ] + ' " ></a></center> '
2024-11-20 14:40:56 +02:00
2024-12-04 14:25:58 +02:00
petition = article . get ( " petition " , " " )
if petition :
try :
frac = petition . get ( " signed " , 0 ) / int ( petition . get ( " goal " , 1 ) )
except :
frac = 0
html = html + ProgressBar ( frac )
2024-11-20 14:40:56 +02:00
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> '
2024-11-23 00:48:29 +02:00
html = html + " <br> " + markdown . convert ( article . get ( " description " , " " ) , False ) + " <br><br> "
2024-11-20 14:40:56 +02:00
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.
2024-11-23 00:48:29 +02:00
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 :
2024-11-30 22:03:24 +02:00
html = ' <img alt= " [avatar] " style= " height:50px;vertical-align: middle " src= " ' + avatar + ' " > <a href= " /account/ ' + username + ' " > ' + title + ' </a></center> \n '
2024-11-23 22:34:11 +02:00
else :
2024-11-30 22:03:24 +02:00
html = ' <img alt= " [avatar] " style= " height:50px;vertical-align: middle " src= " ' + avatar + ' " > ' + title + ' </center> \n '
2024-11-20 14:40:56 +02:00
return html
def Graph ( server , url ) :
2024-11-30 14:27:26 +02:00
2024-11-30 19:43:35 +02:00
if url . endswith ( " .md " ) :
url = url . replace ( " .md " , " " )
2024-11-30 14:27:26 +02:00
# If there are any values after ? in the path
# which means, that somebody is sending the old
# version of the graph link from the legacy code
# we should not count it as a view.
if " ? " in server . path :
AccessDenied ( server )
return
2024-11-29 19:56:59 +02:00
# Since /graph/ is used to count views
# we need the cookie to be generated and
# used by the user's browser before we load
# it, since a lot of people might just click
# the link once. In which case the entire page
# including graph loads before the cookie.
2024-12-01 02:59:19 +02:00
2024-11-29 19:56:59 +02:00
2024-12-01 02:59:19 +02:00
if not server . cookie and server . headers . get ( " user-agent " , " " ) not in ProblematicRefreshes :
2024-11-29 20:07:06 +02:00
Redirect ( server , server . path )
2024-12-01 02:59:19 +02:00
ProblematicRefreshes . append ( server . headers . get ( " user-agent " , " " ) )
2024-11-29 20:07:06 +02:00
return
2024-11-30 14:27:26 +02:00
2024-12-01 02:59:19 +02:00
if server . headers . get ( " user-agent " , " " ) in ProblematicRefreshes :
ProblematicRefreshes . remove ( server . headers . get ( " user-agent " , " " ) )
2024-11-30 14:27:26 +02:00
# Sometimes scrapers try to load graph without
# loading the article first. We don't want to count
# it as a view.
2024-11-30 19:43:35 +02:00
if time . time ( ) - 20 > RecentArticles . get ( url , 0 ) :
2024-11-30 14:27:26 +02:00
print ( consoleForm ( server . cookie ) , " Article wasn ' t loaded, scrapers! " )
AccessDenied ( server )
return
2024-12-01 02:59:19 +02:00
2024-11-30 14:27:26 +02:00
2024-11-30 19:43:35 +02:00
2024-11-24 16:13:01 +02:00
user = validate ( server . cookie )
2024-11-30 19:43:35 +02:00
# Store general analytics about which search engines were used.
# To get to this article.
referrer = RefferedArticles . get ( url , " " )
2024-11-20 14:40:56 +02:00
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 >
"""
2024-11-30 19:43:35 +02:00
2024-11-20 14:40:56 +02:00
try :
with open ( Set . Folder ( ) + " /tabs " + url + " /metadata.json " ) as o :
article = json . load ( o )
except :
article = { }
2024-11-27 00:33:53 +02:00
dateformat = " % Y- % m- %d "
2024-11-20 14:40:56 +02:00
dates = article . get ( " views " , { } ) . get ( " dates " , { } )
2024-11-27 00:33:53 +02:00
if dates :
largest = max ( dates . values ( ) )
2024-11-20 14:40:56 +02:00
2024-11-27 00:33:53 +02:00
startdate = datetime . strptime ( sorted ( list ( dates . keys ( ) ) , reverse = True ) [ 0 ] , dateformat )
enddate = datetime . strptime ( sorted ( list ( dates . keys ( ) ) , reverse = True ) [ - 1 ] , dateformat )
2024-11-20 14:40:56 +02:00
2024-11-27 00:33:53 +02:00
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 '
2024-11-20 14:40:56 +02:00
2024-11-23 00:48:29 +02:00
# 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-23 00:48:29 +02:00
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 :
2024-11-27 00:33:53 +02:00
if cookie and cookie not in article . get ( " views " , { } ) . get ( " viewers " , [ ] ) :
2024-11-24 16:13:01 +02:00
2024-11-23 00:48:29 +02:00
article [ " views " ] [ " amount " ] + = 1
2024-11-24 16:13:01 +02:00
article [ " views " ] [ " viewers " ] . append ( cookie )
2024-11-23 00:48:29 +02:00
2024-11-30 19:43:35 +02:00
if " referrers " not in article [ " views " ] :
article [ " views " ] [ " referrers " ] = { }
2024-11-30 22:03:24 +02:00
if referrer and referrer not in article [ " views " ] [ " referrers " ] :
article [ " views " ] [ " referrers " ] [ referrer ] = 0
2024-11-30 19:43:35 +02:00
if referrer :
article [ " views " ] [ " referrers " ] [ referrer ] + = 1
2024-11-27 00:33:53 +02:00
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 )
2024-11-23 00:48:29 +02:00
2024-11-20 14:40:56 +02:00
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> Pending Approval </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-12-04 01:42:54 +02:00
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> 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> '
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 :
2024-12-02 16:43:03 +02:00
html = html + ' <a class= " button " href= " /login?redirect= ' + urllib . parse . quote_plus ( server . path ) + ' " > '
2024-11-30 22:03:24 +02:00
html = html + ' <img alt= " [icon user] " 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 "
2024-11-30 22:03:24 +02:00
html = html + ' <img alt= " [avatar] " style= " height:40px;vertical-align: middle " src= " ' + avatar + ' " > '
2024-11-23 22:34:11 +02:00
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-12-04 14:25:58 +02:00
def ProgressBar ( frac ) :
title = str ( round ( frac * 100 , 1 ) ) + " % "
frac = min ( 1 , frac )
frac = max ( 0 , frac )
html = ' <div title= " ' + title + ' " class= " back_progress " > '
html = html + ' <div title= " ' + title + ' " class= " front_progress " , style= " width: ' + str ( frac * 100 ) + ' % " > '
html = html + ' </div></div> '
return html
2024-11-23 22:34:11 +02:00
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-12-05 15:54:16 +02:00
def Error ( server , text = " Some Error Happened. " ) :
config = Set . Load ( )
html = head ( title = " 501 Error " ,
description = " 501 Error " ,
config = config
)
html = html + Button ( config . get ( " title " , " My Website " ) , " / " , image = config . get ( " favicon " , " /icon/internet " ) )
html = html + """
< center >
< div class = " article_box " >
< h1 > 501 Error < / h1 >
""" +text+ """
< br > < br >
< / center >
"""
2024-11-25 23:01:02 +02:00
2024-12-05 15:54:16 +02:00
send ( server , html , 501 )
2024-11-23 22:34:11 +02:00
###
2024-11-29 19:56:59 +02:00
def Redirect ( server , url , time = 0 ) :
2024-11-23 22:34:11 +02:00
2024-11-30 14:27:26 +02:00
print ( consoleForm ( server . cookie ) , " Redirecting to: " + url )
html = """ <meta http-equiv= " Refresh " content= \" """ + str ( time ) + """ ; url= ' """ + url . replace ( " ' " , " % 27 " ) . replace ( ' " ' , " % 22 " ) + """ ' " /> """
2024-11-23 22:34:11 +02:00
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 ( )
2024-12-02 16:43:03 +02:00
redirect = server . parsed . get ( " redirect " , [ " " ] ) [ 0 ]
2024-12-03 00:30:02 +02:00
logout = server . parsed . get ( " logout " , [ " " ] ) [ 0 ]
2024-11-23 22:34:11 +02:00
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-12-03 00:30:02 +02:00
2024-11-24 16:13:01 +02:00
f = Set . Folder ( )
folder = f + " /accounts "
2024-12-03 00:30:02 +02:00
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-12-03 00:30:02 +02:00
if logout :
for cookie in list ( account [ " sessions " ] . keys ( ) ) :
if cookie != server . cookie :
del account [ " sessions " ] [ cookie ]
2024-11-23 22:34:11 +02:00
2024-12-03 00:30:02 +02:00
with open ( folder + " / " + username + " .json " , " w " ) as save :
json . dump ( account , save , indent = 4 )
2024-12-02 16:43:03 +02:00
if not redirect :
Redirect ( server , " /settings " )
else :
Redirect ( server , redirect )
2024-11-24 16:13:01 +02:00
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 ]
2024-12-03 00:30:02 +02:00
# Making sure to reverify email.
if key == " email " and user [ key ] != data :
user [ " email_verified " ] = False
2024-11-24 16:13:01 +02:00
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-28 05:30:15 +02:00
2024-12-03 00:30:02 +02:00
def UpdatePassword ( server ) :
user = validate ( server . cookie )
old_password = server . parsed . get ( " password " , [ " " ] ) [ 0 ]
new_password = server . parsed . get ( " new_password " , [ " " ] ) [ 0 ]
old_hashed = hashlib . sha512 ( old_password . encode ( " utf-8 " ) ) . hexdigest ( )
new_hashed = hashlib . sha512 ( new_password . encode ( " utf-8 " ) ) . hexdigest ( )
# Validating the user's password
if user . get ( " password " , " " ) == old_hashed :
user [ " password " ] = new_hashed
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 " )
AccessDenied ( server )
2024-11-28 05:30:15 +02:00
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
2024-11-24 16:13:01 +02:00
2024-11-28 05:30:15 +02:00
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> "
2024-11-29 16:20:48 +02:00
if granted or revoked :
Notify ( account , " /account/ " + account , text )
2024-11-28 05:30:15 +02:00
2024-11-23 22:34:11 +02:00
def DoComment ( server ) :
2024-12-04 01:42:54 +02:00
# Limiting bots from commenting
if not server . cookie :
AccessDenied ( )
return
2024-11-23 22:34:11 +02:00
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-28 05:30:15 +02:00
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 ]
2024-12-04 14:25:58 +02:00
# Petition data
petition_goal = server . parsed . get ( " petition_goal " , [ " " ] ) [ 0 ]
petition_api = server . parsed . get ( " petition_api " , [ " " ] ) [ 0 ]
petition_api_key = server . parsed . get ( " petition_api_key " , [ " " ] ) [ 0 ]
petition_api_title = server . parsed . get ( " petition_api_title " , [ " " ] ) [ 0 ]
petition_api_link = server . parsed . get ( " petition_api_link " , [ " " ] ) [ 0 ]
2024-11-30 14:27:26 +02:00
2024-11-28 05:30:15 +02:00
# 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
2024-12-04 14:25:58 +02:00
# Petition
if petition_goal :
petition = metadata . get ( " petition " , {
" signed " : 0 ,
" signatures " : [ ]
} )
try :
petition [ " goal " ] = int ( petition_goal )
except :
petition [ " goal " ] = 1
2024-12-05 15:54:16 +02:00
# API petition
if petition_api :
petition [ " api " ] = {
" api " : petition_api ,
" keys " : petition_api_key . split ( " / " ) ,
" title " : petition_api_title ,
" link " : petition_api_link
}
metadata [ " petition " ] = petition
2024-12-04 14:25:58 +02:00
2024-12-05 15:54:16 +02:00
else :
try :
del metadata [ " petition " ]
except :
pass
2024-12-04 14:25:58 +02:00
2024-11-28 05:30:15 +02:00
# 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 ) )
2024-12-05 15:54:16 +02:00
if metadata . get ( " petition " ) :
metadata [ " url " ] = " / " + tab + " / " + name
try :
API . Petition ( metadata )
except Exception as e :
Error ( server , " Cannot Load API Value<br> \n " + Safe ( str ( e ) ) )
return
2024-11-28 05:30:15 +02:00
Redirect ( server , " / " + tab + " / " + name )
2024-11-25 23:01:02 +02:00
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 )
2024-11-27 17:02:54 +02:00
2024-12-05 15:54:16 +02:00
def TimeDifference ( timeA , timeB ) :
text = " "
if timeA < timeB :
if timeB - timeA < 10 :
text = " now "
return text
elif timeB - timeA < 60 :
text = str ( int ( timeB - timeA ) ) + " seconds ago "
return text
2024-11-27 17:02:54 +02:00
2024-12-05 15:54:16 +02:00
else :
if int ( ( timeB - timeA ) / 60 ) == 1 :
text = str ( int ( ( timeB - timeA ) / 60 ) ) + " minute ago "
else :
text = str ( int ( ( timeB - timeA ) / 60 ) ) + " minutes ago "
return text
else :
if timeA - timeA < 10 :
text = " now "
return text
elif timeA - timeB < 60 :
text = " in " + str ( int ( timeA - timeB ) ) + " seconds "
return text
else :
if int ( ( timeA - timeB ) / 60 ) == 1 :
text = " in " + str ( int ( ( timeA - timeB ) / 60 ) ) + " minute "
else :
text = " in " + str ( int ( ( timeA - timeB ) / 60 ) ) + " minutes "
return text
2024-11-27 17:02:54 +02:00
def RSS ( server ) :
# Rendering rss feed.
Articles = allArticles ( )
config = Set . Load ( )
favicon = config . get ( " favicon " , " " )
if favicon . startswith ( " / " ) :
favicon = " https:// " + config . get ( " domain " , " example.com " ) + favicon
2024-11-29 16:20:48 +02:00
author = server . parsed . get ( " author " , [ " " ] ) [ 0 ]
title = config . get ( " title " , " My Website " )
if author :
Accounts = accounts ( )
account = Accounts . get ( author , { } )
title = account . get ( " title " , author ) + " at: " + title
2024-12-01 02:59:19 +02:00
rss = """ <?xml version= " 1.0 " encoding= " UTF-8 " ?>
2024-11-27 17:02:54 +02:00
< rss version = " 2.0 " xmlns : atom = " http://www.w3.org/2005/Atom " xmlns : dc = " http://purl.org/dc/elements/1.1/ " >
< channel >
2024-11-29 16:20:48 +02:00
< title > """ +title+ """ < / title >
2024-11-27 17:02:54 +02:00
< link > https : / / """ +config.get( " domain " , " example.com " )+ """ < / link >
< description > """ +config.get( " tagline " , " " )+ """ < / description >
< image >
< url > """ +favicon+ """ < / url >
2024-11-29 16:20:48 +02:00
< title > """ +title+ """ < / title >
2024-11-27 17:02:54 +02:00
< link > https : / / """ +config.get( " domain " , " example.com " )+ """ < / link >
< / image >
"""
2024-11-29 16:20:48 +02:00
n = 0
for article in Articles :
2024-11-27 17:02:54 +02:00
2024-11-29 16:20:48 +02:00
if author and author != Articles [ article ] . get ( " author " , " " ) :
continue
n + = 1
2024-11-27 17:02:54 +02:00
if n > 10 :
break
pubDate = Articles [ article ] . get ( " timestamp " , 0 )
pubDate = datetime . fromtimestamp ( pubDate )
2024-12-01 02:59:19 +02:00
pubDate = email . utils . format_datetime ( pubDate )
2024-11-27 17:02:54 +02:00
rss = rss + """
< item >
2024-12-01 02:59:19 +02:00
< title > """ +Articles[article].get( " title " , article).replace( " & " , " & " )+ """ < / title >
2024-11-27 17:02:54 +02:00
< link > https : / / """ +config.get( " domain " , " example.com " )+Articles[article].get( " url " , " " )+ """ < / link >
2024-12-01 02:59:19 +02:00
< guid > https : / / """ +config.get( " domain " , " example.com " )+Articles[article].get( " url " , " " )+ """ < / guid >
< description > < ! [ CDATA [ """ +markdown.convert(Articles[article].get( " description " , article), False, fullpath=True)+ """ ] ] > < / description >
2024-11-27 17:02:54 +02:00
< pubDate > """ +pubDate+ """ < / pubDate >
< / item >
"""
rss = rss + """
< / channel >
< / rss >
"""
send ( server , rss , 200 )
2024-11-29 16:20:48 +02:00
def Search ( server ) :
Articles = allArticles ( )
Tabs = tabs ( )
config = Set . Load ( )
# Generating <head>
html = head ( title = " Search " ,
description = " " ,
config = config
)
html = html + Button ( config . get ( " title " , " My Website " ) , " / " , image = config . get ( " favicon " , " /icon/internet " ) )
# The place where you can type your search
text = server . parsed . get ( " text " , [ " " ] ) [ 0 ]
try : page = int ( server . parsed . get ( " page " , [ " 0 " ] ) [ 0 ] )
except Exception as e :
print ( e )
page = 0
searchtitle = server . parsed . get ( " title " , [ " " ] ) [ 0 ]
searchauthor = server . parsed . get ( " author " , [ " " ] ) [ 0 ]
searchpost = server . parsed . get ( " post " , [ " " ] ) [ 0 ]
searchdescription = server . parsed . get ( " description " , [ " " ] ) [ 0 ]
searchcomments = server . parsed . get ( " comments " , [ " " ] ) [ 0 ]
# Supporting legacy search links
if not any ( [ searchtitle ,
searchauthor ,
searchpost ,
searchdescription ,
searchcomments
] ) :
searchtitle = True
searchpost = True
searchdescription = True
searchcomments = True
checkedtitle = " "
if searchtitle : checkedtitle = " checked "
checkedauthor = " "
if searchauthor : checkedauthor = " checked "
checkedpost = " "
if searchpost : checkedpost = " checked "
checkeddescription = " "
if searchdescription : checkeddescription = " checked "
checkedcomments = " "
if searchcomments : checkedcomments = " checked "
html = html + """
< center >
< form action = " /search " >
< div class = " toot " >
< input name = " text " class = " button " placeholder = " Search... " value = ' " " " +text+ " " " ' >
< button class = " button " type = " submit " >
< img style = " vertical-align: middle " src = " /icon/search " >
Search
< / button >
< br >
< div class = " button " >
< input type = " checkbox " """ +checkedtitle+ """ name = " title " > Title
< / div >
< div class = " button " >
< input type = " checkbox " """ +checkedauthor+ """ name = " author " > Author
< / div >
< div class = " button " >
< input type = " checkbox " """ +checkedpost+ """ name = " post " > Post Text
< / div >
< div class = " button " >
< input type = " checkbox " """ +checkeddescription+ """ name = " description " > Description
< / div >
< div class = " button " >
< input type = " checkbox " """ +checkedcomments+ """ name = " comments " > Comments
< / div >
< / div >
< form >
< / center >
"""
# Acutally doing the searching
counted = [ ]
for article in Articles :
points = 0
# Title x 100 points
if searchtitle :
title = Articles [ article ] . get ( " title " , article )
points + = title . lower ( ) . count ( text . lower ( ) ) * 100
# Description x 10 points
if searchdescription :
description = Articles [ article ] . get ( " description " , " " )
points + = description . lower ( ) . count ( text . lower ( ) ) * 10
# Author
if searchauthor :
author = Articles [ article ] . get ( " author " , " " )
if author == text :
points + = 2 # Perfect match with username preffered
# People might also look at the username
Accounts = accounts ( )
if text . lower ( ) == Accounts . get ( author , { } ) . get ( " title " , " " ) . lower ( ) :
points + = 1
# Comments x 1
if searchcomments :
comments = Articles [ article ] . get ( " comments " , { } ) . get ( " comments " , { } )
for comment in comments :
commentText = comment . get ( " text " , " " )
points + = commentText . lower ( ) . count ( text . lower ( ) )
# Post Text x 1
if searchpost :
try :
f = Set . Folder ( )
url = Articles [ article ] . get ( " url " )
postText = open ( f + " /tabs/ " + url + " /text.md " ) . read ( )
points + = postText . lower ( ) . count ( text . lower ( ) )
except Exception as e :
print ( e )
if points :
counted . append ( [ points , article ] )
counted = list ( reversed ( sorted ( counted ) ) )
Buffer = 16
From = Buffer * page
To = From + Buffer
urlNoPage = server . path
if " page= " in urlNoPage : urlNoPage = urlNoPage [ : urlNoPage . rfind ( " & " ) ]
if len ( list ( counted ) ) > Buffer :
if page > 0 :
html = html + Button ( str ( page - 1 ) , urlNoPage + " &page= " + str ( page - 1 ) , " left " )
html = html + ' <div class= " button " > ' + str ( page ) + ' </div> '
if To < len ( list ( counted ) ) - 1 :
html = html + Button ( str ( page + 1 ) , urlNoPage + " &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 ( counted ) :
points , article = article
if n < From : continue
if n > = To : break
html = html + ArticlePreview ( Articles [ article ] , Tabs , server . cookie )
rendered + = 1
html = html + ' </div><br> '
if len ( list ( counted ) ) > Buffer :
if page > 0 :
html = html + Button ( str ( page - 1 ) , urlNoPage + " &page= " + str ( page - 1 ) , " left " )
html = html + ' <div class= " button " > ' + str ( page ) + ' </div> '
if To < len ( list ( counted ) ) - 1 :
html = html + Button ( str ( page + 1 ) , urlNoPage + " &page= " + str ( page + 1 ) , " right " )
html = html + LoginButton ( server )
send ( server , html , 200 )