diff options
Diffstat (limited to 'src/mod_muc_log.erl')
-rw-r--r-- | src/mod_muc_log.erl | 861 |
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\"><</a> " + <<"<div class=\"logdate\">~ts<span class=\"w3c\">" + "<a class=\"nav\" href=\"~ts\"><</a> " "<a class=\"nav\" href=\"./\">^</a> <a " - "class=\"nav\" href=\"~s\">></a></span></di" + "class=\"nav\" href=\"~ts\">></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">>}}]. |