2022-08-30 21:10:45 +02:00
""" ================================= |
| This file is part of |
| staticoso |
| Just a simple Static Site Generator |
| |
| Licensed under the AGPLv3 license |
| Copyright ( C ) 2022 , OctoSpacc |
| == == == == == == == == == == == == == == == == = """
2022-11-17 13:03:17 +01:00
from base64 import b64encode
2022-12-20 13:05:41 +01:00
from Modules . Globals import *
2022-08-30 21:10:45 +02:00
from Modules . HTML import *
from Modules . Utils import *
2022-11-16 13:07:27 +01:00
JournalHeadings = ( ' h2 ' , ' h3 ' , ' h4 ' , ' h5 ' )
JournalTitleDecorators = { ' ( ' : ' ) ' , ' [ ' : ' ] ' , ' { ' : ' } ' }
2022-11-17 13:03:17 +01:00
#JournalStyles = {
# "Default": {},
# "details": {}
#}
2022-11-04 11:03:43 +01:00
HTMLSectionTitleLine = ' <h {Index} class= " SectionHeading staticoso-SectionHeading " ><span class= " SectionLink staticoso-SectionLink " ><a href= " # {DashTitle} " ><span>»</span></a> </span><span class= " SectionTitle staticoso-SectionTitle " id= " {DashTitle} " > {Title} </span></h {Index} > '
PugSectionTitleLine = " {Start} {Heading} .SectionHeading.staticoso-SectionHeading #[span.SectionLink.staticoso-SectionLink #[a(href= ' # {DashTitle} ' ) #[span »]] ]#[span# {DashTitle} .SectionTitle.staticoso-SectionTitle {Rest} ] "
2022-08-30 21:10:45 +02:00
CategoryPageTemplate = """ \
/ / Title : { Name }
/ / Type : Page
/ / Index : True
# {Name}
2022-10-17 15:53:07 +02:00
< div > < staticoso : Category : { Name } > < / div >
2022-08-30 21:10:45 +02:00
"""
2022-10-17 15:53:07 +02:00
RedirectPageTemplate = """ \
< ! DOCTYPE html >
< html >
< head >
2023-02-17 21:53:37 +01:00
< meta charset = " UTF-8 " / >
< meta name = " viewport " content = " width=device-width, initial-scale=1.0 " / >
2022-11-02 00:17:36 +01:00
< title > { TitlePrefix } Redirect < / title >
2023-02-17 21:53:37 +01:00
< link rel = " canonical " href = " {SiteDomain} / {DestURL} " / >
< meta http - equiv = " refresh " content = " 0; url= ' {DestURL} ' " / >
2022-10-17 15:53:07 +02:00
< / head >
< body >
2022-11-02 00:17:36 +01:00
< p > < a href = " {DestURL} " > { StrClick } < / a > { StrRedirect } . < / p >
2022-10-17 15:53:07 +02:00
< / body >
< / html >
"""
2022-11-18 20:51:54 +01:00
HTMLCommentsBlock = ' <br><h3> {StrComments} </h3><a href= " {URL} " rel= " noopener " target= " _blank " > {StrOpen} <span class= " twa twa-↗️ " ><span>↗️</span></span></a> '
2022-08-30 21:10:45 +02:00
2023-02-17 21:53:37 +01:00
def DashifyTitle ( Title : str , Done : list = [ ] ) :
2022-08-30 21:10:45 +02:00
return UndupeStr ( DashifyStr ( Title . lstrip ( ' ' ) . rstrip ( ' ' ) ) , Done , ' - ' )
2023-02-17 21:53:37 +01:00
# Generate HTML tree list (nested list) from our internal metaformat, such as:
2022-12-15 16:29:22 +01:00
# :Item 1 \\ <li>Item 1<ul>
# .:Item 2 ============\\ <li>Item 2<ul>
# ..:Item 3 ============// <li>Item 3</li></ul></li></ul></li>
# :Item 4 // <li>Item 4</li>
2023-02-17 21:53:37 +01:00
def GenHTMLTreeList ( MetaList : str , Type : str = ' ul ' , Class : str = " " ) :
2022-12-15 16:29:22 +01:00
HTML = ' '
Lines = MetaList . splitlines ( )
CurDepth , NextDepth , PrevDepth = 0 , 0 , 0
for i , e in enumerate ( Lines ) :
CurDepth = e . find ( ' : ' )
NextDepth = Lines [ i + 1 ] . find ( ' : ' ) if i + 1 < len ( Lines ) else 0
HTML + = ' \n <li> ' + e [ CurDepth + 1 : ]
if NextDepth == CurDepth :
HTML + = ' </li> '
elif NextDepth > CurDepth :
HTML + = f ' \n < { Type } > ' * ( NextDepth - CurDepth )
elif NextDepth < CurDepth :
HTML + = f ' </li> \n </ { Type } > ' * ( CurDepth - NextDepth ) + ' </li> '
PrevDepth = CurDepth
2023-02-17 21:53:37 +01:00
return f ' < { Type } class= " staticoso-TreeList { Class } " > { HTML } \n </ { Type } > '
2022-12-15 16:29:22 +01:00
2022-12-20 13:05:41 +01:00
def MakeLinkableTitle ( Line : str , Title : str , DashTitle : str , Type : str ) :
2022-08-30 21:10:45 +02:00
if Type == ' md ' :
Index = Title . split ( ' ' ) [ 0 ] . count ( ' # ' )
return HTMLSectionTitleLine . format (
Index = Index ,
DashTitle = DashTitle ,
Title = Title [ Index + 1 : ] )
elif Type == ' pug ' :
Index = Line . find ( ' h ' )
2022-11-04 11:03:43 +01:00
return PugSectionTitleLine . format (
Start = Line [ : Index ] ,
Heading = Line [ Index : Index + 2 ] ,
Rest = Line [ Index + 2 : ] ,
DashTitle = DashTitle )
2022-08-30 21:10:45 +02:00
2023-02-17 21:53:37 +01:00
def GetTitle ( FileName : str , Meta : dict , Titles : list , Prefer : str = ' MetaTitle ' , BlogName : str = None ) :
2022-08-30 21:10:45 +02:00
if Prefer == ' BodyTitle ' :
Title = Titles [ 0 ] . lstrip ( ' # ' ) if Titles else Meta [ ' Title ' ] if Meta [ ' Title ' ] else FileName
elif Prefer == ' MetaTitle ' :
Title = Meta [ ' Title ' ] if Meta [ ' Title ' ] else Titles [ 0 ] . lstrip ( ' # ' ) if Titles else FileName
elif Prefer == ' HTMLTitle ' :
Title = Meta [ ' HTMLTitle ' ] if Meta [ ' HTMLTitle ' ] else Meta [ ' Title ' ] if Meta [ ' Title ' ] else Titles [ 0 ] . lstrip ( ' # ' ) if Titles else FileName
2022-12-15 16:29:22 +01:00
if Meta [ ' Type ' ] == ' Post ' and BlogName and ' Blog ' in Meta [ ' Categories ' ] :
2022-08-30 21:10:45 +02:00
Title + = ' - ' + BlogName
return Title
2023-02-17 21:53:37 +01:00
def GetDescription ( Meta : dict , BodyDescription : str , Prefer : str = ' MetaDescription ' ) :
2022-08-30 21:10:45 +02:00
if Prefer == ' BodyDescription ' :
Description = BodyDescription if BodyDescription else Meta [ ' Description ' ] if Meta [ ' Description ' ] else ' '
elif Prefer == ' MetaDescription ' :
Description = Meta [ ' Description ' ] if Meta [ ' Description ' ] else BodyDescription if BodyDescription else ' '
return Description
2023-02-17 21:53:37 +01:00
def GetImage ( Meta : dict , BodyImage : str , Prefer : str = ' MetaImage ' ) :
2022-08-30 21:10:45 +02:00
if Prefer == ' BodyImage ' :
Image = BodyImage if BodyImage else Meta [ ' Image ' ] if Meta [ ' Image ' ] else ' '
elif Prefer == ' MetaImage ' :
Image = Meta [ ' Image ' ] if Meta [ ' Image ' ] else BodyImage if BodyImage else ' '
return Image
2023-02-17 21:53:37 +01:00
def MakeContentHeader ( Meta : dict , Locale : dict , Categories : str = ' ' ) :
2022-08-30 21:10:45 +02:00
Header = ' '
2022-10-19 11:27:51 +02:00
for e in [ ' CreatedOn ' , ' EditedOn ' ] :
if Meta [ e ] :
2022-12-20 13:05:41 +01:00
Header + = f ' <span class= " staticoso-ContentHeader- { e } " id= " staticoso-ContentHeader- { e } " ><span class= " staticoso-Label " > { Locale [ e ] } </span>: <span class= " staticoso-Value " > { Meta [ e ] } </span></span><br> '
2022-08-30 21:10:45 +02:00
if Categories :
2022-12-20 13:05:41 +01:00
Header + = f ' <span class= " staticoso-ContentHeader-Categories " id= " staticoso-ContentHeader-Categories " ><span class= " staticoso-Label " > { Locale [ " Categories " ] } </span>:<span class= " staticoso-Value " > { Categories . removesuffix ( " " ) } </span></span><br> '
if Meta [ ' Index ' ] . lower ( ) in PageIndexStrNeg :
Header + = f ' <span class= " staticoso-ContentHeader-Index " id= " staticoso-ContentHeader-Index " ><span class= " staticoso-Value " > { Locale [ " Unlisted " ] } </span></span><br> '
2022-08-30 21:10:45 +02:00
return f ' <p> { Header } </p> '
2023-02-17 21:53:37 +01:00
def MakeCategoryLine ( File : str , Meta : dict ) :
2022-08-30 21:10:45 +02:00
Categories = ' '
2022-11-04 11:03:43 +01:00
for Cat in Meta [ ' Categories ' ] :
Categories + = f ' <a href= " { GetPathLevels ( File ) } Categories/ { Cat } .html " > { html . escape ( Cat ) } </a> '
2022-08-30 21:10:45 +02:00
return Categories
2023-02-17 21:53:37 +01:00
def MakeListTitle ( File : str , Meta : dict , Titles : list , Prefer : str , BlogName : str , PathPrefix : str = ' ' ) :
2022-10-26 11:01:17 +02:00
Title = GetTitle ( File . split ( ' / ' ) [ - 1 ] , Meta , Titles , Prefer , BlogName ) . lstrip ( ) . rstrip ( )
2022-08-30 21:10:45 +02:00
Link = False if Meta [ ' Index ' ] == ' Unlinked ' else True
if Link :
2022-11-04 11:03:43 +01:00
Href = f ' { PathPrefix } { StripExt ( File ) } .html '
Title = f ' <a href= " { Href } " > { Title } </a> '
2023-02-17 21:53:37 +01:00
#else:
# Title = f'<span class="staticoso-ListItem-Plain">{Title}</span>'
2022-08-30 21:10:45 +02:00
if Meta [ ' Type ' ] == ' Post ' :
CreatedOn = Meta [ ' CreatedOn ' ] if Meta [ ' CreatedOn ' ] else ' ? '
2023-02-17 21:53:37 +01:00
Title = f " <span>[<time> { CreatedOn } </time>]</span> { Title } "
2022-08-30 21:10:45 +02:00
return Title
2023-02-17 21:53:37 +01:00
def FormatTitles ( Titles : list , Flatten = False ) :
2022-08-30 21:10:45 +02:00
# TODO: Somehow titles written in Pug can end up here and don't work, they should be handled
2022-12-15 16:29:22 +01:00
List , DashyTitles = ' ' , [ ]
2022-08-30 21:10:45 +02:00
for t in Titles :
n = 0 if Flatten else t . split ( ' ' ) [ 0 ] . count ( ' # ' )
2022-12-15 16:29:22 +01:00
Level = ' . ' * ( n - 1 ) + ' : '
2022-08-30 21:10:45 +02:00
Title = MkSoup ( t . lstrip ( ' # ' ) ) . get_text ( )
DashyTitle = DashifyTitle ( Title , DashyTitles )
DashyTitles + = [ DashyTitle ]
2022-12-15 16:29:22 +01:00
List + = f ' { Level } <a href= " # { DashyTitle } " > { html . escape ( Title ) } </a> \n '
return GenHTMLTreeList ( List )
2022-11-16 13:07:27 +01:00
# Clean up a generic HTML tree such that it's compliant with the HTML Journal standard
# (https://m15o.ichi.city/site/subscribing-to-a-journal-page.html);
# basis is: find an element with the JournalBody attr., and group its direct children as <article>s
2022-11-17 13:03:17 +01:00
def MakeHTMLJournal ( Flags , Locale , FilePath , HTML ) :
2022-11-16 13:07:27 +01:00
Soup , Journal , Entries = MkSoup ( HTML ) , ' ' , [ ]
for t in Soup . find_all ( attrs = { " htmljournal " : True } ) :
2022-11-17 13:03:17 +01:00
#JournalStyle = JournalStyles[t.attrs["journalstyle"]] if 'journalstyle' in t.attrs and t.attrs["journalstyle"] in JournalStyles else JournalStyles['Default']
2022-11-16 13:07:27 +01:00
for c in t . children : # Entries, some might be entirely grouped in their own element but others could not, use headings as separators
for ct in MkSoup ( str ( c ) ) . find_all ( ) :
# Transform (almost, for now I reserve some) any heading into h2 and remove any attributes
if ct . name in JournalHeadings :
Title = ct . text . strip ( ) . removeprefix ( ' » ' ) . strip ( )
Chr0 = Title [ 0 ]
2022-11-17 13:03:17 +01:00
# Remove leading symbols before date
2022-11-16 13:07:27 +01:00
if Chr0 in JournalTitleDecorators . keys ( ) :
Idx = Title . find ( JournalTitleDecorators [ Chr0 ] )
Title = Title [ 1 : Idx ] + ' - ' + Title [ Idx + 2 : ]
if Journal :
2022-11-17 13:03:17 +01:00
Journal + = ' \n </article><br> \n '
2022-11-16 13:07:27 +01:00
Journal + = f ' \n <article> \n <h2> { Title } </h2> \n '
elif ct . name == ' p ' : # We should handle any type to preserve <details> and things
Journal + = str ( ct )
2022-11-17 13:03:17 +01:00
FileName = FilePath . split ( ' / ' ) [ - 1 ]
URL = f ' { Flags [ " SiteDomain " ] } / { StripExt ( FilePath ) } .Journal.html '
2022-11-18 20:51:54 +01:00
Redirect = f """ <meta http-equiv= " refresh " content= " 0; url= ' ./ { FileName } ' " > """ if Flags [ " JournalRedirect " ] else ' '
2022-11-17 13:03:17 +01:00
# Instead of copying stuff from the full page, for now we use dedicated title, header, footer, and pagination
Title = t . attrs [ " journaltitle " ] if ' journaltitle ' in t . attrs else f ' " { StripExt ( FileName ) } " Journal - { Flags [ " SiteName " ] } ' if Flags [ " SiteName " ] else f ' " { StripExt ( FileName ) } " Journal '
FeedLink = f """ <a title= " Journal Atom Feed " href= " https://journal.miso.town/atom?url= { URL } " target= " _blank " rel= " noopener " ><img width= " 88 " height= " 31 " alt= " Journal Atom Feed " title= " Journal Atom Feed " src= " data:image/png;base64, { b64encode ( ReadFile ( staticosoBaseDir ( ) + ' Assets/Feed-88x31.png ' , ' rb ' ) ) . decode ( ) } " ></a> """ if Flags [ " SiteDomain " ] else ' '
Header = t . attrs [ " journalheader " ] if ' journalheader ' in t . attrs else f """ \
< p >
< i > { Locale [ " StrippedDownNotice " ] . format ( Link = " ./ " + FileName ) } < / i >
< a title = " Valid HTML Journal " href = " https://journal.miso.town " target = " _blank " rel = " noopener " > < img alt = " Valid HTML Journal " title = " Valid HTML Journal " width = " 88 " height = " 31 " src = " data:image/png;base64, { b64encode(ReadFile(staticosoBaseDir()+ ' Assets/Valid-HTML-Journal-88x31.png ' , ' rb ' )).decode()} " > < / a >
{ FeedLink }
< / p >
"""
Journal = f """ \
2022-11-18 20:51:54 +01:00
< ! - -
2022-11-17 13:03:17 +01:00
< ! DOCTYPE html >
< html >
< head >
2023-02-17 21:53:37 +01:00
< meta charset = " UTF-8 " / >
< meta name = " viewport " content = " width=device-width, initial-scale=1.0 " / >
2022-11-17 13:03:17 +01:00
< title > { Title } < / title >
2023-02-17 21:53:37 +01:00
< link rel = " canonical " href = " {URL} " / >
2022-11-18 20:51:54 +01:00
{ Redirect }
2022-11-17 13:03:17 +01:00
< / head >
< body >
2022-11-18 20:51:54 +01:00
- - - >
2022-11-17 13:03:17 +01:00
< h1 > { Title } < / h1 >
2022-11-18 20:51:54 +01:00
< header id = " Header " >
{ Header }
< div id = " staticoso-LinkToFooter " > < b > [ < big > < a href = " #Footer " > < span class = " twa twa-⬇️ " > < span > ⬇ ️ < / span > < / span > Footer < / a > < / big > ] < / b > < / div >
< / header > < br >
2022-11-17 13:03:17 +01:00
{ Journal }
< / article > < br >
2022-11-18 20:51:54 +01:00
< footer id = " Footer " >
< div id = " staticoso-LinkToHeader " > < b > [ < big > < a href = " #Header " > < span class = " twa twa-⬆️ " > < span > ⬆ ️ < / span > < / span > Header < / a > < / big > ] < / b > < / div >
{ t . attrs [ " journalfooter " ] if " journalfooter " in t . attrs else " " }
< / footer >
< ! - -
2022-11-17 13:03:17 +01:00
< / body >
< / html >
2022-11-18 20:51:54 +01:00
- - - >
2022-11-17 13:03:17 +01:00
"""
2022-11-16 13:07:27 +01:00
return Journal