local st = require "util.stanza"; local mt = require"util.multitable"; local datetime = require"util.datetime"; local jid_split = require"util.jid".split; local nodeprep = require"util.encodings".stringprep.nodeprep; local uuid = require"util.uuid".generate; local it = require"util.iterators"; local gettime = require"socket".gettime; local url = require"socket.url"; local xml_escape = st.xml_escape; local t_concat = table.concat; local os_time, os_date = os.time, os.date; local archive = module:open_store("muc_log", "archive"); -- Support both old and new MUC code local mod_muc = module:depends"muc"; local rooms = rawget(mod_muc, "rooms"); local each_room = rawget(mod_muc, "each_room") or function() return it.values(rooms); end; local new_muc = not rooms; if new_muc then rooms = module:shared"muc/rooms"; end local get_room_from_jid = rawget(mod_muc, "get_room_from_jid") or function (jid) return rooms[jid]; end local function get_room(name) local jid = name .. '@' .. module.host; return get_room_from_jid(jid); end module:depends"http"; local function render(template, values) -- This function takes a string template and a table of values. -- Sequences like {name} in the template string are substituted -- with values from the table, optionally depending on a modifier -- symbol. -- -- Variants are: -- {name} is substituted for values["name"] and is XML escaped -- {name? sub-template } renders a sub-template if values["name"] is false-ish -- {name& sub-template } renders a sub-template if values["name"] is true-ish -- {name# sub-template } renders a sub-template using an array of values -- {name!} is substituted *without* XML escaping return (template:gsub("%b{}", function (block) local name, opt, e = block:sub(2, -2):match("^([%a_][%w_]*)(%p?)()"); local value = values[name]; if opt == '#' then if not value or not value[1] then return ""; end local out, subtpl = {}, block:sub(e+1, -2); for i=1, #value do out[i] = render(subtpl, value[i]); end return t_concat(out); elseif opt == '&' then if not value then return ""; end return render(block:sub(e+1, -2), values); elseif opt == '?' and not value then return render(block:sub(e+1, -2), values); elseif value ~= nil then if type(value) ~= "string" then value = tostring(value); end if opt ~= '!' then return xml_escape(value); end return value; end end)); end local template; do local template_file = module:get_option_string(module.name .. "_template", module.name .. ".html"); template_file = assert(module:load_resource(template_file)); template = template_file:read("*a"); template_file:close(); end -- local base_url = module:http_url() .. '/'; -- TODO: Generate links in a smart way local get_link do local link, path = { path = '/' }, { "", "", is_directory = true }; function get_link(room, date) path[1], path[2] = room, date; path.is_directory = not date; link.path = url.build_path(path); return url.build(link); end end local function public_room(room) if type(room) == "string" then room = get_room(room); end return (room and not (room.get_hidden or room.is_hidden)(room) and not (room.get_members_only or room.is_members_only)(room) and room._data.logging == true); end local function sort_Y(a,b) return a.year > b.year end local function sort_m(a,b) return a.n > b.n end local t_diff = os_time(os_date("*t")) - os_time(os_date("!*t")); local function time(t) return os_time(t) + t_diff; end local function find_once(room, query, retval) if query then query.limit = 1; else query = { limit = 1 }; end local iter, err = archive:find(room, query); if not iter then return iter, err; end if retval then return select(retval, iter()); end return iter(); end local function years_page(event, path) local response = event.response; local room = nodeprep(path:match("^(.*)/$")); if not room or not public_room(room) then return end local dates = mt.new(); module:log("debug", "Find all dates with messages"); local next_day; repeat local when = find_once(room, { start = next_day; with = "message 1 and tmp.wday == 2 then days = {}; weeks[#weeks+1] = { days = days }; current_day = 1; end days[current_day], current_day = { wday = tmp.wday, day = i, href = days_t[i] and datetime.date(days_t[i]) }, current_day+1; end end table.sort(year, sort_m); end table.sort(years, sort_Y); response.headers.content_type = "text/html; charset=utf-8"; return render(template, { title = get_room(room):get_name(); jid = get_room(room).jid; years = years; links = { { href = "../", rel = "up", text = "Back to room list" }, }; }); end local function logs_page(event, path) local response = event.response; local room, date = path:match("^(.-)/(%d%d%d%d%-%d%d%-%d%d)$"); room = nodeprep(room); if not room then return years_page(event, path); end if not public_room(room) then return end local logs, i = {}, 1; local iter, err = archive:find(room, { ["start"] = datetime.parse(date.."T00:00:00Z"); ["end"] = datetime.parse(date.."T23:59:59Z"); -- with = "message