forked from mio/gemwriter
416 lines
14 KiB
Lua
416 lines
14 KiB
Lua
local env = require("env")
|
|
local lang = require("lang.en_US")
|
|
local util = require("util")
|
|
|
|
|
|
local writer = {}
|
|
writer.docs = {}
|
|
writer.conf = {}
|
|
writer.posts = {}
|
|
|
|
|
|
writer.docs.get_lang_opts = [[
|
|
Return a table of available language translations for the app interface.
|
|
]]
|
|
function writer.get_lang_opts()
|
|
local lang_opts = {}
|
|
for l = 1, #env.defaults.lang_opts do
|
|
if util.module_exists("lang." .. env.defaults.lang_opts[l]) then
|
|
table.insert(lang_opts, env.defaults.lang_opts[l])
|
|
end
|
|
end
|
|
return lang_opts
|
|
end
|
|
|
|
|
|
writer.docs.gen_config = [[
|
|
Generate a default config directory.
|
|
]]
|
|
function writer.gen_config(lang_pref)
|
|
-- Set lang
|
|
if lang_pref ~= nil and util.module_exists("lang." .. lang_pref) then
|
|
lang = require("lang." .. lang_pref)
|
|
env.defaults_toml = string.gsub(env.defaults_toml, env.general.app_lang,
|
|
lang_pref)
|
|
end
|
|
|
|
util.make_dir(writer.conf.config_dir)
|
|
for name, file in pairs(env.defaults.config_files) do
|
|
-- Check each file individually anyway to avoid overwriting existing
|
|
-- files
|
|
if util.read_file(writer.conf.config_dir .. "/" .. file) == "" then
|
|
if file == env.defaults.config_files.config then
|
|
util.write_file(writer.conf.config_dir .. "/" .. file,
|
|
env.defaults_toml)
|
|
else
|
|
util.write_file(writer.conf.config_dir .. "/" .. file, lang[name])
|
|
end
|
|
end
|
|
end
|
|
-- Create capsule and gemlog directories
|
|
util.make_dir(env.capsules.main.capsule_dir)
|
|
util.make_dir(env.capsules.main.gemlog_dir)
|
|
end
|
|
|
|
|
|
writer.docs.parse_config = [[
|
|
Read the config and template files, and load the values into a table.
|
|
]]
|
|
function writer.parse_config(config_file)
|
|
local lines = util.split_lines(config_file)
|
|
local config_group = env.toml_vars.general
|
|
local line_cap = ""
|
|
writer.conf.capsules = {}
|
|
for l = 1, #lines do
|
|
-- Config group
|
|
if string.sub(lines[l], 1, 1) == "[" then
|
|
for tv, label in pairs(env.toml_vars) do
|
|
if string.find(lines[l], label) ~= nil then config_group = label end
|
|
end
|
|
if config_group == env.toml_vars.capsules then
|
|
local _, lb_sep = string.find(lines[l], "%[" .. config_group .. "%.")
|
|
local rb_sep, _ = string.find(lines[l], "%]")
|
|
if lb_sep ~= nil then
|
|
line_cap = string.sub(lines[l], lb_sep + 1, rb_sep - 1)
|
|
writer.conf.capsules[line_cap] = {}
|
|
end
|
|
end
|
|
|
|
-- Config variables (ignore comments)
|
|
elseif (string.sub(lines[l], 1, 1) ~= "#") and
|
|
(string.find(lines[l], "=") ~= nil) then
|
|
local eq_sep, _ = string.find(lines[l], " = ")
|
|
local qt_sep, _ = string.find(lines[l], "\"")
|
|
local k = string.sub(lines[l], 1, eq_sep - 1)
|
|
local val = string.sub(lines[l], eq_sep + 4, string.len(lines[l]) - 1)
|
|
if qt_sep == nil then
|
|
val = string.sub(lines[l], eq_sep + 3, string.len(lines[l]))
|
|
end
|
|
-- Load settings by key and their values to the config table
|
|
if config_group == env.toml_vars.general then
|
|
writer.conf[k] = val
|
|
elseif config_group == env.toml_vars.capsules then
|
|
-- Convert boolean values
|
|
if (val == "\"true\"") or (val == "\"false\"") then
|
|
writer.conf.capsules[line_cap][k] = util.to_bool(val)
|
|
else
|
|
writer.conf.capsules[line_cap][k] = val
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- If the main capsule id is not the app default, update the id
|
|
if (writer.conf.cap_id == env.general.main_capsule) and
|
|
(writer.conf.main_capsule ~= env.general.main_capsule) then
|
|
writer.conf.cap_id = writer.conf.main_capsule
|
|
end
|
|
-- Add an alias to the selected capsule's table
|
|
writer.conf.cap = writer.conf.capsules[writer.conf.cap_id]
|
|
|
|
-- Fill in the remaining defaults
|
|
for k, v in pairs(env.defaults) do
|
|
if writer.conf[k] == nil then
|
|
writer.conf[k] = env.defaults[k]
|
|
end
|
|
end
|
|
|
|
-- Replace shell variables in paths to make them usable
|
|
for c_id, _ in pairs(writer.conf.capsules) do
|
|
writer.conf.capsules[c_id]["capsule_dir"] = util.replace_shell_vars(
|
|
writer.conf.capsules[c_id]["capsule_dir"])
|
|
writer.conf.capsules[c_id]["gemlog_dir"] = util.replace_shell_vars(
|
|
writer.conf.capsules[c_id]["gemlog_dir"])
|
|
-- Create capsule and gemlog directories
|
|
util.make_dir(writer.conf.capsules[c_id]["capsule_dir"])
|
|
util.make_dir(writer.conf.capsules[c_id]["gemlog_dir"])
|
|
end
|
|
|
|
-- Set lang
|
|
if util.module_exists("lang." .. writer.conf.app_lang) then
|
|
lang = require("lang." .. writer.conf.app_lang)
|
|
end
|
|
|
|
-- Custom templates override lang defaults (ignore config)
|
|
for name, file in pairs(env.defaults.config_files) do
|
|
if name ~= "config" then
|
|
local template = util.read_file(writer.conf.config_dir .. "/" .. file)
|
|
if template ~= "" then lang[name] = template end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
writer.docs.load_config = [[
|
|
Load an existing config. Exit if a capsule is not found.
|
|
]]
|
|
function writer.load_config(cap_id)
|
|
-- Check capsule id exists, abort if no valid capsule found
|
|
if (cap_id == nil) or (string.find(cap_id, " ") ~= nil) then
|
|
writer.conf.cap_id = env.general.main_capsule
|
|
else
|
|
writer.conf.cap_id = cap_id
|
|
end
|
|
local cap_label = "%[" .. env.toml_vars.capsules .. "%." ..
|
|
tostring(cap_id) .. "%]"
|
|
if (string.find(writer.conf.config_file, cap_label) == nil) and
|
|
(writer.conf.cap_id ~= env.general.main_capsule) then
|
|
print(lang.errs.invalid_cap_id)
|
|
os.exit()
|
|
end
|
|
writer.parse_config(writer.conf.config_file)
|
|
end
|
|
|
|
|
|
writer.docs.add_gemtext = [[
|
|
Given a title and mode, create a new gemtext file. Supported modes: page,
|
|
post.
|
|
]]
|
|
function writer.add_gemtext(title, mode)
|
|
local file_name = title
|
|
local post_title = title
|
|
if ((title == "") or (title == nil)) and (mode == "post") then
|
|
file_name = os.date(writer.conf.post_slug_date_format) .. "-" ..
|
|
writer.conf.post_slug .. writer.conf.post_ext
|
|
post_title = writer.conf.post_slug
|
|
elseif ((title == "") or (title == nil)) and (mode == "page") then
|
|
file_name = writer.conf.page_slug .. writer.conf.post_ext
|
|
post_title = writer.conf.page_slug
|
|
else
|
|
-- Strip punctuation from the title and convert to file name
|
|
local delim = "delim-" .. tostring(math.random(90000000000))
|
|
file_name = string.gsub(file_name, " ", delim)
|
|
if mode == "post" then
|
|
file_name = os.date(writer.conf.post_slug_date_format) .. "-" ..
|
|
string.gsub(file_name, "%p", "") .. writer.conf.post_ext
|
|
else
|
|
file_name = string.gsub(file_name, "%p", "") .. writer.conf.post_ext
|
|
end
|
|
file_name = string.lower(string.gsub(file_name, delim, "-"))
|
|
end
|
|
|
|
local text = lang.post
|
|
local file_path = writer.conf.cap.gemlog_dir .. "/" .. file_name
|
|
if mode == "page" then
|
|
text = lang.page
|
|
file_path = writer.conf.cap.capsule_dir .. "/" .. file_name
|
|
end
|
|
text = string.gsub(text, lang.tpl_vars.post.date,
|
|
os.date(writer.conf.post_date_format))
|
|
text = string.gsub(text, lang.tpl_vars.post.author, writer.conf.cap.author)
|
|
text = string.gsub(text, lang.tpl_vars.post.title, post_title)
|
|
|
|
util.write_file(file_path, text)
|
|
return file_name
|
|
end
|
|
|
|
|
|
writer.docs.get_posts = [[
|
|
Get a list of gemlog posts and return the file names and their contents in a
|
|
table.
|
|
]]
|
|
function writer.get_posts()
|
|
local ls = util.ls_grep(writer.conf.cap.gemlog_dir, writer.conf.post_ext)
|
|
local posts = {}
|
|
local n = 0
|
|
local files_list = util.split_str(ls)
|
|
for f = 1, #files_list do
|
|
if files_list[f] ~= writer.conf.cap.index_page then
|
|
n = n + 1
|
|
posts[n] = { files_list[f], util.read_file(writer.conf.cap.gemlog_dir ..
|
|
"/" .. files_list[f]) }
|
|
end
|
|
end
|
|
return posts
|
|
end
|
|
|
|
|
|
writer.docs.get_posts_meta = [[
|
|
Extract gemlog posts metadata and return them as an associative table.
|
|
]]
|
|
function writer.get_posts_meta()
|
|
local meta = {}
|
|
local posts = writer.get_posts()
|
|
for e = 1, #posts do
|
|
meta[e] = {}
|
|
if string.find(posts[e][2], "# ") == nil then
|
|
meta[e]["title"] = posts[e][1]
|
|
meta[e]["content"] = posts[e][2]
|
|
else
|
|
meta[e]["title"] = util.extract_str(posts[e][2], "# ", "\n")
|
|
hi1, hi2 = string.find(posts[e][2], "# ")
|
|
meta[e]["content"] = string.sub(posts[e][2], hi1,
|
|
string.len(posts[e][2]))
|
|
end
|
|
if string.find(posts[e][2], "author: ") == nil then
|
|
meta[e]["author"] = writer.conf.cap.author
|
|
else
|
|
meta[e]["author"] = util.extract_str(posts[e][2], "author: ", "\n")
|
|
end
|
|
if string.find(posts[e][2], "date: ") == nil then
|
|
meta[e]["date"] = util.extract_file_date(writer.conf.cap.gemlog_dir,
|
|
posts[e][1])
|
|
if (meta[e]["date"] == "") or (meta[e]["date"] == nil) then
|
|
meta[e]["date"] = os.date(writer.conf.post_date_format)
|
|
end
|
|
else
|
|
meta[e]["date"] = util.extract_str(posts[e][2], "date: ", "\n")
|
|
end
|
|
if string.find(posts[e][2], "summary: ") == nil then
|
|
meta[e]["summary"] = meta[e]["title"]
|
|
else
|
|
meta[e]["summary"] = util.extract_str(posts[e][2], "summary: ", "\n")
|
|
end
|
|
if string.find(posts[e][2], "tags: ") == nil then
|
|
meta[e]["tags"] = ""
|
|
else
|
|
meta[e]["tags"] = util.extract_str(posts[e][2], "tags: ", "\n")
|
|
end
|
|
meta[e]["url"] = writer.conf.cap.log_url .. "/" .. posts[e][1]
|
|
end
|
|
return meta
|
|
end
|
|
|
|
|
|
writer.docs.gen_index_page = [[
|
|
Generate a gemlog index page listing all gemlog posts.
|
|
]]
|
|
function writer.gen_index_page()
|
|
local index_text = util.replace_vars(lang.index, lang.tpl_vars.log,
|
|
writer.conf.cap)
|
|
local posts_text = ""
|
|
-- Reverse insert links to log posts, newest first
|
|
for e = #writer.posts, 1, -1 do
|
|
local post_date = util.split_str(writer.posts[e]["date"], "(.*)[T]")[1]
|
|
-- If post date cannot be extracted, revert to current date
|
|
if post_date == nil then
|
|
post_date = os.date(writer.conf.post_date_format)
|
|
end
|
|
-- Get post filename from the url
|
|
local fi1, fi2 = string.find(writer.posts[e]["url"],
|
|
writer.conf.post_file_pattern)
|
|
posts_text = posts_text .. "\n=> " ..
|
|
string.sub(writer.posts[e]["url"], fi1 + 1, fi2) .. " " ..
|
|
post_date .. " " .. writer.posts[e]["title"]
|
|
end
|
|
index_text = string.gsub(index_text, lang.tpl_vars.index.posts,
|
|
posts_text)
|
|
util.write_file(writer.conf.cap.gemlog_dir .. "/" ..
|
|
writer.conf.cap.index_page, index_text)
|
|
end
|
|
|
|
|
|
writer.docs.gen_atom_feed = [[
|
|
Generate an Atom feed of gemlog posts.
|
|
]]
|
|
function writer.gen_atom_feed()
|
|
local feed_meta = {
|
|
date = os.date(writer.conf.post_date_format),
|
|
url = writer.conf.cap.url .. "/" .. writer.conf.cap.atom_feed,
|
|
}
|
|
local feed_text = util.replace_vars(lang.atom_header, lang.tpl_vars.log,
|
|
writer.conf.cap)
|
|
feed_text = util.replace_vars(feed_text, lang.tpl_vars.feed, feed_meta)
|
|
local feed_post = ""
|
|
-- Reverse insert log posts, newest first
|
|
for e = #writer.posts, 1, -1 do
|
|
feed_post = lang.atom_entry
|
|
writer.posts[e]["content"] = util.replace_feed_entities(
|
|
writer.posts[e]["content"])
|
|
feed_post = util.replace_vars(feed_post, lang.tpl_vars.post,
|
|
writer.posts[e])
|
|
feed_text = feed_text .. feed_post
|
|
end
|
|
feed_text = feed_text .. lang.atom_footer
|
|
feed_text = util.replace_feed_entities(feed_text, "post")
|
|
|
|
util.write_file(writer.conf.cap.gemlog_dir .. "/" ..
|
|
writer.conf.cap.atom_feed, feed_text)
|
|
end
|
|
|
|
|
|
writer.docs.publish = [[
|
|
Transfer a capsule's contents to a remote location.
|
|
]]
|
|
function writer.publish()
|
|
if writer.conf.cap.transfer_mode == "rsync" then
|
|
os.execute(writer.conf.cap.rsync_exec .. " " ..
|
|
writer.conf.cap.rsync_options .. " " .. writer.conf.cap.capsule_dir ..
|
|
"/ " .. writer.conf.cap.rsync_dest .. "/")
|
|
else
|
|
os.execute(writer.conf.cap.scp_exec .. " -r " ..
|
|
writer.conf.cap.capsule_dir .. "/* " .. writer.conf.cap.scp_target)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
local cli = {}
|
|
cli.docs = {}
|
|
|
|
|
|
cli.docs.handle_args = [[
|
|
Match command-line options to the respective application functions, passing
|
|
any additional arguments to the functions.
|
|
]]
|
|
function cli.handle_args(args)
|
|
if (args[1] ~= env.cli_opts.help) or (args[1] ~= env.cli_opts.version) then
|
|
local config_new = false
|
|
writer.conf.config_dir = util.replace_shell_vars(env.defaults.config_dir)
|
|
writer.conf.config_file = util.read_file(writer.conf.config_dir .. "/" ..
|
|
env.defaults.config_files.config)
|
|
-- No config found
|
|
if (writer.conf.config_file == "") and (not config_new) then
|
|
writer.gen_config(args[2])
|
|
config_new = true
|
|
end
|
|
-- Config found, read the configuration and load the values to an
|
|
-- associative table, writer.conf.
|
|
if (writer.conf.config_file ~= "") and (not config_new) then
|
|
writer.load_config(args[2])
|
|
end
|
|
end
|
|
|
|
if (args[1] == env.cli_opts.index) or (args[1] == env.cli_opts.publish) then
|
|
writer.posts = writer.get_posts_meta()
|
|
if writer.conf.cap.gen_index_page then writer.gen_index_page() end
|
|
if writer.conf.cap.gen_atom_feed then writer.gen_atom_feed() end
|
|
end
|
|
|
|
if args[1] == env.cli_opts.config then
|
|
writer.gen_config(args[2])
|
|
print(string.format(lang.msgs.load_config, writer.conf.config_dir))
|
|
|
|
elseif (args[1] == env.cli_opts.post) or (args[1] == env.cli_opts.page) then
|
|
local file_name = ""
|
|
if args[3] ~= nil then
|
|
file_name = writer.add_gemtext(args[3], args[1])
|
|
else
|
|
file_name = writer.add_gemtext(args[2], args[1])
|
|
end
|
|
print(string.format(lang.msgs.add_gemtext, file_name))
|
|
|
|
elseif args[1] == env.cli_opts.index then
|
|
print(lang.msgs.index)
|
|
|
|
elseif args[1] == env.cli_opts.publish then
|
|
writer.publish()
|
|
print(lang.msgs.publish)
|
|
|
|
elseif args[1] == env.cli_opts.help then
|
|
print(string.format(lang.msgs.help_usage, env.app.exec_name) ..
|
|
string.format(lang.msgs.help_opts, env.cli_opts.config,
|
|
env.cli_opts.page, env.cli_opts.post, env.cli_opts.index,
|
|
env.cli_opts.publish, env.cli_opts.help, env.cli_opts.version) ..
|
|
string.format(lang.msgs.help_lang_opts,
|
|
table.concat(writer.get_lang_opts(), ", ")))
|
|
|
|
elseif args[1] == env.cli_opts.version then
|
|
print(env.app.name .. " " .. env.app.version ..
|
|
" (" .. env.app.last_updated .. ")")
|
|
end
|
|
end
|
|
|
|
|
|
cli.handle_args(arg)
|