💾 Archived View for gemini.conman.org › extensions › port70 › handlers › blog.lua captured on 2021-12-03 at 14:04:38.
⬅️ Previous capture (2020-10-31)
-=-=-=-=-=-=-
-- *********************************************************************** -- -- Copyright 2020 by Sean Conner. -- -- This program is free software: you can redistribute it and/or modify it -- under the terms of the GNU General Public License as published by the -- Free Software Foundation, either version 3 of the License, or (at your -- option) any later version. -- -- This program is distributed in the hope that it will be useful, but -- WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -- Public License for more details. -- -- You should have received a copy of the GNU General Public License along -- with this program. If not, see <http://www.gnu.org/licenses/>. -- -- Comments, questions and criticisms can be sent to: sean@conman.org -- -- ======================================================================= -- -- Code to handle blog requests. -- -- *********************************************************************** -- luacheck: globals init handler last_link -- luacheck: ignore 611 631 local syslog = require "org.conman.syslog" local fsys = require "org.conman.fsys" local magic = require "org.conman.fsys.magic" local date = require "org.conman.date" local wrapt = require "org.conman.string".wrapt local url = require "org.conman.parsers.url.data" + require "org.conman.parsers.url.gopher" + require "org.conman.parsers.url" local mimetype = require "org.conman.parsers.mimetype" local gtypes = require "org.conman.const.gopher-types" local io = require "io" local os = require "os" local table = require "table" local string = require "string" local utf8 = require "utf8" local mklink = require "port70.mklink" local readfile = require "port70.readfile" local html = require "org.conman.app.port70.handlers.blog.html" local uurl = require "org.conman.app.port70.handlers.blog.url-util" local format = require "org.conman.app.port70.handlers.blog.format" local blog = require "org.conman.app.mod_blog" local require = require local ipairs = ipairs local tonumber = tonumber _ENV = {} -- *********************************************************************** -- usage: init() -- desc: Intialize the handler module -- *********************************************************************** function init(conf) conf.blog = blog.init(conf.config) return true end -- *********************************************************************** function handler(config,request,ios) local CONF = require "port70.CONF" local first = blog.bookend("first") local last = blog.bookend("last") local port do if CONF.network.port == 70 or CONF.network.port == 'gopher' then port = "" else port = string.format(":%d",CONF.network.port) end end -- ---------------------------------------------------------------- local function affiliate(location) for _,aff in ipairs(config.blog.affiliate) do if location.scheme == aff.proto then return string.format(aff.link,location.path) end end return uurl.toa(location) end -- ---------------------------------------------------------------- local function xlat_links(when,links) local function filetype(filename) if fsys.access(filename,"r") then local mime = mimetype:match(magic(filename)) local ftype if not mime then ftype = gtypes.binary else if mime.type:find("text/",1,true) then ftype = gtypes.file elseif mime.type:find("image/gif",1,true) then ftype = gtypes.gif elseif mime.type:find("image/",1,true) then ftype = gtypes.image elseif mime.type:find("audio/",1,true) then ftype = gtypes.sound elseif mime.type:find("video/",1,true) then ftype = gtypes.video else ftype = gtypes.binary end end return ftype end return gtypes.file end -- ---------------------------------------------------------------- for i,link in ipairs(links) do local u = url:match(link) if u.scheme then ios:write(string.format("[%d] %s\n",i,affiliate(u))) else local t = blog.tumbler.new(link,first,last) if t then if t.ustart == 'part' or t.range or t.file and t.filename ~= "" then if t.file then local filename = config.basedir .. string.format("/%04d/%02d/%02d/%s",t.start.year,t.start.month,t.start.day,t.filename) ios:write(string.format("[%d] gopher://%s%s/%s%s%s\n", i, CONF.network.host, port, filetype(filename), config.selector, link )) else ios:write(string.format("[%d] gopher://%s%s/0%s%s\n", i, CONF.network.host, port, config.selector, link )) end else ios:write(string.format("[%d] gopher://%s%s/1%s%s\n", i, CONF.network.host, port, config.selector, link )) end else if link:match "^/" then if link:find("/thisday",1,true) then ios:write(string.format("[%d] gopher://%s%s/0%s%s\n", i, CONF.network.host, port, config.selector, link:sub(2,-1) )) else ios:write(string.format("[%d] %s%s\n",i,config.blog.url,link:sub(2,-1))) end else if link:find("parallel/",1,true) then ios:write(string.format("[%d] %s%s\n",i,config.blog.url,link)) else local filename = string.format( "%s/%04d/%02d/%02d/%s", config.blog.basedir, when.year, when.month, when.day, link ) ios:write(string.format("{%d] gopher://%s%s/%s%s%04d/%02d/%02d/%s\n", i, CONF.network.host, port, filetype(filename), config.selector, when.year, when.month, when.day, link )) end end end end end end -- ---------------------------------------------------------------- local function display(tumbler) local function gopherlink(entry) return string.format("gopher://%s%s/%s%s%04d/%02d/%02d.%d", CONF.network.host, port, gtypes.file, config.selector, entry.when.year, entry.when.month, entry.when.day, entry.when.part ) end for entry in blog.entries(tumbler) do local x = html(entry.body) local text,links = format(x) local t = wrapt(entry.title,77) table.insert(t,1,"") table.insert(t,2,"* * * * *") table.insert(t,3,"") table.insert(t,"") if tumbler.range then table.insert(t,os.date("%A, %B %d, %Y",os.time(tumbler.start))) table.insert(t,gopherlink(entry)) table.insert(t,"") end for i = 1 , #t do t[i] = string.rep(" ",(80 - utf8.len(t[i])) // 2) .. t[i] end ios:write(table.concat(t,"\n"),'\n') ios:write(text) xlat_links(tumbler.start,links) end return true end -- ---------------------------------------------------------------- local function thisday(req) local month,day = req:match("^thisday/?(%d+)/(%d+)") if not month then local now = os.date("*t") month = now.month day = now.day else month = tonumber(month) day = tonumber(day) end for year = first.year , last.year do local x = string.format("%04d/%02d/%02d",year,month,day) local tumbler = blog.tumbler.new(x,first,last) if tumbler then tumbler.range = true display(tumbler) end end if ios.__wbytes == 0 then ios:write("There are no entries for this date.\n") end return true end -- ---------------------------------------------------------------- -- If the request is empty, display the year menu -- ---------------------------------------------------------------- if request.rest == "" then for i = last.year , first.year , -1 do local itop = string.format("%04d",i) ios:write(mklink { type = 'dir', display = itop, selector = config.selector .. itop, }) end return true end -- ---------------------------------------------------------------- -- There's a tumbler. Parse and handle -- ---------------------------------------------------------------- local tumbler = blog.tumbler.new(request.rest,first,last) if not tumbler then if request.rest:match "^thisday" then return thisday(request.rest) else ios:write(mklink { type = 'error', display = 'Selector not found', selector = request.rest }) return false end end if tumbler.file and tumbler.filename ~= "" then local filename = blog.filename(tumbler) if filename:match("%.x%-html$") then local f,err = io.open(filename,"rb") if not f then syslog('error',"%s: %s",filename,err) ios:write(mklink { type = 'error', display = 'Selector not found', selector = request }) return false end local text,links = format(html(f:read("a"))) ios:write(text) xlat_links(tumbler.start,links) f:close() return true else return readfile(blog.filename(tumbler),"\1",config,request,ios) end elseif tumbler.range then return display(tumbler) elseif tumbler.ustart == 'year' then local when = { year = 2021 , month = 1 , day = 1 } for i = 1 , 12 do if tumbler.start.year == first.year and i >= first.month or tumbler.start.year == last.year and i <= last.month or tumbler.start.year > first.year and tumbler.start.year < last.year then when.month = i local d = os.time(when) ios:write(mklink { type = 'dir', display = os.date("%B",d), selector = string.format("%s%04d/%02d",config.selector,tumbler.start.year,i) }) end end return true elseif tumbler.ustart == 'month' then local entries for day = 1 , date.daysinmonth(tumbler.start) do tumbler.start.day = day local list = blog.day_titles(tumbler.start) if #list > 0 then entries = true ios:write(mklink { type = 'dir', display = string.format("%04d/%02d/%02d",tumbler.start.year,tumbler.start.month,tumbler.start.day), selector = string.format("%s%04d/%02d/%02d",config.selector,tumbler.start.year,tumbler.start.month,tumbler.start.day), }) for i,title in ipairs(list) do ios:write(mklink { type = 'file', display = title, selector = string.format("%s%04d/%02d/%02d.%d",config.selector,tumbler.start.year,tumbler.start.month,tumbler.start.day,i), }) end end end if not entries then ios:write(mklink { type = 'info', display = "There are no entries this month" }) end return true elseif tumbler.ustart == 'day' then local list = blog.day_titles(tumbler.start) for i,title in ipairs(list) do ios:write(mklink { type = 'file', display = title, selector = string.format("%s%04d/%02d/%02d.%d",config.selector,tumbler.start.year,tumbler.start.month,tumbler.start.day,i), }) end return true else return display(tumbler) end end -- *********************************************************************** -- usage: link = last_link() -- desc: return a gopher link for the latest blog entry -- return: link (string) gopher link -- *********************************************************************** function last_link(selector) local last = blog.bookend("last") return string.format( "%s%d/%02d/%02d.%d", selector, last.year, last.month, last.day, last.part ) end -- *********************************************************************** return _ENV