From e37723f4a2cc749424d41573a954af84a74a8ac4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 18 Aug 2008 19:08:30 +0000 Subject: [PATCH] * src/mod_muc/mod_muc_log.erl: MUC log files options: plaintext format; filename with only room name (EJAB-596) * doc/guide.tex: Document both options * doc/guide.html: Likewise SVN Revision: 1531 --- ChangeLog | 5 + doc/guide.html | 23 +++- doc/guide.tex | 23 +++- src/mod_muc/mod_muc_log.erl | 231 ++++++++++++++++++++++-------------- 4 files changed, 182 insertions(+), 100 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5132512e8..a666b3cc0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2008-08-18 Badlop + * src/mod_muc/mod_muc_log.erl: MUC log files options: plaintext + format; filename with only room name (EJAB-596) + * doc/guide.tex: Document both options + * doc/guide.html: Likewise + * src/mod_register.erl: Change password using mod_register always returns success regardless of real result (EJAB-723) * src/ejabberd_auth.erl: Likewise diff --git a/doc/guide.html b/doc/guide.html index b944bdff0..f916476a0 100644 --- a/doc/guide.html +++ b/doc/guide.html @@ -2184,26 +2184,38 @@ file or if they need to use the embedded CSS file. Allowed values are include the embedded CSS code. With the latter, you can specify the URL of the custom CSS file (for example: ‘http://example.com/my.css’). The default value is false. +
dirname
+Allows to configure the name of the room directory. +Allowed values are room_jid and room_name. +With the first value, the room directory name will be the full room JID. +With the latter, the room directory name will be only the room name, +not including the MUC service name. +The default value is room_jid.
dirtype
The type of the created directories can be specified with this option. Allowed values are subdirs and plain. With the first value, subdirectories are created for each year and month. With the latter, the names of the log files contain the full date, and there are no subdirectories. The default value is subdirs. +
file_format
+Define the format of the log files: +html stores in HTML format, +plaintext stores in plain text. +The default value is html.
outdir
This option sets the full path to the directory in which the HTML files should be stored. Make sure the ejabberd daemon user has write access on that directory. The default value is "www/muc". -
timezone
-The time zone for the logs is configurable with this option. Allowed values -are local and universal. With the first value, the local time, -as reported to Erlang by the operating system, will be used. With the latter, -GMT/UTC time will be used. The default value is local.
spam_prevention
To prevent spam, the spam_prevention option adds a special attribute to links that prevent their indexation by search engines. The default value is true, which mean that nofollow attributes will be added to user submitted links. +
timezone
+The time zone for the logs is configurable with this option. Allowed values +are local and universal. With the first value, the local time, +as reported to Erlang by the operating system, will be used. With the latter, +GMT/UTC time will be used. The default value is local.
top_link
With this option you can customize the link on the top right corner of each log file. The syntax of this option is {"URL", "Text"}. The default @@ -2225,6 +2237,7 @@ time zone will be GMT/UTC. Finally, the top link will be {access_log, muc}, {cssfile, "http://example.com/my.css"}, {dirtype, plain}, + {dirname, room_jid}, {outdir, "/var/www/muclogs"}, {timezone, universal}, {spam_prevention, true}, diff --git a/doc/guide.tex b/doc/guide.tex index d2bb0603a..28488c403 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -2828,26 +2828,38 @@ Options: include the embedded CSS code. With the latter, you can specify the URL of the custom CSS file (for example: `http://example.com/my.css'). The default value is \term{false}. +\titem{dirname}\ind{options!dirname} + Allows to configure the name of the room directory. + Allowed values are \term{room\_jid} and \term{room\_name}. + With the first value, the room directory name will be the full room JID. + With the latter, the room directory name will be only the room name, + not including the MUC service name. + The default value is \term{room\_jid}. \titem{dirtype}\ind{options!dirtype} The type of the created directories can be specified with this option. Allowed values are \term{subdirs} and \term{plain}. With the first value, subdirectories are created for each year and month. With the latter, the names of the log files contain the full date, and there are no subdirectories. The default value is \term{subdirs}. +\titem{file\_format}\ind{options!file\_format} + Define the format of the log files: + \term{html} stores in HTML format, + \term{plaintext} stores in plain text. + The default value is \term{html}. \titem{outdir}\ind{options!outdir} This option sets the full path to the directory in which the HTML files should be stored. Make sure the \ejabberd{} daemon user has write access on that directory. The default value is \term{"www/muc"}. -\titem{timezone}\ind{options!timezone} - The time zone for the logs is configurable with this option. Allowed values - are \term{local} and \term{universal}. With the first value, the local time, - as reported to Erlang by the operating system, will be used. With the latter, - GMT/UTC time will be used. The default value is \term{local}. \titem{spam\_prevention}\ind{options!spam\_prevention} To prevent spam, the \term{spam\_prevention} option adds a special attribute to links that prevent their indexation by search engines. The default value is \term{true}, which mean that nofollow attributes will be added to user submitted links. +\titem{timezone}\ind{options!timezone} + The time zone for the logs is configurable with this option. Allowed values + are \term{local} and \term{universal}. With the first value, the local time, + as reported to Erlang by the operating system, will be used. With the latter, + GMT/UTC time will be used. The default value is \term{local}. \titem{top\_link}\ind{options!top\_link} With this option you can customize the link on the top right corner of each log file. The syntax of this option is \term{\{"URL", "Text"\}}. The default @@ -2872,6 +2884,7 @@ Examples: {access_log, muc}, {cssfile, "http://example.com/my.css"}, {dirtype, plain}, + {dirname, room_jid}, {outdir, "/var/www/muclogs"}, {timezone, universal}, {spam_prevention, true}, diff --git a/src/mod_muc/mod_muc_log.erl b/src/mod_muc/mod_muc_log.erl index 767fc7f6c..e76904193 100644 --- a/src/mod_muc/mod_muc_log.erl +++ b/src/mod_muc/mod_muc_log.erl @@ -52,6 +52,8 @@ -record(state, {host, out_dir, dir_type, + dir_name, + file_format, css_file, access, lang, @@ -113,6 +115,8 @@ check_access_log(Host, From) -> 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), @@ -129,6 +133,8 @@ init([Host, Opts]) -> {ok, #state{host = Host, out_dir = OutDir, dir_type = DirType, + dir_name = DirName, + file_format = FileFormat, css_file = CSSFile, access = AccessLog, lang = Lang, @@ -231,10 +237,10 @@ add_to_log2(kickban, {Nick, Reason, Code}, Room, Opts, State) -> %%---------------------------------------------------------------------- %% 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 -> @@ -248,55 +254,75 @@ build_filename_string(TimeStamp, OutDir, RoomJID, DirType) -> [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, "
ejabberd/mod_muc log"), - fw(F, "
"), - fw(F, " \"Powered", [Images_dir]), - fw(F, " \"Powered", [Images_dir]), - fw(F, ""), - fw(F, " \"Valid", [Images_dir]), - fw(F, " \"Valid", [Images_dir]), - fw(F, "
"), + 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, "
ejabberd/mod_muc log"), + fw(F, "
"), + fw(F, " \"Powered", [Images_dir]), + fw(F, " \"Powered", [Images_dir]), + fw(F, ""), + fw(F, " \"Valid", [Images_dir]), + fw(F, " \"Valid", [Images_dir]), + fw(F, "
"). + 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]); @@ -308,32 +334,32 @@ add_message_to_log(Nick1, Message, RoomJID, Opts, State) -> 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("~s
", [?T("Chatroom configuration modified")]); join -> @@ -344,19 +370,19 @@ add_message_to_log(Nick1, Message, RoomJID, Opts, State) -> [Nick, ?T("leaves the room")]); {leave, Reason} -> io_lib:format("~s ~s: ~s
", - [Nick, ?T("leaves the room"), htmlize(Reason,NoFollow)]); + [Nick, ?T("leaves the room"), htmlize(Reason,NoFollow,FileFormat)]); {kickban, "301", ""} -> io_lib:format("~s ~s
", [Nick, ?T("has been banned")]); {kickban, "301", Reason} -> io_lib:format("~s ~s: ~s
", - [Nick, ?T("has been banned"), htmlize(Reason)]); + [Nick, ?T("has been banned"), htmlize(Reason,FileFormat)]); {kickban, "307", ""} -> io_lib:format("~s ~s
", [Nick, ?T("has been kicked")]); {kickban, "307", Reason} -> io_lib:format("~s ~s: ~s
", - [Nick, ?T("has been kicked"), htmlize(Reason)]); + [Nick, ?T("has been kicked"), htmlize(Reason,FileFormat)]); {kickban, "321", ""} -> io_lib:format("~s ~s
", [Nick, ?T("has been kicked because of an affiliation change")]); @@ -368,18 +394,18 @@ add_message_to_log(Nick1, Message, RoomJID, Opts, State) -> [Nick, ?T("has been kicked because of a system shutdown")]); {nickchange, OldNick} -> io_lib:format("~s ~s ~s
", - [htmlize(OldNick), ?T("is now known as"), Nick]); + [htmlize(OldNick,FileFormat), ?T("is now known as"), Nick]); {subject, T} -> io_lib:format("~s~s~s
", - [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("~s ~s
", - [Nick, string:substr(htmlize(T), 5)]); + [Nick, string:substr(htmlize(T,FileFormat), 5)]); nomatch -> - io_lib:format("<~s> ~s
", - [Nick, htmlize(T,NoFollow)]) + io_lib:format("~s ~s
", + [Nick2, htmlize(T,NoFollow,FileFormat)]) end end, {Hour, Minute, Second} = Time, @@ -388,11 +414,11 @@ add_message_to_log(Nick1, Message, RoomJID, Opts, State) -> {_, _, Microsecs} = Now, STimeUnique = io_lib:format("~s.~w", [STime, Microsecs]), - % Write message - file:write(F, io_lib:format("[~s] ~s~n", - [STimeUnique, STimeUnique, STimeUnique, STime, Text])), + %% Write message + fw(F, io_lib:format("[~s] ", + [STimeUnique, STimeUnique, STimeUnique, STime]) ++ Text, FileFormat), - % Close file + %% Close file file:close(F), ok. @@ -443,13 +469,13 @@ make_dir_rec(Dir) -> 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" @@ -471,7 +497,7 @@ image_base64("powered-by-erlang.png") -> "KF/d/wX3cJvREzl1vAAAAABJRU5ErkJggg=="; image_base64("valid-xhtml10.png") -> - "iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAAEjEcpEAAACiFBMVEUAAADe" + "iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAAEjEcpEAAACiFBMVEUAAADe" "5+fOezmtra3ejEKlhELvvWO9WlrehELOe3vepaWclHvetVLGc3PerVKcCAj3" "vVqUjHOUe1JjlL0xOUpjjL2UAAC91ueMrc7vrVKlvdbW3u+EpcbO3ufO1ucY" "WpSMKQi9SiF7e3taWkoQEAiMczkQSoxaUkpzc3O1lEoICACEazEhGAgIAACE" @@ -527,7 +553,7 @@ image_base64("valid-xhtml10.png") -> "QZ+RYfpNE/4Xosmq7jsZAJsAAAAASUVORK5CYII="; image_base64("vcss.png") -> - "iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAABUFvrSAAABKVBMVEUAAAAj" + "iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAABUFvrSAAABKVBMVEUAAAAj" "Ix8MR51ZVUqAdlmdnZ3ejEWLDAuNjY1kiMG0n2d9fX19Ghfrp1FtbW3y39+3" "Ph6lIRNdXV2qJBFcVUhcVUhPT0/dsmpUfLr57+/u7u4/PDWZAACZAADOp1Gd" "GxG+SyTgvnNdSySzk16+mkuxw+BOS0BOS0DOzs7MzMy4T09RRDwsJBG+vr73" @@ -555,7 +581,7 @@ image_base64("vcss.png") -> "AElFTkSuQmCC"; image_base64("powered-by-ejabberd.png") -> - "iVBORw0KGgoAAAANSUhEUgAAAGUAAAAfCAMAAADJG/NaAAAAw1BMVEUAAAAj" + "iVBORw0KGgoAAAANSUhEUgAAAGUAAAAfCAMAAADJG/NaAAAAw1BMVEUAAAAj" "BgYtBAM5AwFCAAAYGAJNAABcAABIDQ5qAAAoJRV7AACFAAAoKSdJHByLAAAw" "Lwk1NQA1MzFJKyo4NxtDQQBEQT5KSCxSTgBSUBlgQ0JYSEpZWQJPUU5hYABb" "W0ZiYClcW1poaCVwbQRpaDhzYWNsakhuZ2VrbFZ8dwCEgAB3dnd4d2+OjACD" @@ -579,27 +605,43 @@ image_base64("powered-by-ejabberd.png") -> "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. + 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, []). +fw(F, S) -> fw(F, S, [], html). -put_header(F, Room, Date, CSSFile, Lang, Hour_offset, Date_prev, Date_next, Top_link) -> +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, ""), fw(F, "", [Lang, Lang]), fw(F, ""), @@ -618,8 +660,8 @@ put_header(F, Room, Date, CSSFile, Lang, Hour_offset, Date_prev, Date_next, Top_ {"", ""} -> ok; {SuA, Su} -> fw(F, "
~s~s~s
", [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]) @@ -669,7 +711,9 @@ put_header_script(F) -> fw(F, "else {document.getElementById(e).style.display='none';}}"), fw(F, ""). -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, "
"), fw(F, "
~s
", [Now2, ?T("Room Configuration")]), @@ -680,11 +724,18 @@ put_room_config(F, RoomConfig, Lang) -> %% 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) -> @@ -735,20 +786,20 @@ get_room_info(RoomJID, Opts) -> 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( @@ -765,7 +816,7 @@ roomconfig_to_string(Options, Lang) -> T -> case Opt of password -> "
" ++ OptText ++ "
"; - title -> "
" ++ ?T("Room title") ++ ": \"" ++ htmlize(T) ++ "\"
"; + title -> "
" ++ ?T("Room title") ++ ": \"" ++ htmlize(T, FileFormat) ++ "\"
"; _ -> "\"" ++ T ++ "\"" end end, @@ -795,7 +846,7 @@ get_roomconfig_text(_) -> undefined. 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.