558 lines
16 KiB
Lua
558 lines
16 KiB
Lua
meta = {
|
|
domain = "compudanzas.net",
|
|
title = "compudanzas",
|
|
description = "explorations of computing at a human scale",
|
|
fecha = os.date("%Y-%m-%d")
|
|
}
|
|
|
|
webdir = "web" -- web output directory
|
|
gemdir = "gem" -- gemini output directory
|
|
templates = {
|
|
webEn = {
|
|
header = { path = "pageheader.html", template = ""},
|
|
footer = { path = "pagefooter.html", template = ""},
|
|
incoming = { path = "pageincoming.html", template = ""}
|
|
},
|
|
webEs = {
|
|
header = { path = "pageheader.html", template = ""},
|
|
footer = { path = "pagefooter_es.html", template = ""},
|
|
incoming = { path = "pageincoming_es.html", template = ""}
|
|
},
|
|
gemEn = {
|
|
header = { path = "pageheader.gmi", template = ""},
|
|
footer = { path = "pagefooter.gmi", template = ""},
|
|
incoming = { path = "pageincoming.gmi", template = ""}
|
|
},
|
|
gemEs = {
|
|
header = { path = "pageheader.gmi", template = ""},
|
|
footer = { path = "pagefooter_es.gmi", template = ""},
|
|
incoming = { path = "pageincoming_es.gmi", template = ""}
|
|
},
|
|
}
|
|
|
|
pages = {}
|
|
|
|
function slugify( s )
|
|
return (string.gsub( s, "%s", "_" ))
|
|
end
|
|
|
|
function spacify( s ) -- opposite of slugify (?)
|
|
return (string.gsub( s, "_", " "))
|
|
end
|
|
|
|
function fillTemplate( template, pagemeta )
|
|
return ( string.gsub( template, "({.-})", function (key)
|
|
local var = string.sub(key,2,-2) -- remove { }
|
|
return meta[var] or pagemeta[var]
|
|
end) )
|
|
end
|
|
|
|
function getHeader( line ) -- returns title, level
|
|
local head, title = string.match(line,"^(#+)%s*(.-)$")
|
|
return title, #head
|
|
end
|
|
|
|
function getLink( line ) -- returns dest, text
|
|
return string.match( line, "^=>%s*(%S-)%s+(.-)$")
|
|
end
|
|
|
|
function isImagePath( text )
|
|
return text:match(".jpg$") or text:match(".gif$") or text:match(".png$")
|
|
end
|
|
|
|
function sanitize( line ) -- replace relevant html entities
|
|
return line:gsub("<","<"):gsub(">",">")
|
|
end
|
|
|
|
function closeTags()
|
|
local close = ""
|
|
if flags.list then
|
|
close = "</ul>\n"
|
|
flags.list = false
|
|
elseif flags.p then
|
|
close = "</p>\n"
|
|
flags.p = false
|
|
elseif flags.gallery then
|
|
close = "</gallery>\n"
|
|
flags.gallery = false
|
|
end
|
|
|
|
return close
|
|
end
|
|
|
|
function insertIncoming( pagename, incomingname )
|
|
if incomingname == "pages" then return false end
|
|
|
|
local incoming = pages[pagename].incoming
|
|
for i = 1, #incoming do
|
|
if incoming[i] == incomingname then
|
|
return false
|
|
end
|
|
end
|
|
|
|
table.insert( incoming, incomingname)
|
|
return true
|
|
end
|
|
|
|
function firstPass()
|
|
-- load templates
|
|
for _,t in pairs(templates) do
|
|
-- read templates
|
|
for k,subtemp in pairs( t ) do
|
|
local f = assert(io.open(string.format("templates/%s",subtemp.path),"r"))
|
|
subtemp.template = f:read("a")
|
|
f:close()
|
|
end
|
|
end
|
|
|
|
-- for each page:
|
|
-- convert gmo to gmi and html
|
|
-- and calculate incoming links
|
|
for name,page in pairs(pages) do
|
|
local pagemeta = pages[name]
|
|
|
|
local gmopath = string.format("src/%s.gmo", pagemeta.slug)
|
|
|
|
-- open file
|
|
local f = assert( io.open(gmopath, "r") )
|
|
|
|
-- initialize flags
|
|
flags = { list = false, pre = false , p = false, gallery = false }
|
|
|
|
-- table to store the lines to write to file
|
|
local lines = { web = {}, gem = {} }
|
|
|
|
local navlines = { "<ul>" }
|
|
|
|
-- convert one line at a time
|
|
local count = 1
|
|
for line in f:lines() do
|
|
-- the output line:
|
|
local out = { gem = line, web = nil}
|
|
|
|
if count == 1 then -- extract title
|
|
pagemeta.ptitle = string.match(line, "^#+%s*(.-)$")
|
|
elseif count == 2 then -- language
|
|
if line == "" then
|
|
pagemeta.lang = "en,es-MX" -- default
|
|
else
|
|
local langformat = "^lang=(%a+)"
|
|
local tradformat = "%s(%a+)->{(.-)}$"
|
|
pagemeta.lang = string.match(line,langformat)
|
|
local trlang, trname = string.match(line, tradformat)
|
|
if trlang ~= nil then
|
|
local ttext = "in english:"
|
|
if string.match( trlang, "^es" ) then
|
|
ttext = "en español:"
|
|
end
|
|
local weblink = string.format(
|
|
"<a href='./%s.html' rel='alternate'>%s</a>",
|
|
pages[ trname ].slug, trname )
|
|
local gemlink = string.format(
|
|
"=> ./%s.gmi %s",
|
|
pages[ trname ].slug, trname )
|
|
pagemeta.webtranslation = string.format(
|
|
"<p id='translation'>%s %s</p>",
|
|
ttext, weblink )
|
|
pagemeta.gemtranslation = string.format(
|
|
"%s\n%s\n", ttext, gemlink)
|
|
end
|
|
end
|
|
elseif count == 3 then -- obtain page description
|
|
pagemeta.pdescription,n = string.gsub(line,"[{}]","")
|
|
end
|
|
|
|
if count <=2 then goto nextline end -- skip normal processing for the first lines
|
|
|
|
-- CONVERT LINES
|
|
if string.match( line, "^```") then -- preformated
|
|
if flags.pre then
|
|
out.web = "</pre>"
|
|
else
|
|
out.web = "<pre>"
|
|
end
|
|
flags.pre = not flags.pre
|
|
goto insert -- skip more checks
|
|
end
|
|
|
|
if flags.pre then
|
|
out.web = sanitize( line )
|
|
goto insert
|
|
end
|
|
|
|
if string.match( line, "^%+") then -- + append html
|
|
out.gem = nil
|
|
out.web = string.sub(line,3) -- remove "+ "
|
|
|
|
elseif string.match( line, "^&") then -- & append gemtext
|
|
out.gem = string.sub(line,3) -- remove "& "
|
|
|
|
elseif string.match( line, "^$") then -- empty line
|
|
out.web = closeTags()
|
|
|
|
elseif string.match( line, "^#+") then -- HEADERS
|
|
local title, level = getHeader( line )
|
|
|
|
-- add h1 to navigation
|
|
if level==1 then
|
|
table.insert(navlines,string.format("<li><a href='#%s'>%s</a></li>",title,title))
|
|
end
|
|
|
|
local close = closeTags()
|
|
out.web = string.format("%s<h%d id='%s'>%s</h%d>",close,level,title,title,level)
|
|
|
|
elseif string.match( line, "^>") then -- BLOCKQUOTE
|
|
local close = closeTags()
|
|
out.web = string.format("%s<blockquote>%s</blockquote>", close, string.sub(line,3))
|
|
|
|
elseif string.match( line, "^%*") then -- LIST ITEMS
|
|
-- TODO reuse {wikilinks} code from paragraphs
|
|
-- web
|
|
local strippedline = string.sub(line, 3) -- remove "* "
|
|
local webtext = string.gsub( strippedline, "{(.-)}", function (wikiname)
|
|
return string.format("<a href='./%s.html'>%s</a>", pages[wikiname].slug, wikiname)
|
|
end)
|
|
|
|
local li = string.format("<li>%s</li>", webtext)
|
|
if not flags.list then -- start list
|
|
out.web = string.format("<ul>\n%s", li)
|
|
flags.list = true
|
|
else -- append list element
|
|
out.web = li
|
|
end
|
|
|
|
-- gem
|
|
local gemlinks = {}
|
|
local gemline = string.gsub( line, "{(.-)}", function (wikiname)
|
|
table.insert(gemlinks, string.format("=> ./%s.gmi %s",pages[wikiname].slug, wikiname))
|
|
return wikiname
|
|
end)
|
|
|
|
out.gem = gemline
|
|
-- append links if there were any
|
|
if #gemlinks > 0 then
|
|
out.gem = out.gem.."\n"..table.concat(gemlinks,"\n")
|
|
end
|
|
|
|
elseif string.match( line, "^=>") then -- LINKS
|
|
local dest, text = getLink( line )
|
|
if string.match( dest, "^%./") then --local links
|
|
local cleantext = string.match( text, "^{(.-)}$" ) -- see if wikilink and clean
|
|
local htmldest = string.gsub( dest, "gmi", "html" )
|
|
if cleantext ~= nil then -- wikilink
|
|
local close = closeTags()
|
|
out.web = string.format("%s<p><a href='%s' class='arrow'>%s</a></p>",close,htmldest,cleantext)
|
|
out.gem = string.format("=> %s %s",dest,cleantext)
|
|
|
|
elseif isImagePath( dest ) then -- images
|
|
local img = string.format('<img src="%s" alt="%s" loading="lazy"/>',dest,text)
|
|
if not flags.gallery then
|
|
local close = closeTags()
|
|
out.web = string.format("%s<gallery>\n%s", close, img)
|
|
flags.gallery = true
|
|
else
|
|
out.web = img
|
|
end
|
|
|
|
else -- other files
|
|
local close = closeTags()
|
|
out.web = string.format("%s<p><a href='%s'>%s</a></p>",close,dest,text)
|
|
end
|
|
|
|
else -- external links
|
|
out.web = string.format('<p><a href="%s" rel="external noreferrer noopener" target=_blank class="arrow">%s</a></p>', dest, text)
|
|
end
|
|
|
|
else -- PARAGRAPHS
|
|
-- web
|
|
-- search and replace wikilinks
|
|
local webline = string.gsub( line, "{(.-)}", function (wikiname)
|
|
return string.format("<a href='./%s.html'>%s</a>", pages[wikiname].slug, wikiname)
|
|
end)
|
|
if not flags.p then
|
|
out.web = string.format("<p>%s", webline)
|
|
flags.p = true
|
|
else
|
|
out.web = string.format("<br/>\n%s",webline)
|
|
end
|
|
|
|
-- gem
|
|
local gemlinks = {}
|
|
local gemline = string.gsub( line, "{(.-)}", function (wikiname)
|
|
table.insert(gemlinks, string.format("=> ./%s.gmi %s",pages[wikiname].slug, wikiname))
|
|
return wikiname
|
|
end)
|
|
|
|
out.gem = gemline
|
|
-- append links if there were any
|
|
if #gemlinks > 0 then
|
|
out.gem = out.gem.."\n"..table.concat(gemlinks,"\n")
|
|
end
|
|
end -- paragraphs
|
|
|
|
::insert:: -- insert line in table
|
|
for k,l in pairs(out) do
|
|
if l ~= nil then
|
|
table.insert( lines[k], l )
|
|
end
|
|
end
|
|
|
|
|
|
-- skip incoming links calculation if <pre> mode
|
|
if flags.pre then goto nextline end
|
|
|
|
-- calculate incoming links from outgoing
|
|
for outname in string.gmatch( line, "{([^ ]?.-[^ ]?)}" ) do
|
|
insertIncoming( outname, name )
|
|
end
|
|
|
|
::nextline::
|
|
count = count + 1
|
|
end -- end for line in f:lines()
|
|
|
|
-- finalize html
|
|
local close = closeTags()
|
|
if isPre then
|
|
table.insert( lines.web, "</pre>" )
|
|
elseif close~="" then
|
|
table.insert( lines.web, close )
|
|
end
|
|
|
|
table.insert( navlines, "</ul>" )
|
|
pagemeta.navcontent = table.concat( navlines, "\n" )
|
|
|
|
-- set templates
|
|
if string.match( pagemeta.lang, "^en" ) then
|
|
pagemeta.outs.web.templates = templates.webEn
|
|
pagemeta.outs.gem.templates = templates.gemEn
|
|
else
|
|
pagemeta.outs.web.templates = templates.webEs
|
|
pagemeta.outs.gem.templates = templates.gemEs
|
|
end
|
|
|
|
|
|
-- fill templates and write results
|
|
for key, out in pairs(pagemeta.outs) do
|
|
local fo = assert( io.open( out.path, "w" ) )
|
|
fo:write( fillTemplate(out.templates.header.template, pagemeta) )
|
|
fo:write( table.concat( lines[key],"\n" )) -- content
|
|
fo:write( "\n" )
|
|
fo:close()
|
|
end
|
|
|
|
-- close source file
|
|
f:close()
|
|
end
|
|
end
|
|
|
|
function secondPass() -- write incoming links and footer
|
|
|
|
for name,page in pairs(pages) do
|
|
local pagemeta = page
|
|
|
|
local doIncoming = true
|
|
if #pagemeta.incoming > 0 then
|
|
local gemlines = {}
|
|
local weblines = { "<p>" }
|
|
|
|
-- format list of incoming links
|
|
for i = 1, #pagemeta.incoming do
|
|
name = pagemeta.incoming[i]
|
|
slug = pages[name].slug
|
|
table.insert( gemlines, string.format("=> ./%s.gmi %s",slug,name) )
|
|
table.insert( weblines, string.format("<a href='./%s.html'>%s</a> ",slug,name) )
|
|
end
|
|
|
|
table.insert(weblines,"</p>")
|
|
|
|
page.gemincoming = table.concat( gemlines, "\n" )
|
|
page.webincoming = table.concat( weblines )
|
|
|
|
else
|
|
doIncoming = false
|
|
print( string.format("{%s} is an orphan!", name) )
|
|
end
|
|
|
|
-- fill incoming and footer templates and write results
|
|
for key, out in pairs(pagemeta.outs) do
|
|
local fo = assert( io.open( out.path, "a" ) )
|
|
if doIncoming then
|
|
fo:write( fillTemplate(out.templates.incoming.template, pagemeta) )
|
|
end
|
|
fo:write( fillTemplate(out.templates.footer.template, pagemeta) )
|
|
fo:close()
|
|
end
|
|
end
|
|
end
|
|
|
|
function initPageMetadata( name, date ) -- return a table with metadata
|
|
local meta = {
|
|
incoming = {},
|
|
slug = slugify(name), name = name,
|
|
ptitle = "", pdescription = "",
|
|
updatedate = date,
|
|
navcontent = "",
|
|
gemincoming = "", webincoming = "",
|
|
gemtranslation = "", webtranslation = ""
|
|
}
|
|
meta.outs = {
|
|
web = { path = string.format("%s/%s.html", webdir, meta.slug) },
|
|
gem = { path = string.format("%s/%s.gmi", gemdir, meta.slug) }
|
|
}
|
|
return meta
|
|
end
|
|
|
|
function genIndex()
|
|
local indexpath = "src/pages.gmo"
|
|
|
|
-- initialize and open index file
|
|
os.execute(string.format("cp templates/index.gmi %s",indexpath))
|
|
local index = assert( io.open(indexpath,"a" ) )
|
|
|
|
-- get gmo files in chronological order
|
|
local gmofiles = io.popen("ls -to --time-style=+%F src/*gmo")
|
|
local count = 0
|
|
for filename in gmofiles:lines() do
|
|
local date,basename = string.match(filename,"^.+%s(.-)%ssrc/(.-).gmo$")
|
|
local name = spacify(basename)
|
|
-- create an entry for each file
|
|
if name ~= "pages" then
|
|
local entry = string.format("=> ./%s.gmi {%s}\n", basename, name)
|
|
index:write(entry)
|
|
end
|
|
-- also initialize each name in the pages table
|
|
-- initialize page metadata
|
|
pages[ name ] = initPageMetadata( name, date )
|
|
count = count + 1
|
|
end
|
|
gmofiles:close()
|
|
index:close()
|
|
return count
|
|
end
|
|
|
|
function genLog()
|
|
-- generate the following log files:
|
|
-- * twtxt gemini and web
|
|
-- * atom feed gemini and web
|
|
-- * gmo log
|
|
-- * gmisub
|
|
|
|
local logsrc = assert( io.open( "src/log.txt", "r" ) )
|
|
|
|
local logs = {
|
|
gemtw = { path = string.format("%s/tw.txt",gemdir), head="templates/twheader.txt" },
|
|
webtw = { path = string.format("%s/tw.txt",webdir), head="templates/twheader.txt" },
|
|
gmolog = { path = "src/log.gmo", head="templates/logheader.gmi" },
|
|
gematom = { path = string.format("%s/atom.xml",gemdir), head="templates/gematomheader.txt" },
|
|
webatom = { path = string.format("%s/atom.xml",webdir), head="templates/webatomheader.txt" },
|
|
gemfeed = { path = string.format("%s/feed.gmi",gemdir), head="templates/feedheader.gmi" }
|
|
}
|
|
|
|
-- create and open logs
|
|
for _,log in pairs(logs) do
|
|
os.execute(string.format("cp %s %s",log.head,log.path))
|
|
log.f = assert( io.open( log.path, "a" ) )
|
|
end
|
|
|
|
logs.gematom.f:write(string.format("<updated>%sT12:00:00Z</updated>\n",fecha))
|
|
logs.webatom.f:write(string.format("<updated>%sT12:00:00Z</updated>\n",fecha))
|
|
|
|
|
|
-- process each line of log.txt
|
|
for line in logsrc:lines() do
|
|
local pattern = "^(.-)%s(.-)%s{(.-)}$"
|
|
local timestamp, text, wikilink = string.match(line,pattern)
|
|
local slug = slugify(wikilink)
|
|
local date = string.sub(timestamp,1,10) -- YYYY-MM-DD
|
|
|
|
-- gmo log
|
|
local gmoformat = "## %s\n%s\n=> ./%s.gmi {%s}\n\n"
|
|
logs.gmolog.f:write(string.format(gmoformat, date, text, slug, wikilink))
|
|
|
|
-- gmi sub
|
|
local gmisubformat = "=> ./%s.gmi %s %s\n"
|
|
logs.gemfeed.f:write(string.format(gmisubformat, slug, date, text))
|
|
|
|
-- twtxt
|
|
local weburl = string.format("https://%s/%s.html",meta.domain,slug)
|
|
local gemurl = string.format("gemini://%s/%s.gmi",meta.domain,slug)
|
|
local twtxtformat = "%s\t%s | %s\n"
|
|
logs.webtw.f:write(string.format(twtxtformat, timestamp, text, weburl))
|
|
logs.gemtw.f:write(string.format(twtxtformat, timestamp, text, gemurl))
|
|
|
|
-- atom
|
|
local weburldate = string.format("https://%s/%s.html#%s",meta.domain,slug,date)
|
|
local atomformat = [[
|
|
|
|
<entry>
|
|
<title>%s</title>
|
|
<updated>%s</updated>
|
|
<link rel='alternate' href='%s'/>
|
|
<id>%s</id>
|
|
</entry>
|
|
]]
|
|
logs.webatom.f:write(string.format(atomformat, text, timestamp, weburldate, weburldate))
|
|
logs.gematom.f:write(string.format(atomformat, text, timestamp, gemurl, gemurl))
|
|
end
|
|
|
|
|
|
--close logs
|
|
logs.webatom.f:write("</feed>\n")
|
|
logs.gematom.f:write("</feed>\n")
|
|
for _,log in pairs(logs) do
|
|
log.f:close()
|
|
end
|
|
|
|
logsrc:close()
|
|
end
|
|
|
|
|
|
-- THE SCRIPT
|
|
|
|
-- create directories
|
|
os.execute(string.format("mkdir -p %s/static %s/img %s/img",webdir,webdir,gemdir))
|
|
|
|
print(meta.fecha)
|
|
|
|
-- clean up
|
|
print("limpiando archivos previos...")
|
|
os.execute(string.format('find %s %s -regex ".+.\\(gmi\\|html\\)$" -exec rm {} +',webdir,gemdir))
|
|
|
|
-- copy images
|
|
print("copiando imágenes y otros archivos...")
|
|
os.execute( string.format("find src/img/ -type f -exec cp -vut %s/img/ {} +",webdir))
|
|
os.execute( string.format("find src/img/ -type f -exec cp -vut %s/img/ {} +",gemdir))
|
|
|
|
-- copy stylesheet and misc files
|
|
os.execute(string.format("cp -vu templates/estilo.css %s/static/",webdir))
|
|
|
|
local miscfiles = { "src/llave_sejo.asc" }
|
|
for k = 1, #miscfiles do
|
|
os.execute(string.format("cp -vu %s %s/", miscfiles[k], webdir))
|
|
os.execute(string.format("cp -vu %s %s/", miscfiles[k], gemdir))
|
|
end
|
|
|
|
|
|
-- generate log files
|
|
print("generando log...")
|
|
genLog()
|
|
|
|
-- generate index
|
|
print("generando índice de páginas...")
|
|
local n = genIndex()
|
|
|
|
-- start first pass
|
|
-- converting from gmo to gmi and html and calculating incoming links
|
|
print(string.format("convirtiendo %d páginas...",n))
|
|
firstPass()
|
|
|
|
-- second pass, write incoming links
|
|
print("escribiendo incoming links...")
|
|
secondPass()
|
|
|
|
-- update index files with home
|
|
os.execute("cp web/home.html web/index.html")
|
|
os.execute("cp gem/home.gmi gem/index.gmi")
|
|
|
|
print("listx!")
|