-record(state, {host,
out_dir,
dir_type,
+ dir_name,
+ file_format,
css_file,
access,
lang,
init([Host, Opts]) ->
OutDir = gen_mod:get_opt(outdir, Opts, "www/muc"),
DirType = gen_mod:get_opt(dirtype, Opts, subdirs),
+ DirName = gen_mod:get_opt(dirname, Opts, room_jid),
+ FileFormat = gen_mod:get_opt(file_format, Opts, html), % Allowed values: html|plaintext
CSSFile = gen_mod:get_opt(cssfile, Opts, false),
AccessLog = gen_mod:get_opt(access_log, Opts, muc_admin),
Timezone = gen_mod:get_opt(timezone, Opts, local),
{ok, #state{host = Host,
out_dir = OutDir,
dir_type = DirType,
+ dir_name = DirName,
+ file_format = FileFormat,
css_file = CSSFile,
access = AccessLog,
lang = Lang,
%%----------------------------------------------------------------------
%% Core
-build_filename_string(TimeStamp, OutDir, RoomJID, DirType) ->
+build_filename_string(TimeStamp, OutDir, RoomJID, DirType, DirName, FileFormat) ->
{{Year, Month, Day}, _Time} = TimeStamp,
- % Directory and file names
+ %% Directory and file names
{Dir, Filename, Rel} =
case DirType of
subdirs ->
[Year, Month, Day])),
{"", Date, "."}
end,
- Fd = filename:join([OutDir, RoomJID, Dir]),
- Fn = filename:join([Fd, Filename ++ ".html"]),
- Fnrel = filename:join([Rel, Dir, Filename ++ ".html"]),
+
+ RoomString = case DirName of
+ room_jid -> RoomJID;
+ room_name -> get_room_name(RoomJID)
+ end,
+ Extension = case FileFormat of
+ html -> ".html";
+ plaintext -> ".txt"
+ end,
+ Fd = filename:join([OutDir, RoomString, Dir]),
+ Fn = filename:join([Fd, Filename ++ Extension]),
+ Fnrel = filename:join([Rel, Dir, Filename ++ Extension]),
{Fd, Fn, Fnrel}.
-% calculate day before
+get_room_name(RoomJID) ->
+ JID = jlib:string_to_jid(RoomJID),
+ JID#jid.user.
+
+%% calculate day before
get_timestamp_daydiff(TimeStamp, Daydiff) ->
{Date1, HMS} = TimeStamp,
Date2 = calendar:gregorian_days_to_date(
calendar:date_to_gregorian_days(Date1) + Daydiff),
{Date2, HMS}.
-% Try to close the previous day log, if it exists
-close_previous_log(Fn, Images_dir) ->
+%% Try to close the previous day log, if it exists
+close_previous_log(Fn, Images_dir, FileFormat) ->
case file:read_file_info(Fn) of
{ok, _} ->
{ok, F} = file:open(Fn, [append]),
- %fw(F, "<div class=\"legend\">ejabberd/mod_muc log<span class=\"w3c\">"),
- fw(F, "<div class=\"legend\">"),
- fw(F, " <a href=\"http://www.ejabberd.im\"><img style=\"border:0\" src=\"~s/powered-by-ejabberd.png\" alt=\"Powered by ejabberd\"/></a>", [Images_dir]),
- fw(F, " <a href=\"http://www.erlang.org/\"><img style=\"border:0\" src=\"~s/powered-by-erlang.png\" alt=\"Powered by Erlang\"/></a>", [Images_dir]),
- fw(F, "<span class=\"w3c\">"),
- fw(F, " <a href=\"http://validator.w3.org/check?uri=referer\"><img style=\"border:0;width:88px;height:31px\" src=\"~s/valid-xhtml10.png\" alt=\"Valid XHTML 1.0 Transitional\" /></a>", [Images_dir]),
- fw(F, " <a href=\"http://jigsaw.w3.org/css-validator/\"><img style=\"border:0;width:88px;height:31px\" src=\"~s/vcss.png\" alt=\"Valid CSS!\"/></a>", [Images_dir]),
- fw(F, "</span></div></body></html>"),
+ write_last_lines(F, Images_dir, FileFormat),
file:close(F);
_ -> ok
end.
+write_last_lines(_, _, plaintext) ->
+ ok;
+write_last_lines(F, Images_dir, _FileFormat) ->
+ %%fw(F, "<div class=\"legend\">ejabberd/mod_muc log<span class=\"w3c\">"),
+ fw(F, "<div class=\"legend\">"),
+ fw(F, " <a href=\"http://www.ejabberd.im\"><img style=\"border:0\" src=\"~s/powered-by-ejabberd.png\" alt=\"Powered by ejabberd\"/></a>", [Images_dir]),
+ fw(F, " <a href=\"http://www.erlang.org/\"><img style=\"border:0\" src=\"~s/powered-by-erlang.png\" alt=\"Powered by Erlang\"/></a>", [Images_dir]),
+ fw(F, "<span class=\"w3c\">"),
+ fw(F, " <a href=\"http://validator.w3.org/check?uri=referer\"><img style=\"border:0;width:88px;height:31px\" src=\"~s/valid-xhtml10.png\" alt=\"Valid XHTML 1.0 Transitional\" /></a>", [Images_dir]),
+ fw(F, " <a href=\"http://jigsaw.w3.org/css-validator/\"><img style=\"border:0;width:88px;height:31px\" src=\"~s/vcss.png\" alt=\"Valid CSS!\"/></a>", [Images_dir]),
+ fw(F, "</span></div></body></html>").
+
add_message_to_log(Nick1, Message, RoomJID, Opts, State) ->
- Nick = htmlize(Nick1),
#state{out_dir = OutDir,
dir_type = DirType,
+ dir_name = DirName,
+ file_format = FileFormat,
css_file = CSSFile,
lang = Lang,
timezone = Timezone,
spam_prevention = NoFollow,
top_link = TopLink} = State,
Room = get_room_info(RoomJID, Opts),
-
+ Nick = htmlize(Nick1, FileFormat),
+ Nick2 = htmlize("<"++Nick1++">", FileFormat),
Now = now(),
TimeStamp = case Timezone of
local -> calendar:now_to_local_time(Now);
universal -> calendar:now_to_universal_time(Now)
end,
- {Fd, Fn, _Dir} = build_filename_string(TimeStamp, OutDir, Room#room.jid, DirType),
+ {Fd, Fn, _Dir} = build_filename_string(TimeStamp, OutDir, Room#room.jid, DirType, DirName, FileFormat),
{Date, Time} = TimeStamp,
- % Open file, create if it does not exist, create parent dirs if needed
+ %% Open file, create if it does not exist, create parent dirs if needed
case file:read_file_info(Fn) of
{ok, _} ->
{ok, F} = file:open(Fn, [append]);
TimeStampYesterday = get_timestamp_daydiff(TimeStamp, -1),
{_FdYesterday, FnYesterday, DatePrev} =
build_filename_string(
- TimeStampYesterday, OutDir, Room#room.jid, DirType),
+ TimeStampYesterday, OutDir, Room#room.jid, DirType, DirName, FileFormat),
TimeStampTomorrow = get_timestamp_daydiff(TimeStamp, 1),
{_FdTomorrow, _FnTomorrow, DateNext} =
build_filename_string(
- TimeStampTomorrow, OutDir, Room#room.jid, DirType),
+ TimeStampTomorrow, OutDir, Room#room.jid, DirType, DirName, FileFormat),
HourOffset = calc_hour_offset(TimeStamp),
put_header(F, Room, Datestring, CSSFile, Lang,
- HourOffset, DatePrev, DateNext, TopLink),
+ HourOffset, DatePrev, DateNext, TopLink, FileFormat),
- Images_dir = filename:join([OutDir, "images"]),
+ Images_dir = filename:join([OutDir, "images"]),
file:make_dir(Images_dir),
- create_image_files(Images_dir),
- Images_url = case DirType of
- subdirs -> "../../../images";
- plain -> "../images"
- end,
- close_previous_log(FnYesterday, Images_url)
+ create_image_files(Images_dir),
+ Images_url = case DirType of
+ subdirs -> "../../../images";
+ plain -> "../images"
+ end,
+ close_previous_log(FnYesterday, Images_url, FileFormat)
end,
- % Build message
+ %% Build message
Text = case Message of
roomconfig_change ->
- RoomConfig = roomconfig_to_string(Room#room.config, Lang),
- put_room_config(F, RoomConfig, Lang),
+ RoomConfig = roomconfig_to_string(Room#room.config, Lang, FileFormat),
+ put_room_config(F, RoomConfig, Lang, FileFormat),
io_lib:format("<font class=\"mrcm\">~s</font><br/>",
[?T("Chatroom configuration modified")]);
join ->
[Nick, ?T("leaves the room")]);
{leave, Reason} ->
io_lib:format("<font class=\"ml\">~s ~s: ~s</font><br/>",
- [Nick, ?T("leaves the room"), htmlize(Reason,NoFollow)]);
+ [Nick, ?T("leaves the room"), htmlize(Reason,NoFollow,FileFormat)]);
{kickban, "301", ""} ->
io_lib:format("<font class=\"mb\">~s ~s</font><br/>",
[Nick, ?T("has been banned")]);
{kickban, "301", Reason} ->
io_lib:format("<font class=\"mb\">~s ~s: ~s</font><br/>",
- [Nick, ?T("has been banned"), htmlize(Reason)]);
+ [Nick, ?T("has been banned"), htmlize(Reason,FileFormat)]);
{kickban, "307", ""} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick, ?T("has been kicked")]);
{kickban, "307", Reason} ->
io_lib:format("<font class=\"mk\">~s ~s: ~s</font><br/>",
- [Nick, ?T("has been kicked"), htmlize(Reason)]);
+ [Nick, ?T("has been kicked"), htmlize(Reason,FileFormat)]);
{kickban, "321", ""} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick, ?T("has been kicked because of an affiliation change")]);
[Nick, ?T("has been kicked because of a system shutdown")]);
{nickchange, OldNick} ->
io_lib:format("<font class=\"mnc\">~s ~s ~s</font><br/>",
- [htmlize(OldNick), ?T("is now known as"), Nick]);
+ [htmlize(OldNick,FileFormat), ?T("is now known as"), Nick]);
{subject, T} ->
io_lib:format("<font class=\"msc\">~s~s~s</font><br/>",
- [Nick, ?T(" has set the subject to: "), htmlize(T,NoFollow)]);
+ [Nick, ?T(" has set the subject to: "), htmlize(T,NoFollow,FileFormat)]);
{body, T} ->
case regexp:first_match(T, "^/me\s") of
{match, _, _} ->
io_lib:format("<font class=\"mne\">~s ~s</font><br/>",
- [Nick, string:substr(htmlize(T), 5)]);
+ [Nick, string:substr(htmlize(T,FileFormat), 5)]);
nomatch ->
- io_lib:format("<font class=\"mn\"><~s></font> ~s<br/>",
- [Nick, htmlize(T,NoFollow)])
+ io_lib:format("<font class=\"mn\">~s</font> ~s<br/>",
+ [Nick2, htmlize(T,NoFollow,FileFormat)])
end
end,
{Hour, Minute, Second} = Time,
{_, _, Microsecs} = Now,
STimeUnique = io_lib:format("~s.~w", [STime, Microsecs]),
- % Write message
- file:write(F, io_lib:format("<a id=\"~s\" name=\"~s\" href=\"#~s\" class=\"ts\">[~s]</a> ~s~n",
- [STimeUnique, STimeUnique, STimeUnique, STime, Text])),
+ %% Write message
+ fw(F, io_lib:format("<a id=\"~s\" name=\"~s\" href=\"#~s\" class=\"ts\">[~s]</a> ",
+ [STimeUnique, STimeUnique, STimeUnique, STime]) ++ Text, FileFormat),
- % Close file
+ %% Close file
file:close(F),
ok.
end.
-% {ok, F1}=file:open("valid-xhtml10.png", [read]).
-% {ok, F1b}=file:read(F1, 1000000).
-% c("../../ejabberd/src/jlib.erl").
-% jlib:encode_base64(F1b).
+%% {ok, F1}=file:open("valid-xhtml10.png", [read]).
+%% {ok, F1b}=file:read(F1, 1000000).
+%% c("../../ejabberd/src/jlib.erl").
+%% jlib:encode_base64(F1b).
image_base64("powered-by-erlang.png") ->
- "iVBORw0KGgoAAAANSUhEUgAAAGUAAAAfCAYAAAD+xQNoAAADN0lEQVRo3u1a"
+ "iVBORw0KGgoAAAANSUhEUgAAAGUAAAAfCAYAAAD+xQNoAAADN0lEQVRo3u1a"
"P0waURz+rjGRRQ+nUyRCYmJyDPTapDARaSIbTUjt1gVSh8ZW69aBAR0cWLSx"
"CXWp59LR1jbdqKnGxoQuRZZrSYyHEVM6iZMbHewROA7u3fHvkr5vOn737vcu"
"33ffu9/vcQz+gef5Cij6CkmSGABgFEH29r5SVvqIsTEOHo8HkiQxDBXEOjg9"
"KF/d/wX3cJvREzl1vAAAAABJRU5ErkJggg==";
image_base64("valid-xhtml10.png") ->
- "iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAAEjEcpEAAACiFBMVEUAAADe"
+ "iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAAEjEcpEAAACiFBMVEUAAADe"
"5+fOezmtra3ejEKlhELvvWO9WlrehELOe3vepaWclHvetVLGc3PerVKcCAj3"
"vVqUjHOUe1JjlL0xOUpjjL2UAAC91ueMrc7vrVKlvdbW3u+EpcbO3ufO1ucY"
"WpSMKQi9SiF7e3taWkoQEAiMczkQSoxaUkpzc3O1lEoICACEazEhGAgIAACE"
"QZ+RYfpNE/4Xosmq7jsZAJsAAAAASUVORK5CYII=";
image_base64("vcss.png") ->
- "iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAABUFvrSAAABKVBMVEUAAAAj"
+ "iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAABUFvrSAAABKVBMVEUAAAAj"
"Ix8MR51ZVUqAdlmdnZ3ejEWLDAuNjY1kiMG0n2d9fX19Ghfrp1FtbW3y39+3"
"Ph6lIRNdXV2qJBFcVUhcVUhPT0/dsmpUfLr57+/u7u4/PDWZAACZAADOp1Gd"
"GxG+SyTgvnNdSySzk16+mkuxw+BOS0BOS0DOzs7MzMy4T09RRDwsJBG+vr73"
"AElFTkSuQmCC";
image_base64("powered-by-ejabberd.png") ->
- "iVBORw0KGgoAAAANSUhEUgAAAGUAAAAfCAMAAADJG/NaAAAAw1BMVEUAAAAj"
+ "iVBORw0KGgoAAAANSUhEUgAAAGUAAAAfCAMAAADJG/NaAAAAw1BMVEUAAAAj"
"BgYtBAM5AwFCAAAYGAJNAABcAABIDQ5qAAAoJRV7AACFAAAoKSdJHByLAAAw"
"Lwk1NQA1MzFJKyo4NxtDQQBEQT5KSCxSTgBSUBlgQ0JYSEpZWQJPUU5hYABb"
"W0ZiYClcW1poaCVwbQRpaDhzYWNsakhuZ2VrbFZ8dwCEgAB3dnd4d2+OjACD"
"AElFTkSuQmCC".
create_image_files(Images_dir) ->
- Filenames = [
- "powered-by-ejabberd.png",
- "powered-by-erlang.png",
- "valid-xhtml10.png",
- "vcss.png"
- ],
- lists:foreach(
- fun(Filename) ->
- Filename_full = filename:join([Images_dir, Filename]),
- {ok, F} = file:open(Filename_full, [write]),
- Image = jlib:decode_base64(image_base64(Filename)),
- io:format(F, "~s", [Image]),
- file:close(F)
- end,
- Filenames),
- ok.
-
-fw(F, S, O) -> io:format(F, S ++ "~n", O).
-fw(F, S) -> fw(F, S, []).
+ Filenames = ["powered-by-ejabberd.png",
+ "powered-by-erlang.png",
+ "valid-xhtml10.png",
+ "vcss.png"
+ ],
+ lists:foreach(
+ fun(Filename) ->
+ Filename_full = filename:join([Images_dir, Filename]),
+ {ok, F} = file:open(Filename_full, [write]),
+ Image = jlib:decode_base64(image_base64(Filename)),
+ io:format(F, "~s", [Image]),
+ file:close(F)
+ end,
+ Filenames),
+ ok.
-put_header(F, Room, Date, CSSFile, Lang, Hour_offset, Date_prev, Date_next, Top_link) ->
+fw(F, S) -> fw(F, S, [], html).
+
+fw(F, S, O) when is_list(O) ->
+ fw(F, S, O, html);
+fw(F, S, FileFormat) when is_atom(FileFormat) ->
+ fw(F, S, [], FileFormat).
+
+fw(F, S, O, FileFormat) ->
+ S1 = io_lib:format(S ++ "~n", O),
+ S2 = case FileFormat of
+ html ->
+ S1;
+ plaintext ->
+ {ok, Res, _} = regexp:gsub(S1, "<[^>]*>", ""),
+ Res
+ end,
+ io:format(F, S2, []).
+
+put_header(_, _, _, _, _, _, _, _, _, plaintext) ->
+ ok;
+put_header(F, Room, Date, CSSFile, Lang, Hour_offset, Date_prev, Date_next, Top_link, FileFormat) ->
fw(F, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"),
fw(F, "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"~s\" lang=\"~s\">", [Lang, Lang]),
fw(F, "<head>"),
{"", ""} -> ok;
{SuA, Su} -> fw(F, "<div class=\"roomsubject\">~s~s~s</div>", [SuA, ?T(" has set the subject to: "), Su])
end,
- RoomConfig = roomconfig_to_string(Room#room.config, Lang),
- put_room_config(F, RoomConfig, Lang),
+ RoomConfig = roomconfig_to_string(Room#room.config, Lang, FileFormat),
+ put_room_config(F, RoomConfig, Lang, FileFormat),
Time_offset_str = case Hour_offset<0 of
true -> io_lib:format("~p", [Hour_offset]);
false -> io_lib:format("+~p", [Hour_offset])
fw(F, "else {document.getElementById(e).style.display='none';}}"),
fw(F, "</script>").
-put_room_config(F, RoomConfig, Lang) ->
+put_room_config(_F, _RoomConfig, _Lang, plaintext) ->
+ ok;
+put_room_config(F, RoomConfig, Lang, _FileFormat) ->
{_, Now2, _} = now(),
fw(F, "<div class=\"rc\">"),
fw(F, "<div class=\"rct\" onclick=\"sh('a~p');return false;\">~s</div>", [Now2, ?T("Room Configuration")]),
%% The default behaviour is to ignore the nofollow spam prevention on links
%% (NoFollow=false)
htmlize(S1) ->
- htmlize(S1, false).
+ htmlize(S1, html).
+
+htmlize(S1, plaintext) ->
+ S1;
+htmlize(S1, FileFormat) ->
+ htmlize(S1, false, FileFormat).
%% The NoFollow parameter tell if the spam prevention should be applied to the link found
%% true means 'apply nofollow on links'.
-htmlize(S1, NoFollow) ->
+htmlize(S1, _NoFollow, plaintext) ->
+ S1;
+htmlize(S1, NoFollow, _FileFormat) ->
S2_list = string:tokens(S1, "\n"),
lists:foldl(
fun(Si, Res) ->
config = Opts
}.
-roomconfig_to_string(Options, Lang) ->
- % Get title, if available
+roomconfig_to_string(Options, Lang, FileFormat) ->
+ %% Get title, if available
Title = case lists:keysearch(title, 1, Options) of
{value, Tuple} -> [Tuple];
false -> []
end,
-
- % Remove title from list
+
+ %% Remove title from list
Os1 = lists:keydelete(title, 1, Options),
-
- % Order list
+
+ %% Order list
Os2 = lists:sort(Os1),
-
- % Add title to ordered list
+
+ %% Add title to ordered list
Options2 = Title ++ Os2,
lists:foldl(
T ->
case Opt of
password -> "<div class=\"rcoe\">" ++ OptText ++ "</div>";
- title -> "<div class=\"rcot\">" ++ ?T("Room title") ++ ": \"" ++ htmlize(T) ++ "\"</div>";
+ title -> "<div class=\"rcot\">" ++ ?T("Room title") ++ ": \"" ++ htmlize(T, FileFormat) ++ "\"</div>";
_ -> "\"" ++ T ++ "\""
end
end,
get_proc_name(Host) -> gen_mod:get_module_proc(Host, ?PROCNAME).
calc_hour_offset(TimeHere) ->
- TimeZero = calendar:now_to_universal_time(now()),
- TimeHereHour = calendar:datetime_to_gregorian_seconds(TimeHere) div 3600,
- TimeZeroHour = calendar:datetime_to_gregorian_seconds(TimeZero) div 3600,
- TimeHereHour - TimeZeroHour.
+ TimeZero = calendar:now_to_universal_time(now()),
+ TimeHereHour = calendar:datetime_to_gregorian_seconds(TimeHere) div 3600,
+ TimeZeroHour = calendar:datetime_to_gregorian_seconds(TimeZero) div 3600,
+ TimeHereHour - TimeZeroHour.