aboutsummaryrefslogtreecommitdiff
path: root/src/mod_muc_log.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_muc_log.erl')
-rw-r--r--src/mod_muc_log.erl861
1 files changed, 299 insertions, 562 deletions
diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl
index d5ced9116..08498869f 100644
--- a/src/mod_muc_log.erl
+++ b/src/mod_muc_log.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Mar 2006 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -27,8 +27,6 @@
-protocol({xep, 334, '0.2'}).
--behaviour(ejabberd_config).
-
-author('badlop@process-one.net').
-behaviour(gen_server).
@@ -36,22 +34,18 @@
-behaviour(gen_mod).
%% API
--export([start_link/2, start/2, stop/1, transform_module_options/1,
+-export([start/2, stop/1, reload/3, get_url/1,
check_access_log/2, add_to_log/5]).
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
- mod_opt_type/1, opt_type/1, depends/2]).
+ mod_opt_type/1, mod_options/1, depends/2]).
--include("ejabberd.hrl").
-include("logger.hrl").
-
--include("jlib.hrl").
--include("mod_muc.hrl").
+-include("xmpp.hrl").
-include("mod_muc_room.hrl").
+-include("translate.hrl").
--define(T(Text), translate:translate(Lang, Text)).
--define(PROCNAME, ejabberd_mod_muc_log).
-record(room, {jid, title, subject, subject_author, config}).
-define(PLAINTEXT_CO, <<"ZZCZZ">>).
@@ -74,20 +68,15 @@
%%====================================================================
%% API
%%====================================================================
-start_link(Host, Opts) ->
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
-
start(Host, Opts) ->
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
- transient, 1000, worker, [?MODULE]},
- supervisor:start_child(ejabberd_sup, ChildSpec).
+ gen_mod:start_child(?MODULE, Host, Opts).
stop(Host) ->
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- gen_server:call(Proc, stop),
- supervisor:delete_child(ejabberd_sup, Proc).
+ gen_mod:stop_child(?MODULE, Host).
+
+reload(Host, NewOpts, _OldOpts) ->
+ Proc = get_proc_name(Host),
+ gen_server:cast(Proc, {reload, NewOpts}).
add_to_log(Host, Type, Data, Room, Opts) ->
gen_server:cast(get_proc_name(Host),
@@ -101,13 +90,18 @@ check_access_log(Host, From) ->
Res -> Res
end.
-transform_module_options(Opts) ->
- lists:map(
- fun({top_link, {S1, S2}}) ->
- {top_link, [{S1, S2}]};
- (Opt) ->
- Opt
- end, Opts).
+-spec get_url(#state{}) -> {ok, binary()} | error.
+get_url(#state{room = Room, host = Host, server_host = ServerHost}) ->
+ case mod_muc_log_opt:url(ServerHost) of
+ undefined -> error;
+ URL ->
+ case mod_muc_log_opt:dirname(ServerHost) of
+ room_jid ->
+ {ok, <<URL/binary, $/, Room/binary, $@, Host/binary>>};
+ room_name ->
+ {ok, <<URL/binary, $/, Room/binary>>}
+ end
+ end.
depends(_Host, _Opts) ->
[{mod_muc, hard}].
@@ -115,60 +109,10 @@ depends(_Host, _Opts) ->
%%====================================================================
%% gen_server callbacks
%%====================================================================
-init([Host, Opts]) ->
- OutDir = gen_mod:get_opt(outdir, Opts,
- fun iolist_to_binary/1,
- <<"www/muc">>),
- DirType = gen_mod:get_opt(dirtype, Opts,
- fun(subdirs) -> subdirs;
- (plain) -> plain
- end, subdirs),
- DirName = gen_mod:get_opt(dirname, Opts,
- fun(room_jid) -> room_jid;
- (room_name) -> room_name
- end, room_jid),
- FileFormat = gen_mod:get_opt(file_format, Opts,
- fun(html) -> html;
- (plaintext) -> plaintext
- end, html),
- FilePermissions = gen_mod:get_opt(file_permissions, Opts,
- fun(SubOpts) ->
- F = fun({mode, Mode}, {_M, G}) ->
- {Mode, G};
- ({group, Group}, {M, _G}) ->
- {M, Group}
- end,
- lists:foldl(F, {644, 33}, SubOpts)
- end, {644, 33}),
- CSSFile = gen_mod:get_opt(cssfile, Opts,
- fun iolist_to_binary/1,
- false),
- AccessLog = gen_mod:get_opt(access_log, Opts,
- fun acl:access_rules_validator/1,
- muc_admin),
- Timezone = gen_mod:get_opt(timezone, Opts,
- fun(local) -> local;
- (universal) -> universal
- end, local),
- Top_link = gen_mod:get_opt(top_link, Opts,
- fun([{S1, S2}]) ->
- {iolist_to_binary(S1),
- iolist_to_binary(S2)}
- end, {<<"/">>, <<"Home">>}),
- NoFollow = gen_mod:get_opt(spam_prevention, Opts,
- fun(B) when is_boolean(B) -> B end,
- true),
- Lang = ejabberd_config:get_option(
- {language, Host},
- fun iolist_to_binary/1,
- ?MYLANG),
- {ok,
- #logstate{host = Host, out_dir = OutDir,
- dir_type = DirType, dir_name = DirName,
- file_format = FileFormat, css_file = CSSFile,
- file_permissions = FilePermissions,
- access = AccessLog, lang = Lang, timezone = Timezone,
- spam_prevention = NoFollow, top_link = Top_link}}.
+init([Host|_]) ->
+ process_flag(trap_exit, true),
+ Opts = gen_mod:get_module_opts(Host, ?MODULE),
+ {ok, init_state(Host, Opts)}.
handle_call({check_access_log, ServerHost, FromJID}, _From, State) ->
Reply = acl:match_rule(ServerHost, State#logstate.access, FromJID),
@@ -176,13 +120,17 @@ handle_call({check_access_log, ServerHost, FromJID}, _From, State) ->
handle_call(stop, _From, State) ->
{stop, normal, ok, State}.
+handle_cast({reload, Opts}, #logstate{host = Host}) ->
+ {noreply, init_state(Host, Opts)};
handle_cast({add_to_log, Type, Data, Room, Opts}, State) ->
case catch add_to_log2(Type, Data, Room, Opts, State) of
{'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]);
_ -> ok
end,
{noreply, State};
-handle_cast(_Msg, State) -> {noreply, State}.
+handle_cast(Msg, State) ->
+ ?WARNING_MSG("Unexpected cast: ~p", [Msg]),
+ {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
@@ -193,18 +141,35 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+init_state(Host, Opts) ->
+ OutDir = mod_muc_log_opt:outdir(Opts),
+ DirType = mod_muc_log_opt:dirtype(Opts),
+ DirName = mod_muc_log_opt:dirname(Opts),
+ FileFormat = mod_muc_log_opt:file_format(Opts),
+ FilePermissions = mod_muc_log_opt:file_permissions(Opts),
+ CSSFile = mod_muc_log_opt:cssfile(Opts),
+ AccessLog = mod_muc_log_opt:access_log(Opts),
+ Timezone = mod_muc_log_opt:timezone(Opts),
+ Top_link = mod_muc_log_opt:top_link(Opts),
+ NoFollow = mod_muc_log_opt:spam_prevention(Opts),
+ Lang = ejabberd_option:language(Host),
+ #logstate{host = Host, out_dir = OutDir,
+ dir_type = DirType, dir_name = DirName,
+ file_format = FileFormat, css_file = CSSFile,
+ file_permissions = FilePermissions,
+ access = AccessLog, lang = Lang, timezone = Timezone,
+ spam_prevention = NoFollow, top_link = Top_link}.
+
add_to_log2(text, {Nick, Packet}, Room, Opts, State) ->
case has_no_permanent_store_hint(Packet) of
false ->
- case {fxml:get_subtag(Packet, <<"subject">>),
- fxml:get_subtag(Packet, <<"body">>)}
- of
- {false, false} -> ok;
- {false, SubEl} ->
- Message = {body, fxml:get_tag_cdata(SubEl)},
+ case {Packet#message.subject, Packet#message.body} of
+ {[], []} -> ok;
+ {[], Body} ->
+ Message = {body, xmpp:get_text(Body)},
add_message_to_log(Nick, Message, Room, Opts, State);
- {SubEl, _} ->
- Message = {subject, fxml:get_tag_cdata(SubEl)},
+ {Subj, _} ->
+ Message = {subject, xmpp:get_text(Subj)},
add_message_to_log(Nick, Message, Room, Opts, State)
end;
true -> ok
@@ -249,18 +214,18 @@ build_filename_string(TimeStamp, OutDir, RoomJID,
{Dir, Filename, Rel} = case DirType of
subdirs ->
SYear =
- iolist_to_binary(io_lib:format("~4..0w",
+ (str:format("~4..0w",
[Year])),
SMonth =
- iolist_to_binary(io_lib:format("~2..0w",
+ (str:format("~2..0w",
[Month])),
- SDay = iolist_to_binary(io_lib:format("~2..0w",
+ SDay = (str:format("~2..0w",
[Day])),
{fjoin([SYear, SMonth]), SDay,
<<"../..">>};
plain ->
Date =
- iolist_to_binary(io_lib:format("~4..0w-~2..0w-~2..0w",
+ (str:format("~4..0w-~2..0w-~2..0w",
[Year,
Month,
Day])),
@@ -280,7 +245,7 @@ build_filename_string(TimeStamp, OutDir, RoomJID,
{Fd, Fn, Fnrel}.
get_room_name(RoomJID) ->
- JID = jid:from_string(RoomJID), JID#jid.user.
+ JID = jid:decode(RoomJID), JID#jid.user.
%% calculate day before
get_timestamp_daydiff(TimeStamp, Daydiff) ->
@@ -305,26 +270,26 @@ write_last_lines(F, Images_dir, _FileFormat) ->
fw(F, <<"<div class=\"legend\">">>),
fw(F,
<<" <a href=\"http://www.ejabberd.im\"><img "
- "style=\"border:0\" src=\"~s/powered-by-ejabbe"
+ "style=\"border:0\" src=\"~ts/powered-by-ejabbe"
"rd.png\" alt=\"Powered by ejabberd - robust, scalable and extensible XMPP server\"/></a>">>,
[Images_dir]),
fw(F,
<<" <a href=\"http://www.erlang.org/\"><img "
- "style=\"border:0\" src=\"~s/powered-by-erlang"
+ "style=\"border:0\" src=\"~ts/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;h"
- "eight:31px\" src=\"~s/valid-xhtml10.png\" "
+ "eight:31px\" src=\"~ts/valid-xhtml10.png\" "
"alt=\"Valid XHTML 1.0 Transitional\" "
"/></a>">>,
[Images_dir]),
fw(F,
<<" <a href=\"http://jigsaw.w3.org/css-validato"
"r/\"><img style=\"border:0;width:88px;height:"
- "31px\" src=\"~s/vcss.png\" alt=\"Valid "
+ "31px\" src=\"~ts/vcss.png\" alt=\"Valid "
"CSS!\"/></a>">>,
[Images_dir]),
fw(F, <<"</span></div></body></html>">>).
@@ -349,7 +314,7 @@ add_message_to_log(Nick1, Message, RoomJID, Opts,
Room = get_room_info(RoomJID, Opts),
Nick = htmlize(Nick1, FileFormat),
Nick2 = htmlize_nick(Nick1, FileFormat),
- Now = p1_time_compat:timestamp(),
+ Now = erlang:timestamp(),
TimeStamp = case Timezone of
local -> calendar:now_to_local_time(Now);
universal -> calendar:now_to_universal_time(Now)
@@ -393,8 +358,8 @@ add_message_to_log(Nick1, Message, RoomJID, Opts,
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">>)]);
+ io_lib:format("<font class=\"mrcm\">~ts</font><br/>",
+ [tr(Lang, ?T("Chatroom configuration modified"))]);
{roomconfig_change, Occupants} ->
RoomConfig = roomconfig_to_string(Room#room.config,
Lang, FileFormat),
@@ -402,70 +367,70 @@ add_message_to_log(Nick1, Message, RoomJID, Opts,
RoomOccupants = roomoccupants_to_string(Occupants,
FileFormat),
put_room_occupants(F, RoomOccupants, Lang, FileFormat),
- io_lib:format("<font class=\"mrcm\">~s</font><br/>",
- [?T(<<"Chatroom configuration modified">>)]);
+ io_lib:format("<font class=\"mrcm\">~ts</font><br/>",
+ [tr(Lang, ?T("Chatroom configuration modified"))]);
join ->
- io_lib:format("<font class=\"mj\">~s ~s</font><br/>",
- [Nick, ?T(<<"joins the room">>)]);
+ io_lib:format("<font class=\"mj\">~ts ~ts</font><br/>",
+ [Nick, tr(Lang, ?T("joins the room"))]);
leave ->
- io_lib:format("<font class=\"ml\">~s ~s</font><br/>",
- [Nick, ?T(<<"leaves the room">>)]);
+ io_lib:format("<font class=\"ml\">~ts ~ts</font><br/>",
+ [Nick, tr(Lang, ?T("leaves the room"))]);
{leave, Reason} ->
- io_lib:format("<font class=\"ml\">~s ~s: ~s</font><br/>",
- [Nick, ?T(<<"leaves the room">>),
+ io_lib:format("<font class=\"ml\">~ts ~ts: ~ts</font><br/>",
+ [Nick, tr(Lang, ?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">>),
+ {kickban, 301, <<"">>} ->
+ io_lib:format("<font class=\"mb\">~ts ~ts</font><br/>",
+ [Nick, tr(Lang, ?T("has been banned"))]);
+ {kickban, 301, Reason} ->
+ io_lib:format("<font class=\"mb\">~ts ~ts: ~ts</font><br/>",
+ [Nick, tr(Lang, ?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">>),
+ {kickban, 307, <<"">>} ->
+ io_lib:format("<font class=\"mk\">~ts ~ts</font><br/>",
+ [Nick, tr(Lang, ?T("has been kicked"))]);
+ {kickban, 307, Reason} ->
+ io_lib:format("<font class=\"mk\">~ts ~ts: ~ts</font><br/>",
+ [Nick, tr(Lang, ?T("has been kicked")),
htmlize(Reason, FileFormat)]);
- {kickban, <<"321">>, <<"">>} ->
- io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
+ {kickban, 321, <<"">>} ->
+ io_lib:format("<font class=\"mk\">~ts ~ts</font><br/>",
[Nick,
- ?T(<<"has been kicked because of an affiliation "
- "change">>)]);
- {kickban, <<"322">>, <<"">>} ->
- io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
+ tr(Lang, ?T("has been kicked because of an affiliation "
+ "change"))]);
+ {kickban, 322, <<"">>} ->
+ io_lib:format("<font class=\"mk\">~ts ~ts</font><br/>",
[Nick,
- ?T(<<"has been kicked because the room has "
- "been changed to members-only">>)]);
- {kickban, <<"332">>, <<"">>} ->
- io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
+ tr(Lang, ?T("has been kicked because the room has "
+ "been changed to members-only"))]);
+ {kickban, 332, <<"">>} ->
+ io_lib:format("<font class=\"mk\">~ts ~ts</font><br/>",
[Nick,
- ?T(<<"has been kicked because of a system "
- "shutdown">>)]);
+ tr(Lang, ?T("has been kicked because of a system "
+ "shutdown"))]);
{nickchange, OldNick} ->
- io_lib:format("<font class=\"mnc\">~s ~s ~s</font><br/>",
+ io_lib:format("<font class=\"mnc\">~ts ~ts ~ts</font><br/>",
[htmlize(OldNick, FileFormat),
- ?T(<<"is now known as">>), Nick]);
+ tr(Lang, ?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: ">>),
+ io_lib:format("<font class=\"msc\">~ts~ts~ts</font><br/>",
+ [Nick, tr(Lang, ?T(" has set the subject to: ")),
htmlize(T, NoFollow, FileFormat)]);
{body, T} ->
case {ejabberd_regexp:run(T, <<"^/me ">>), Nick} of
{_, <<"">>} ->
- io_lib:format("<font class=\"msm\">~s</font><br/>",
+ io_lib:format("<font class=\"msm\">~ts</font><br/>",
[htmlize(T, NoFollow, FileFormat)]);
{match, _} ->
- io_lib:format("<font class=\"mne\">~s ~s</font><br/>",
+ io_lib:format("<font class=\"mne\">~ts ~ts</font><br/>",
[Nick,
str:substr(htmlize(T, FileFormat), 5)]);
{nomatch, _} ->
- io_lib:format("<font class=\"mn\">~s</font> ~s<br/>",
+ io_lib:format("<font class=\"mn\">~ts</font> ~ts<br/>",
[Nick2, htmlize(T, NoFollow, FileFormat)])
end;
{room_existence, RoomNewExistence} ->
- io_lib:format("<font class=\"mrcm\">~s</font><br/>",
+ io_lib:format("<font class=\"mrcm\">~ts</font><br/>",
[get_room_existence_string(RoomNewExistence,
Lang)])
end,
@@ -473,12 +438,12 @@ add_message_to_log(Nick1, Message, RoomJID, Opts,
STime = io_lib:format("~2..0w:~2..0w:~2..0w",
[Hour, Minute, Second]),
{_, _, Microsecs} = Now,
- STimeUnique = io_lib:format("~s.~w",
+ STimeUnique = io_lib:format("~ts.~w",
[STime, Microsecs]),
catch fw(F,
list_to_binary(
- io_lib:format("<a id=\"~s\" name=\"~s\" href=\"#~s\" "
- "class=\"ts\">[~s]</a> ",
+ io_lib:format("<a id=\"~ts\" name=\"~ts\" href=\"#~ts\" "
+ "class=\"ts\">[~ts]</a> ",
[STimeUnique, STimeUnique, STimeUnique, STime])
++ Text),
FileFormat),
@@ -489,48 +454,48 @@ add_message_to_log(Nick1, Message, RoomJID, Opts,
%% Utilities
get_room_existence_string(created, Lang) ->
- ?T(<<"Chatroom is created">>);
+ tr(Lang, ?T("Chatroom is created"));
get_room_existence_string(destroyed, Lang) ->
- ?T(<<"Chatroom is destroyed">>);
+ tr(Lang, ?T("Chatroom is destroyed"));
get_room_existence_string(started, Lang) ->
- ?T(<<"Chatroom is started">>);
+ tr(Lang, ?T("Chatroom is started"));
get_room_existence_string(stopped, Lang) ->
- ?T(<<"Chatroom is stopped">>).
+ tr(Lang, ?T("Chatroom is stopped")).
get_dateweek(Date, Lang) ->
Weekday = case calendar:day_of_the_week(Date) of
- 1 -> ?T(<<"Monday">>);
- 2 -> ?T(<<"Tuesday">>);
- 3 -> ?T(<<"Wednesday">>);
- 4 -> ?T(<<"Thursday">>);
- 5 -> ?T(<<"Friday">>);
- 6 -> ?T(<<"Saturday">>);
- 7 -> ?T(<<"Sunday">>)
+ 1 -> tr(Lang, ?T("Monday"));
+ 2 -> tr(Lang, ?T("Tuesday"));
+ 3 -> tr(Lang, ?T("Wednesday"));
+ 4 -> tr(Lang, ?T("Thursday"));
+ 5 -> tr(Lang, ?T("Friday"));
+ 6 -> tr(Lang, ?T("Saturday"));
+ 7 -> tr(Lang, ?T("Sunday"))
end,
{Y, M, D} = Date,
Month = case M of
- 1 -> ?T(<<"January">>);
- 2 -> ?T(<<"February">>);
- 3 -> ?T(<<"March">>);
- 4 -> ?T(<<"April">>);
- 5 -> ?T(<<"May">>);
- 6 -> ?T(<<"June">>);
- 7 -> ?T(<<"July">>);
- 8 -> ?T(<<"August">>);
- 9 -> ?T(<<"September">>);
- 10 -> ?T(<<"October">>);
- 11 -> ?T(<<"November">>);
- 12 -> ?T(<<"December">>)
+ 1 -> tr(Lang, ?T("January"));
+ 2 -> tr(Lang, ?T("February"));
+ 3 -> tr(Lang, ?T("March"));
+ 4 -> tr(Lang, ?T("April"));
+ 5 -> tr(Lang, ?T("May"));
+ 6 -> tr(Lang, ?T("June"));
+ 7 -> tr(Lang, ?T("July"));
+ 8 -> tr(Lang, ?T("August"));
+ 9 -> tr(Lang, ?T("September"));
+ 10 -> tr(Lang, ?T("October"));
+ 11 -> tr(Lang, ?T("November"));
+ 12 -> tr(Lang, ?T("December"))
end,
list_to_binary(
case Lang of
<<"en">> ->
- io_lib:format("~s, ~s ~w, ~w", [Weekday, Month, D, Y]);
+ io_lib:format("~ts, ~ts ~w, ~w", [Weekday, Month, D, Y]);
<<"es">> ->
- io_lib:format("~s ~w de ~s de ~w",
+ io_lib:format("~ts ~w de ~ts de ~w",
[Weekday, D, Month, Y]);
_ ->
- io_lib:format("~s, ~w ~s ~w", [Weekday, D, Month, Y])
+ io_lib:format("~ts, ~w ~ts ~w", [Weekday, D, Month, Y])
end).
make_dir_rec(Dir) ->
@@ -539,188 +504,23 @@ make_dir_rec(Dir) ->
%% {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+xQNoA"
- "AADN0lEQVRo3u1aP0waURz+rjGRRQ+nUyRCYmJyDPTapD"
- "ARaSIbTUjt1gVSh8ZW69aBAR0cWLSxCXWp59LR1jbdqKn"
- "GxoQuRZZrSYyHEVM6iZMbHewROA7u3fHvkr5vOn737vcu"
- "33ffu9/vcQz+gef5Cij6CkmSGABgFEH29r5SVvqIsTEOH"
- "o8HkiQxDBXEOjg9PcHc3BxuUSqsI8jR0REAUFGsCCoKFY"
- "WCBAN6AxyO0Z7cyMXFb6oGqSgAsIrJut9hMQlvdNbUhKW"
- "shLd3HtTF4jihShgVpRaBxKKmIGX5HL920/hz/BM2+zAm"
- "pn2YioQaxnECj0BiEYcrG0Tzzc8/rfudSm02jaVSm9Vr1"
- "MdG8rSKKXlJ7lHrfjouCut2IrC82BDPbe/gc+xlXez7Kx"
- "Ez63H4lmIN473Rh8Si1BKhRY6aEJI8pLmbjSPN0xOnBBI"
- "Lmg5RC6Lg28preKOzsNmHG8R1Bf0o7GdMucUslDy1pJLG"
- "2sndVVG0lq3c9vum4zmBR1kuwiYMN5ybmCYXxQg57ThFO"
- "TYznzpPO+IQi+IK+jXjg/YhuIJ+cIIHg+wQJoJ+2N3jYN"
- "3Olvk4ge/IU98spne+FfGtlslm16nna8fduntfDscoVjG"
- "JqUgIjz686ViFUdjP4N39x9Xq638viZVtlq2tLXKncLf5"
- "ticuZSWU5XOUshJKxxKtfdtdvs4OyNb/68urKvlluYizg"
- "wwu5SLK8jllu1t9ihYOlzdwdpBBKSvh+vKKzHkCj1JW3y"
- "1m+hSj13WjqOiJKK0qpXKhSFxJAYBvKYaZ9TjWRu4SiWi"
- "2LyDtb6wghGmn5HfTml16ILGA/G5al2DW7URYTFYrOU7g"
- "icQ020sYqYDM9CbdgqFd4vzHL03JfvLjk6ZgADAVCSEsJ"
- "vHsdL+utNYrm2ufZDVZSkzPKaQkW8kthpyS297BvRdRzR"
- "6DdTurJbPy9Ov1K6xr3HBPQuIMowR3asegUyDuU9SuUG+"
- "dmIGyZ0b7FBN9St3WunyC5yMsrVv7uXzRP58s/qKn6C4q"
- "lQoVxVIvd4YBwzBUFKs6ZaD27U9hEdcAN98Sx2IxykafI"
- "YrizbfESoB+dd9/KF/d/wX3cJvREzl1vAAAAABJRU5Erk"
- "Jggg==">>;
-image_base64(<<"valid-xhtml10.png">>) ->
- <<"iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAAEjEcpEA"
- "AACiFBMVEUAAADe5+fOezmtra3ejEKlhELvvWO9WlrehE"
- "LOe3vepaWclHvetVLGc3PerVKcCAj3vVqUjHOUe1JjlL0"
- "xOUpjjL2UAAC91ueMrc7vrVKlvdbW3u+EpcbO3ufO1ucY"
- "WpSMKQi9SiF7e3taWkoQEAiMczkQSoxaUkpzc3O1lEoIC"
- "ACEazEhGAgIAACEYzFra2utjELWcznGnEr/7+9jY2POaz"
- "HOYzGta2NShLVrlL05OUqctdacCADGa2ucAADGpVqUtc6"
- "1ORg5OTmlUikYGAiUezl7YzEYEAiUczkxMTG9nEqtIRDe"
- "3t4AMXu9lEoQCACMazEAKXspKSmljFrW1ta1jELOzs7n7"
- "/fGxsa9pVqEOSkpY5xznL29tZxahLXOpVr/99ZrY1L/79"
- "ZjUiljSikAOYTvxmMAMYScezmchFqUczGtlFp7c2utjFq"
- "UlJStxt73///39/9Ce61CSkq9xsZznMbW5+9Cc62MjIxC"
- "Qkrv9/fv7/fOzsbnlErWjIz/3mtCORhza1IpIRBzWjH/1"
- "mtCMRhzY1L/zmvnvVpSQiHOpVJrUinntVr3zmOEc1L3xm"
- "NaWlq1nFo5QkrGWim1lFoISpRSUlK1zt4hWpwASoz////"
- "///8xa6WUaykAQoxKe61KSkp7nMbWtWPe5+9jWlL39/f3"
- "9/fWrWNCQkLera3nvWPv7+85MRjntWPetVp7c1IxKRCUl"
- "HtKORh7a1IxIRCUjHtaSiHWrVIpIQhzWinvvVpaQiH/1m"
- "PWpVKMe1L/zmP/xmNrUiGErc4YGBj/73PG1ucQWpT/53O"
- "9nFoQUpS1SiEQEBC9zt69vb05c6UISoxSUko5a6UICAhS"
- "SkohUpS1tbXetWMAQoSUgD+kAAAA2HRSTlP/////////i"
- "P9sSf//dP////////////////////////////////////"
- "////////////8M////////////ef/////////////////"
- "/////////////////////////////////////////////"
- "//////////////////////9d/////////////////////"
- "///////////////AP//////////////CP//RP////////"
- "/////////////////////////////////////////////"
- "///////9xPp1gAAAFvUlEQVR42pVWi18URRwfy7vsYUba"
- "iqBRBFmICUQGVKcZckQeaRJQUCLeycMSfKGH0uo5NELpI"
- "vGQGzokvTTA85VHKTpbRoeJnPno/p1+M7t3txj20e/Nzu"
- "7Ofve7v/k9Zg4Vc+wRQMW0eyLx1ZSANeBDxVmxZZSwEUY"
- "kGAewm1eIBOMRvhv1UA+q8KXIVuxGdCelFYwxAnxOrxgb"
- "Y8Ti1t4VA0QHYz4x3FnVC8OVLXv9fkKGSWDoW/4lG6Vbd"
- "tBblesOs+MjmEmzJKNIJWFEfEQTCWNPFKvcKEymjLO1b8"
- "bwYQd1hCiiDCl5KsrDCIlhj4fSuvcpfSpgJmyv6dzeZv+"
- "nMPx3dhbt94II07/JZliEtm1N2RIYPkTYshwYm245a/zk"
- "WjJwcyFh6ZIcYxxmqiaDSYxhOhFUsqngi3Fzcj3ljdYDN"
- "E9uzA1YD/5MhnzW1KRqF7mYG8jFYXLcfLpjOe2LA0fuGq"
- "QrQHl10sdK0sFcFSOSlzF0BgXQH9h3QZDBI0ccNEhftjX"
- "uippBDD2/eMRiETmwwNEYHyqhdDyo22w+3QHuNbdve5a7"
- "eOkHmDVJ0ixNmfbz1h0qo/Q6GuSB2wQJQbpOjOQAl7woW"
- "SRJ0m2ewhvAOUiYYtZtaZL0CZZmtmVOQttLfr/dbveLZo"
- "drfrL7W75wG/JjqkQxoNTtNsTKELQpQL6/D5loaSmyTT8"
- "TUhsmi8iFA0hZiyltf7OiNKdarRm5w2So2lTNdPLuIzR+"
- "AiLj8VTRJaj0LmX4VhJ27f/VJV/yycilWPOrk8NkXi7Qq"
- "mj5bHqVZlJKZIRk1wFzKrt0WUbnXMPJ1fk4TJ5oWBA61p"
- "1V76DeIs0MX+s3GxRlA1vtw83KhgNphc1nyErLO5zcvbO"
- "srq+scbZnpzc6QVFPenLwGxmC+BOfYI+DN55QYddh4Q/N"
- "E/yGYYj4TOGNngQavAZnzzTovEA+kcMJ+247uYexNA+4F"
- "svjmuv662jsWxPZx2xg890bYMYnTgya7bjmCiEY0qgJ0v"
- "MF3c+NoFdPyzxz6V3Uxs3AOWCDchRvOsQtBrbFsrT2fhH"
- "Ec7ByGzu/dA4IO0A3HdfeP9yMqAwP6NPEb6cbwn0PWVU1"
- "7/FDBQh/CPIrbfcg027IZrsAT/Bf3FNWyn9RSR4cvvwn3"
- "e4HFmYPDl/thYcRVi8qPEoXVUWBl6FTBFTtnqmKKg5wnl"
- "F4wZ1yeLv7TiwXKektE+iDBNicWEyLpnFhfDkpJc3q2kh"
- "SPyQBbE0dMJnOoDzTwGsI7cdyMkL5gWqUjCF6Txst/twx"
- "Cv1WzzHoy21ZDQ1xnuDzdPDWR4knr14v0tYn3IxaMFFdi"
- "MOlEOJHw1jOQ4sWt5rQopRkXZhMEi7pmeDCVWBlfUKwhM"
- "Z7rsF6elKsvbwiKxgxIdewa3ErsaYomCVZFYJb0GUu3Jq"
- "GUNoplBxYiYby8vLBFWef+Cri4/I1sbQ/1OtYTrNtdXS+"
- "rSe7kQ52eSObL99/iErCWUjCy5W4JLygmCouGfG9x9fmx"
- "17XhBuDCaOerbt538erta7TFktLvdHghZcCbcPQO33zIJ"
- "G9kxF5hoVXnzTzRz0r5js8oTj6uyPkGRf346HOLcasgFe"
- "xueNUWFPtuFKzjoSFYYedhwVlhsRVYWWJpltv1XPQT1Rl"
- "0bjZIBlb1XujVDzY/Kj4k6Ku3+Z0jo1owjVzDpFTXe1ju"
- "vBSWNFmNWGZy8LvzUl5PN4JCwyNDzbQ0aAj4Zrjz0FatG"
- "JJYhvq4j7mGSpvytGFlZtHf2C4o/28Zu8z7wo7eYPfXys"
- "nF0i9NnPh1t1zR7VBb9GqaOXhtTmHQdgMFXE+Z608cnpO"
- "DdZdjL+TuDY44Q38kJXHhccWLoOd9uv1AwwvO+48uu+fa"
- "CSJPJ1bmy6ThyvpivBmYWgjxPDPAp7JTemY/yGKFEiRt/"
- "jG/2P79s8KCwoLCgoLC/khUBA5F0SfQZ+RYfpNE/4Xosm"
- "q7jsZAJsAAAAASUVORK5CYII=">>;
-image_base64(<<"vcss.png">>) ->
- <<"iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAABUFvrSA"
- "AABKVBMVEUAAAAjIx8MR51ZVUqAdlmdnZ3ejEWLDAuNjY"
- "1kiMG0n2d9fX19Ghfrp1FtbW3y39+3Ph6lIRNdXV2qJBF"
- "cVUhcVUhPT0/dsmpUfLr57+/u7u4/PDWZAACZAADOp1Gd"
- "GxG+SyTgvnNdSySzk16+mkuxw+BOS0BOS0DOzs7MzMy4T"
- "09RRDwsJBG+vr73wV6fkG6eCQRFcLSurq6/X1+ht9nXfz"
- "5sepHuwV59ZTHetFjQ2+wMCQQ2ZK5tWCsmWajsz8+Sq9N"
- "MPh4hVaY8MRj///////////////////////9MTEyOp9Lu"
- "8vhXU1A8PDyjOSTBz+YLRJ2rLy8sLCwXTaKujEUcHByDn"
- "82dfz7/zGafDw+fDw+zRSlzlMcMDAyNcji1tbXf5vIcFg"
- "vATJOjAAAAY3RSTlP/8/////////////////8A//////P"
- "/////ov//8//////////////z///T//////////+i////"
- "//////////8w/////6IA/xAgMP//////////8////////"
- "/8w0/////////+zehebAAACkUlEQVR42u2VfVPTQBDG19"
- "VqC6LY+lKrRIxFQaFSBPuSvhBPF8SIUZK2J5Yav/+HcO8"
- "uZdLqTCsU/nKnyWwvk1/unnt2D9ZmH+8/cMAaTRFy+ng6"
- "9/yiwC/+gy8R3McGv5zHvGJEGAdR4eBgi1IbZwevIEZE2"
- "4pFtBtzG1Q4AoD5zvw5pEDcJvIQV/TE3/l+H9GnNJwcdA"
- "BS5wAbFQLMqI98/UReoAaOTlaJsp0zaHx7LwZvY0BUR2x"
- "pWTzqam0gzY8KGzG4MhBCNGucha4QbpETy+Yk/BP85nt7"
- "34AjpQLTsE4ZFpf/dnkUCglXVNYB+OfUZJHvAqAoa45Oe"
- "uPgm4+Xjtv7xm4N7PMV4C61+Mrz3H2WImm3ATiWrAiwZR"
- "WcUA5Ej4dgIEMxDv6yxHHcNuAutnjv2HZ1NeuycoVPh0m"
- "wC834zZC9Ao5dkZZKwLVGwT+WdLw0YOZ1saEkUDoT+QGW"
- "KZ0E2xpcrPakVW2KXwyUtYEtlEAj3GXD/fYwrryAdeiyG"
- "qidQSw1eqtJcA8cZq4zXqhPuCBYE1fKJjh/5X6MwRm9c2"
- "xf7WVdLf5oSdt64esVIwVAKC1HJ2oli8vj3L0YzC4zjkM"
- "agt+arDAs6bApbL1RVlWIqrJbreqKZmh4y6VR7rAJeUYD"
- "VRj9VqRXkErpJ9lbEwtE83KlIfeG4p52t7zWIMO1XcaGz"
- "54uUyet+hBM7BXXDS8Xc5+8Gmmbu1xwSoGIokA3oTptQe"
- "cQ4Iimm/Ew7jwbPfMi3TM91T9XVIGo+W9xC8oWpugVCXL"
- "uwXijjxJ3r/6PjX7nlFua8QmyM+TO/Gja2TTc2Z95C5ua"
- "ewGH6cJi6bJO6Z+TY276eH3tbgy+/3ly3Js+rj66osG/A"
- "V5htgaQ9SeRAAAAAElFTkSuQmCC">>;
-image_base64(<<"powered-by-ejabberd.png">>) ->
- <<"iVBORw0KGgoAAAANSUhEUgAAAGUAAAAfCAMAAADJG/NaA"
- "AAAw1BMVEUAAAAjBgYtBAM5AwFCAAAYGAJNAABcAABIDQ"
- "5qAAAoJRV7AACFAAAoKSdJHByLAAAwLwk1NQA1MzFJKyo"
- "4NxtDQQBEQT5KSCxSTgBSUBlgQ0JYSEpZWQJPUU5hYABb"
- "W0ZiYClcW1poaCVwbQRpaDhzYWNsakhuZ2VrbFZ8dwCEg"
- "AB3dnd4d2+OjACDhYKcmACJi4iQkpWspgCYmJm5swCmqa"
- "zEwACwsbS4ub3X0QLExsPLyszW1Nnc3ODm5ugMBwAWAwP"
- "Hm1IFAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJ"
- "cEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfVCRQOBA7VB"
- "kCMAAACcElEQVRIx72WjXKiMBSFQalIFbNiy1pdrJZaRV"
- "YR5deGwPs/VRNBSBB2OjvQO0oYjPfj5J6bCcdx8i2Uldx"
- "KcDhk1HbIPwFBF/kHKJfjPSVAyIRHF9rRZ4sUX3EDdWOv"
- "1+u2tESaavpnYTbv9zvd0WwDy3/QcGQXlH5uTxB1l07MJ"
- "lRpsUei0JF6Qi+OHyGK7ijXxPklHe/umIllim3iUBMJDI"
- "EULxxPP0TVWhhKJoN9fUpdmQLteV8aDgEAg9gIcTjL4F4"
- "L+r4WVKEF+rbJdwYYAoQHY+oQjnGootyKwxapoi73WkyF"
- "FySQBv988naEEp4+YMMec5VUCQDJTscEy7Kc0HsLmqNE7"
- "rovDjMpIHHGYeidXn4TQcaxMYqP3RV3C8oCl2WvrlSPaN"
- "pGZadRnmPGCk8ylM2okAJ4i9TEe1KersXxSl6jUt5uayi"
- "IodirtcKLOaWblj50wiyMv1F9lm9TUDArGAD0FmEpvCUs"
- "VoZy6dW81Fg0aDaHogQa36ekAPG5DDGsbdZrGsrzZUnzv"
- "Bo1I2tLmuL69kSitAweyHKN9b3leDfQMnu3nIIKWfmXnq"
- "GVKedJT6QpICbJvf2f8aOsvn68v+k7/cwUQdPoxaMoRTn"
- "KFHNlKsKQphCTOa84u64vpi8bH31CqsbF6lSONRTkTyQG"
- "Arq49/fEvjBwz4eDS2/JpaXRNOoXRD/VmOrDVTJJRIZCT"
- "Lav3VrqbPvP3vdduGEhQJzilncbpSA4F3vsihErO+dayv"
- "/sY5/yRE0GDEXCu2VoNiMlo5i+P2KlgMEvTNk2eYa5XEy"
- "h12Ex17Z8vzQUR3KEPbYd6XG87eC4Ly75RneS5ZYHAAAA"
- "AElFTkSuQmCC">>.
+%% base64:encode(F1b).
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 = fjoin([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.
+ lists:foreach(
+ fun(Filename) ->
+ Src = filename:join([misc:img_dir(), Filename]),
+ Dst = fjoin([Images_dir, Filename]),
+ case file:copy(Src, Dst) of
+ {ok, _} -> ok;
+ {error, Why} ->
+ ?ERROR_MSG("Failed to copy ~ts to ~ts: ~ts",
+ [Src, Dst, file:format_error(Why)])
+ end
+ end, Filenames).
fw(F, S) -> fw(F, S, [], html).
@@ -729,7 +529,7 @@ fw(F, S, FileFormat) when is_atom(FileFormat) ->
fw(F, S, [], FileFormat).
fw(F, S, O, FileFormat) ->
- S1 = list_to_binary(io_lib:format(binary_to_list(S) ++ "~n", O)),
+ S1 = (str:format(binary_to_list(S) ++ "~n", O)),
S2 = case FileFormat of
html ->
S1;
@@ -750,13 +550,13 @@ put_header(F, Room, Date, CSSFile, Lang, Hour_offset,
"org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">">>),
fw(F,
<<"<html xmlns=\"http://www.w3.org/1999/xhtml\" "
- "xml:lang=\"~s\" lang=\"~s\">">>,
+ "xml:lang=\"~ts\" lang=\"~ts\">">>,
[Lang, Lang]),
fw(F, <<"<head>">>),
fw(F,
<<"<meta http-equiv=\"Content-Type\" content=\"t"
"ext/html; charset=utf-8\" />">>),
- fw(F, <<"<title>~s - ~s</title>">>,
+ fw(F, <<"<title>~ts - ~ts</title>">>,
[htmlize(Room#room.title), Date]),
put_header_css(F, CSSFile),
put_header_script(F),
@@ -767,19 +567,19 @@ put_header(F, Room, Date, CSSFile, Lang, Hour_offset,
<<"<div style=\"text-align: right;\"><a "
"style=\"color: #AAAAAA; font-family: "
"monospace; text-decoration: none; font-weight"
- ": bold;\" href=\"~s\">~s</a></div>">>,
+ ": bold;\" href=\"~ts\">~ts</a></div>">>,
[Top_url, Top_text]),
- fw(F, <<"<div class=\"roomtitle\">~s</div>">>,
+ fw(F, <<"<div class=\"roomtitle\">~ts</div>">>,
[htmlize(Room#room.title)]),
fw(F,
- <<"<a class=\"roomjid\" href=\"xmpp:~s?join\">~s"
+ <<"<a class=\"roomjid\" href=\"xmpp:~ts?join\">~ts"
"</a>">>,
[Room#room.jid, Room#room.jid]),
fw(F,
- <<"<div class=\"logdate\">~s<span class=\"w3c\">"
- "<a class=\"nav\" href=\"~s\">&lt;</a> "
+ <<"<div class=\"logdate\">~ts<span class=\"w3c\">"
+ "<a class=\"nav\" href=\"~ts\">&lt;</a> "
"<a class=\"nav\" href=\"./\">^</a> <a "
- "class=\"nav\" href=\"~s\">&gt;</a></span></di"
+ "class=\"nav\" href=\"~ts\">&gt;</a></span></di"
"v>">>,
[Date, Date_prev, Date_next]),
case {htmlize(Room#room.subject_author),
@@ -787,8 +587,8 @@ put_header(F, Room, Date, CSSFile, Lang, Hour_offset,
of
{<<"">>, <<"">>} -> ok;
{SuA, Su} ->
- fw(F, <<"<div class=\"roomsubject\">~s~s~s</div>">>,
- [SuA, ?T(<<" has set the subject to: ">>), Su])
+ fw(F, <<"<div class=\"roomsubject\">~ts~ts~ts</div>">>,
+ [SuA, tr(Lang, ?T(" has set the subject to: ")), Su])
end,
RoomConfig = roomconfig_to_string(Room#room.config,
Lang, FileFormat),
@@ -801,117 +601,44 @@ put_header(F, Room, Date, CSSFile, Lang, Hour_offset,
true -> io_lib:format("~p", [Hour_offset]);
false -> io_lib:format("+~p", [Hour_offset])
end,
- fw(F, <<"<br/><a class=\"ts\">GMT~s</a><br/>">>,
+ fw(F, <<"<br/><a class=\"ts\">GMT~ts</a><br/>">>,
[Time_offset_str]).
-put_header_css(F, false) ->
+put_header_css(F, {file, Path}) ->
fw(F, <<"<style type=\"text/css\">">>),
fw(F, <<"<!--">>),
- fw(F,
- <<".ts {color: #AAAAAA; text-decoration: "
- "none;}">>),
- fw(F,
- <<".mrcm {color: #009900; font-style: italic; "
- "font-weight: bold;}">>),
- fw(F,
- <<".msc {color: #009900; font-style: italic; "
- "font-weight: bold;}">>),
- fw(F,
- <<".msm {color: #000099; font-style: italic; "
- "font-weight: bold;}">>),
- fw(F, <<".mj {color: #009900; font-style: italic;}">>),
- fw(F, <<".ml {color: #009900; font-style: italic;}">>),
- fw(F, <<".mk {color: #009900; font-style: italic;}">>),
- fw(F, <<".mb {color: #009900; font-style: italic;}">>),
- fw(F, <<".mnc {color: #009900; font-style: italic;}">>),
- fw(F, <<".mn {color: #0000AA;}">>),
- fw(F, <<".mne {color: #AA0099;}">>),
- fw(F,
- <<"a.nav {color: #AAAAAA; font-family: "
- "monospace; letter-spacing: 3px; text-decorati"
- "on: none;}">>),
- fw(F,
- <<"div.roomtitle {border-bottom: #224466 "
- "solid 3pt; margin-left: 20pt;}">>),
- fw(F,
- <<"div.roomtitle {color: #336699; font-size: "
- "24px; font-weight: bold; font-family: "
- "sans-serif; letter-spacing: 3px; text-decorat"
- "ion: none;}">>),
- fw(F,
- <<"a.roomjid {color: #336699; font-size: "
- "24px; font-weight: bold; font-family: "
- "sans-serif; letter-spacing: 3px; margin-left: "
- "20pt; text-decoration: none;}">>),
- fw(F,
- <<"div.logdate {color: #663399; font-size: "
- "20px; font-weight: bold; font-family: "
- "sans-serif; letter-spacing: 2px; border-botto"
- "m: #224466 solid 1pt; margin-left:80pt; "
- "margin-top:20px;}">>),
- fw(F,
- <<"div.roomsubject {color: #336699; font-size: "
- "18px; font-family: sans-serif; margin-left: "
- "80pt; margin-bottom: 10px;}">>),
- fw(F,
- <<"div.rc {color: #336699; font-size: 12px; "
- "font-family: sans-serif; margin-left: "
- "50%; text-align: right; background: "
- "#f3f6f9; border-bottom: 1px solid #336699; "
- "border-right: 4px solid #336699;}">>),
- fw(F,
- <<"div.rct {font-weight: bold; background: "
- "#e3e6e9; padding-right: 10px;}">>),
- fw(F, <<"div.rcos {padding-right: 10px;}">>),
- fw(F, <<"div.rcoe {color: green;}">>),
- fw(F, <<"div.rcod {color: red;}">>),
- fw(F, <<"div.rcoe:after {content: \": v\";}">>),
- fw(F, <<"div.rcod:after {content: \": x\";}">>),
- fw(F, <<"div.rcot:after {}">>),
- fw(F,
- <<".legend {width: 100%; margin-top: 30px; "
- "border-top: #224466 solid 1pt; padding: "
- "10px 0px 10px 0px; text-align: left; "
- "font-family: monospace; letter-spacing: "
- "2px;}">>),
- fw(F,
- <<".w3c {position: absolute; right: 10px; "
- "width: 60%; text-align: right; font-family: "
- "monospace; letter-spacing: 1px;}">>),
+ case file:read_file(Path) of
+ {ok, Data} -> fw(F, Data);
+ {error, _} -> ok
+ end,
fw(F, <<"//-->">>),
fw(F, <<"</style>">>);
-put_header_css(F, CSSFile) ->
+put_header_css(F, {url, URL}) ->
fw(F,
<<"<link rel=\"stylesheet\" type=\"text/css\" "
- "href=\"~s\" media=\"all\">">>,
- [CSSFile]).
+ "href=\"~ts\" media=\"all\">">>,
+ [URL]).
put_header_script(F) ->
fw(F, <<"<script type=\"text/javascript\">">>),
- fw(F, <<"function sh(e) // Show/Hide an element">>),
- fw(F,
- <<"{if(document.getElementById(e).style.display="
- "='none')">>),
- fw(F,
- <<"{document.getElementById(e).style.display='bl"
- "ock';}">>),
- fw(F,
- <<"else {document.getElementById(e).style.displa"
- "y='none';}}">>),
+ case misc:read_js("muc.js") of
+ {ok, Data} -> fw(F, Data);
+ {error, _} -> ok
+ end,
fw(F, <<"</script>">>).
put_room_config(_F, _RoomConfig, _Lang, plaintext) ->
ok;
put_room_config(F, RoomConfig, Lang, _FileFormat) ->
- {_, Now2, _} = p1_time_compat:timestamp(),
+ {_, Now2, _} = erlang:timestamp(),
fw(F, <<"<div class=\"rc\">">>),
fw(F,
<<"<div class=\"rct\" onclick=\"sh('a~p');return "
- "false;\">~s</div>">>,
- [Now2, ?T(<<"Room Configuration">>)]),
+ "false;\">~ts</div>">>,
+ [Now2, tr(Lang, ?T("Room Configuration"))]),
fw(F,
<<"<div class=\"rcos\" id=\"a~p\" style=\"displa"
- "y: none;\" ><br/>~s</div>">>,
+ "y: none;\" ><br/>~ts</div>">>,
[Now2, RoomConfig]),
fw(F, <<"</div>">>).
@@ -920,18 +647,18 @@ put_room_occupants(_F, _RoomOccupants, _Lang,
ok;
put_room_occupants(F, RoomOccupants, Lang,
_FileFormat) ->
- {_, Now2, _} = p1_time_compat:timestamp(),
+ {_, Now2, _} = erlang:timestamp(),
%% htmlize
%% The default behaviour is to ignore the nofollow spam prevention on links
%% (NoFollow=false)
fw(F, <<"<div class=\"rc\">">>),
fw(F,
<<"<div class=\"rct\" onclick=\"sh('o~p');return "
- "false;\">~s</div>">>,
- [Now2, ?T(<<"Room Occupants">>)]),
+ "false;\">~ts</div>">>,
+ [Now2, tr(Lang, ?T("Room Occupants"))]),
fw(F,
<<"<div class=\"rcos\" id=\"o~p\" style=\"displa"
- "y: none;\" ><br/>~s</div>">>,
+ "y: none;\" ><br/>~ts</div>">>,
[Now2, RoomOccupants]),
fw(F, <<"</div>">>).
@@ -991,7 +718,7 @@ get_room_info(RoomJID, Opts) ->
false -> <<"">>
end,
Subject = case lists:keysearch(subject, 1, Opts) of
- {value, {_, S}} -> S;
+ {value, {_, S}} -> xmpp:get_text(S);
false -> <<"">>
end,
SubjectAuthor = case lists:keysearch(subject_author, 1,
@@ -1000,7 +727,7 @@ get_room_info(RoomJID, Opts) ->
{value, {_, SA}} -> SA;
false -> <<"">>
end,
- #room{jid = jid:to_string(RoomJID), title = Title,
+ #room{jid = jid:encode(RoomJID), title = Title,
subject = Subject, subject_author = SubjectAuthor,
config = Opts}.
@@ -1013,10 +740,9 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
Os2 = lists:sort(Os1),
Options2 = Title ++ Os2,
lists:foldl(fun ({Opt, Val}, R) ->
- case get_roomconfig_text(Opt) of
+ case get_roomconfig_text(Opt, Lang) of
undefined -> R;
- OptT ->
- OptText = (?T(OptT)),
+ OptText ->
R2 = case Val of
false ->
<<"<div class=\"rcod\">",
@@ -1035,7 +761,7 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
max_users ->
<<"<div class=\"rcot\">",
OptText/binary, ": \"",
- (htmlize(jlib:integer_to_binary(T),
+ (htmlize(integer_to_binary(T),
FileFormat))/binary,
"\"</div>">>;
title ->
@@ -1053,7 +779,7 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
allow_private_messages_from_visitors ->
<<"<div class=\"rcot\">",
OptText/binary, ": \"",
- (htmlize(?T((jlib:atom_to_binary(T))),
+ (htmlize(tr(Lang, misc:atom_to_binary(T)),
FileFormat))/binary,
"\"</div>">>;
_ -> <<"\"", T/binary, "\"">>
@@ -1064,49 +790,48 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
end,
<<"">>, Options2).
-get_roomconfig_text(title) -> <<"Room title">>;
-get_roomconfig_text(persistent) ->
- <<"Make room persistent">>;
-get_roomconfig_text(public) ->
- <<"Make room public searchable">>;
-get_roomconfig_text(public_list) ->
- <<"Make participants list public">>;
-get_roomconfig_text(password_protected) ->
- <<"Make room password protected">>;
-get_roomconfig_text(password) -> <<"Password">>;
-get_roomconfig_text(anonymous) ->
- <<"This room is not anonymous">>;
-get_roomconfig_text(members_only) ->
- <<"Make room members-only">>;
-get_roomconfig_text(moderated) ->
- <<"Make room moderated">>;
-get_roomconfig_text(members_by_default) ->
- <<"Default users as participants">>;
-get_roomconfig_text(allow_change_subj) ->
- <<"Allow users to change the subject">>;
-get_roomconfig_text(allow_private_messages) ->
- <<"Allow users to send private messages">>;
-get_roomconfig_text(allow_private_messages_from_visitors) ->
- <<"Allow visitors to send private messages to">>;
-get_roomconfig_text(allow_query_users) ->
- <<"Allow users to query other users">>;
-get_roomconfig_text(allow_user_invites) ->
- <<"Allow users to send invites">>;
-get_roomconfig_text(logging) -> <<"Enable logging">>;
-get_roomconfig_text(allow_visitor_nickchange) ->
- <<"Allow visitors to change nickname">>;
-get_roomconfig_text(allow_visitor_status) ->
- <<"Allow visitors to send status text in "
- "presence updates">>;
-get_roomconfig_text(captcha_protected) ->
- <<"Make room captcha protected">>;
-get_roomconfig_text(description) ->
- <<"Room description">>;
-%% get_roomconfig_text(subject) -> "Subject";
-%% get_roomconfig_text(subject_author) -> "Subject author";
-get_roomconfig_text(max_users) ->
- <<"Maximum Number of Occupants">>;
-get_roomconfig_text(_) -> undefined.
+get_roomconfig_text(title, Lang) -> tr(Lang, ?T("Room title"));
+get_roomconfig_text(persistent, Lang) ->
+ tr(Lang, ?T("Make room persistent"));
+get_roomconfig_text(public, Lang) ->
+ tr(Lang, ?T("Make room public searchable"));
+get_roomconfig_text(public_list, Lang) ->
+ tr(Lang, ?T("Make participants list public"));
+get_roomconfig_text(password_protected, Lang) ->
+ tr(Lang, ?T("Make room password protected"));
+get_roomconfig_text(password, Lang) -> tr(Lang, ?T("Password"));
+get_roomconfig_text(anonymous, Lang) ->
+ tr(Lang, ?T("This room is not anonymous"));
+get_roomconfig_text(members_only, Lang) ->
+ tr(Lang, ?T("Make room members-only"));
+get_roomconfig_text(moderated, Lang) ->
+ tr(Lang, ?T("Make room moderated"));
+get_roomconfig_text(members_by_default, Lang) ->
+ tr(Lang, ?T("Default users as participants"));
+get_roomconfig_text(allow_change_subj, Lang) ->
+ tr(Lang, ?T("Allow users to change the subject"));
+get_roomconfig_text(allow_private_messages, Lang) ->
+ tr(Lang, ?T("Allow users to send private messages"));
+get_roomconfig_text(allow_private_messages_from_visitors, Lang) ->
+ tr(Lang, ?T("Allow visitors to send private messages to"));
+get_roomconfig_text(allow_query_users, Lang) ->
+ tr(Lang, ?T("Allow users to query other users"));
+get_roomconfig_text(allow_user_invites, Lang) ->
+ tr(Lang, ?T("Allow users to send invites"));
+get_roomconfig_text(logging, Lang) -> tr(Lang, ?T("Enable logging"));
+get_roomconfig_text(allow_visitor_nickchange, Lang) ->
+ tr(Lang, ?T("Allow visitors to change nickname"));
+get_roomconfig_text(allow_visitor_status, Lang) ->
+ tr(Lang, ?T("Allow visitors to send status text in presence updates"));
+get_roomconfig_text(captcha_protected, Lang) ->
+ tr(Lang, ?T("Make room CAPTCHA protected"));
+get_roomconfig_text(description, Lang) ->
+ tr(Lang, ?T("Room description"));
+%% get_roomconfig_text(subject, Lang) -> "Subject";
+%% get_roomconfig_text(subject_author, Lang) -> "Subject author";
+get_roomconfig_text(max_users, Lang) ->
+ tr(Lang, ?T("Maximum Number of Occupants"));
+get_roomconfig_text(_, _) -> undefined.
%% Users = [{JID, Nick, Role}]
roomoccupants_to_string(Users, _FileFormat) ->
@@ -1161,34 +886,37 @@ role_users_to_string(RoleS, Users) ->
<<RoleS/binary, ": ", UsersString/binary>>.
get_room_occupants(RoomJIDString) ->
- RoomJID = jid:from_string(RoomJIDString),
+ RoomJID = jid:decode(RoomJIDString),
RoomName = RoomJID#jid.luser,
MucService = RoomJID#jid.lserver,
- StateData = get_room_state(RoomName, MucService),
- [{U#user.jid, U#user.nick, U#user.role}
- || {_, U} <- (?DICT):to_list(StateData#state.users)].
+ case get_room_state(RoomName, MucService) of
+ {ok, StateData} ->
+ [{U#user.jid, U#user.nick, U#user.role}
+ || U <- maps:values(StateData#state.users)];
+ error ->
+ []
+ end.
--spec get_room_state(binary(), binary()) -> muc_room_state().
+-spec get_room_state(binary(), binary()) -> {ok, mod_muc_room:state()} | error.
get_room_state(RoomName, MucService) ->
- case mnesia:dirty_read(muc_online_room,
- {RoomName, MucService})
- of
- [R] ->
- RoomPid = R#muc_online_room.pid,
- get_room_state(RoomPid);
- [] -> #state{}
+ case mod_muc:find_online_room(RoomName, MucService) of
+ {ok, RoomPid} ->
+ get_room_state(RoomPid);
+ error ->
+ error
end.
--spec get_room_state(pid()) -> muc_room_state().
+-spec get_room_state(pid()) -> {ok, mod_muc_room:state()} | error.
get_room_state(RoomPid) ->
- {ok, R} = gen_fsm:sync_send_all_state_event(RoomPid,
- get_state),
- R.
+ case mod_muc_room:get_state(RoomPid) of
+ {ok, State} -> {ok, State};
+ {error, _} -> error
+ end.
get_proc_name(Host) ->
- gen_mod:get_module_proc(Host, ?PROCNAME).
+ gen_mod:get_module_proc(Host, ?MODULE).
calc_hour_offset(TimeHere) ->
TimeZero = calendar:universal_time(),
@@ -1203,53 +931,62 @@ calc_hour_offset(TimeHere) ->
fjoin(FileList) ->
list_to_binary(filename:join([binary_to_list(File) || File <- FileList])).
+-spec tr(binary(), binary()) -> binary().
+tr(Lang, Text) ->
+ translate:translate(Lang, Text).
+
has_no_permanent_store_hint(Packet) ->
- fxml:get_subtag_with_xmlns(Packet, <<"no-store">>, ?NS_HINTS)
- =/= false orelse
- fxml:get_subtag_with_xmlns(Packet, <<"no-storage">>, ?NS_HINTS)
- =/= false orelse
- fxml:get_subtag_with_xmlns(Packet, <<"no-permanent-store">>, ?NS_HINTS)
- =/= false orelse
- fxml:get_subtag_with_xmlns(Packet, <<"no-permanent-storage">>, ?NS_HINTS)
- =/= false.
+ xmpp:has_subtag(Packet, #hint{type = 'no-store'}) orelse
+ xmpp:has_subtag(Packet, #hint{type = 'no-storage'}) orelse
+ xmpp:has_subtag(Packet, #hint{type = 'no-permanent-store'}) orelse
+ xmpp:has_subtag(Packet, #hint{type = 'no-permanent-storage'}).
mod_opt_type(access_log) ->
- fun (A) when is_atom(A) -> A end;
-mod_opt_type(cssfile) -> fun iolist_to_binary/1;
+ econf:acl();
+mod_opt_type(cssfile) ->
+ econf:url_or_file();
mod_opt_type(dirname) ->
- fun (room_jid) -> room_jid;
- (room_name) -> room_name
- end;
+ econf:enum([room_jid, room_name]);
mod_opt_type(dirtype) ->
- fun (subdirs) -> subdirs;
- (plain) -> plain
- end;
+ econf:enum([subdirs, plain]);
mod_opt_type(file_format) ->
- fun (html) -> html;
- (plaintext) -> plaintext
- end;
+ econf:enum([html, plaintext]);
mod_opt_type(file_permissions) ->
- fun (SubOpts) ->
- F = fun ({mode, Mode}, {_M, G}) -> {Mode, G};
- ({group, Group}, {M, _G}) -> {M, Group}
- end,
- lists:foldl(F, {644, 33}, SubOpts)
- end;
-mod_opt_type(outdir) -> fun iolist_to_binary/1;
+ econf:and_then(
+ econf:options(
+ #{mode => econf:non_neg_int(),
+ group => econf:non_neg_int()}),
+ fun(Opts) ->
+ {proplists:get_value(mode, Opts, 644),
+ proplists:get_value(group, Opts, 33)}
+ end);
+mod_opt_type(outdir) ->
+ econf:directory(write);
mod_opt_type(spam_prevention) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(timezone) ->
- fun (local) -> local;
- (universal) -> universal
- end;
+ econf:enum([local, universal]);
+mod_opt_type(url) ->
+ econf:url();
mod_opt_type(top_link) ->
- fun ([{S1, S2}]) ->
- {iolist_to_binary(S1), iolist_to_binary(S2)}
- end;
-mod_opt_type(_) ->
- [access_log, cssfile, dirname, dirtype, file_format,
- file_permissions, outdir, spam_prevention, timezone,
- top_link].
-
-opt_type(language) -> fun iolist_to_binary/1;
-opt_type(_) -> [language].
+ econf:and_then(
+ econf:non_empty(
+ econf:map(econf:binary(), econf:binary())),
+ fun hd/1).
+
+-spec mod_options(binary()) -> [{top_link, {binary(), binary()}} |
+ {file_permissions,
+ {non_neg_integer(), non_neg_integer()}} |
+ {atom(), any()}].
+mod_options(_) ->
+ [{access_log, muc_admin},
+ {cssfile, {file, filename:join(misc:css_dir(), <<"muc.css">>)}},
+ {dirname, room_jid},
+ {dirtype, subdirs},
+ {file_format, html},
+ {file_permissions, {644, 33}},
+ {outdir, <<"www/muc">>},
+ {spam_prevention, true},
+ {timezone, local},
+ {url, undefined},
+ {top_link, {<<"/">>, <<"Home">>}}].