posix = require 'posix' json = require 'json' http = require 'lcurl' require 'tokyocabinet' require 'corz' CACHE = "/srv/tears/embed.db" cache = tokyocabinet.bdbnew() cache:open(CACHE, cache.OWRITER + cache.OREADER) function userurl(basename, basecache, home, fallback) local path = ("/home/%s/%s"):format(home, basename) local cache = ("/srv/www/users/%s"):format(basecache) if not posix.stat(path) then return fallback end if not posix.stat(cache) then posix.unlink(cache) posix.link(path, cache, true) end return ("https://intern.radionova.no/users/%s"):format(basecache) end function userfile(basename, home, fallback) local path = ("/home/%s/%s"):format(home, basename) local ok, data = pcall(function() return io.open(path):read("*l") end) if ok then return data:gsub("[\r\n]*", "") else return fallback end end function oembed(endpoint, id) local url = endpoint .. id local cached = cache:get(id) if cached then local data = json.decode(cached) return data.html end local body = "" local result = http.easy{url=url, writefunction=function(s) body = body .. s end}:perform() if result:getinfo(http.INFO_RESPONSE_CODE) == 200 then cache:put(id, body) local data = json.decode(body) return "
" .. data.html .. "
" else return ("Klarte ikke embedde %s :("):format(url) end end function twitter(id) return oembed("https://publish.twitter.com/oembed?url=", id) end function youtube(id) return oembed("http://www.youtube.com/oembed?url=", id) end function soundcloud(id) return oembed("https://soundcloud.com/oembed?format=json&url=", id) end function vimeo(id) return oembed("https://developer.vimeo.com/apis/oembed.json?url=", id) end function twitch(id) return oembed("https://api.twitch.tv/v4/oembed?url=", id) end function instagram(id) return oembed("https://api.instagram.com/oembed/?url=", id) end function flickr(id) return oembed("https://www.flickr.com/services/oembed?url=", id) end function facebook(id) return oembed("https://www.facebook.com/plugins/video/oembed.json?url=", id) end local function printf(...) io.write(string.format(...)) end local function setfenv(fn, env) local i = 1 while true do local name = debug.getupvalue(fn, i) if name == "_ENV" then debug.upvaluejoin(fn, i, (function() return env end), 1) break elseif not name then break end i = i + 1 end return fn end local html = {} function html.print(...) printf(...) end function html.tag(tag, options) options = options or {} local text = options.text; options.text = nil printf("<%s", tag) for k, v in pairs(options) do if v and v ~= "" then printf(" %s='%s'", k, v) else printf(" %s", k) end end printf(">") if text then printf(text or "") printf("", tag) end printf("\n") end function html.dl(facts) if #facts < 1 then return end printf("
") for i,v in ipairs(facts) do local term, fact = v:match("^(.-)%s*:%s*(.-)$") html.tag("dt", { text=term }) html.tag("dd", { text=fact }) end printf("
") end function html.label(options) options.text = options.text or error("no text") html.tag("label", options) end function html.orz(options) if options.inline then html.tag("a", { class = 'orz', text = 'orz'}) return end local n = options.name or "orz" local v = options.value or "//:0" local uri = options.uri or "/orz/" html.tag('img', { class='orz', ['data-name']=n, src=v, ['data-uri']=uri}) html.tag('input', { name=n, value=v, type='hidden'}) html.tag('a', { class='orz', ['data-name']=n, ['data-uri']=uri, text='orz'}) end function html.infobox(options) html.print("") end function html.date(options) local n = options.name or "date" local s = options.size or 10 local v = options.value or "" html.tag('input', { type='date', name=n, size=s, value=v}) end function html.text(options) local n = options.name or "text" local s = options.size or 18 local v = options.value or "" html.tag("input", { type='text', name=n, size=s, value=v}) end function html.hidden(options) local n = options.name or "hidden" local v = options.value or "" html.tag('input', { type='hidden', name=n, value=v}) end function html.textarea(options) local n = options.name or "textarea" local v = (options.value or ""):gsub("[\n]$", "") local cols = options.cols or 72 local rows = options.rows or 12 html.tag('textarea', { name=n, cols=cols, rows=rows, text=v}) end function html.select(k, options, selected) local p = "" printf("") end function html.submit(value) printf("", value) end function html.img(options) html.tag('img', { src=options.src, alt=options.alt}) end function html.p(options) html.tag("p", options) end function html.h1(...) printf("

%s

", ... or '') end function html.h2(...) printf("

%s

", ... or '') end function html.h3(...) printf("

%s

", ... or '') end function html.marxup(raw) corz.marxup(raw, io.output()) end help = { html = html, os = os, posix = posix, pairs = pairs, ipairs = ipairs } lupin = {} lupin.blueprints = {} lupin.blueprints.default = { pure = function(body, data) print(data.title) if data.type ~= "default" then printf("# type: %s\n", data.type) end data.title = nil data.type = nil print(body) for key, value in pairs(data) do if key == "info" then local term for i,v in ipairs(data.info) do if i % 2 == 1 then term = v else if #data.info >= i then print(("# info: %s: %s"):format(term, v)) end end end else print(("# %s: %s"):format(key, value)) end end end, form = function(body, data) help.form.text('overskrift', data.title) end, html = function(body, data) if data.mode == "title" then printf("

%s

", data.title) return elseif data.mode == "full" then printf("

%s

", data.title) end corz.marxup(body, io.output()) if not next(data) then return end print("
") for key, value in pairs(data) do printf("
%s
%s
", key, value) end print("
") end } function help.blueprint(id) return setmetatable({ id = id }, { __index = lupin.blueprints.default }) end function lupin.loadblueprints(path) local blueprints = posix.glob(path .. "/*.lua") if blueprints then for i, file in pairs(blueprints) do local chunk = loadfile(file) setfenv(chunk, help) local blueprint = chunk() lupin.blueprints[blueprint.id] = blueprint end end end function lupin.transform(path, format, env, mode) if path ~= "-" then io.input(path) end local raw, data = "", {} data.info = {} if env then data.type = os.getenv("POST_type") or "default" data.title = os.getenv("POST_title") or "default" raw = os.getenv("POST_text") or "" raw = raw:gsub("\r\n", "\n") raw = raw:gsub("[\n]+$", "") raw = raw:match("(.-)%s*$") if lupin.blueprints[data.type] and lupin.blueprints[data.type].env then lupin.blueprints[data.type].env(data) end if os.getenv("POST_info") then for line in os.getenv("POST_info"):gmatch("[^\r\n]+") do table.insert(data.info, line) end end else for line in io.lines() do if line:match("^#") then local key, value = line:match("^#%s*(.-):%s*(.+)") if key and value and key == "info" then table.insert(data.info, value) elseif key and value then data[key] = value end else if not data.title then data.title = line else line = line:gsub("[\r\n]*", "") raw = raw .. line .. "\n" end end end data.type = data.type or "default" end data.mode = mode if data.mode == "short" then raw = data.lead or raw:match("[^\r\n]+") end if path ~= "-" then local stat = posix.stat(path) local pwd = posix.getpasswd(stat.uid) data.author = pwd.name data.authorname = userfile("navn", data.author, data.author) data.group = posix.getgroup(stat.gid).name data.groupname = userfile("navn", data.group, data.group) if data.published then data.date = os.date("%A %d. %B %Y, kl. %H.%M", data.published) else local root = "/srv/www/happiest.place/tears/artikler" y, m, d = path:match(root .. "/(.*)/(.*)/(.*)/(.*)") data.date = ("%s/%s/%s"):format(y, m, d) end data.portrait = userurl("selfie.jpg", data.author .. ".jpg", data.author) end if format == "later" then data.raw = raw local info = data.info data.info = {} for i,v in ipairs(info) do local term, fact = v:match("^(.-)%s*:%s*(.-)$") table.insert(data.info, { term = term, fact = fact }) end data.text = function() local f = io.tmpfile() corz.marxup(data.raw, f) f:seek("set") local result = f:read("*a") result = result:gsub("$%s*twitter:%s*(.-)\n", twitter) result = result:gsub("$%s*youtube:%s*(.-)\n", youtube) result = result:gsub("$%s*soundcloud:%s*(.-)\n", soundcloud) result = result:gsub("$%s*twitch:%s*(.-)\n", twitch) result = result:gsub("$%s*vimeo:%s*(.-)\n", vimeo) result = result:gsub("$%s*instagram:%s*(.-)\n", instagram) result = result:gsub("$%s*flickr:%s*(.-)\n", flickr) result = result:gsub("$%s*facebook:%s*(.-)\n", facebook) return result end return data end lupin.blueprints[data.type][format](raw, data) end