generator and source files for the compudanzas.net site
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

560 lines
16 KiB

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("<","&lt;"):gsub(">","&gt;")
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
-- TODO apply the sanitization to everything?
-- web
local strippedline = sanitize(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 = { "<ul>" }
-- 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("<li><a href='./%s.html'>%s</a></li>",slug,name) )
end
table.insert(weblines,"</ul>")
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",meta.fecha))
logs.webatom.f:write(string.format("<updated>%sT12:00:00Z</updated>\n",meta.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", "src/biosejo.xml" }
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!")