ejabberd-contrib/mod_archive/src/mod_archive_webview.erl

554 lines
27 KiB
Erlang
Raw Normal View History

%%%----------------------------------------------------------------------
%%% File : mod_archive_webview.erl
%%% Author : Olivier Goffart <ogoffart at kde dot org>
%%% Purpose : Online viewer of message archive. (to be used with mod_archive_odbc)
%%% Created :
%%% Id :
%%%----------------------------------------------------------------------
-module(mod_archive_webview).
-author('ogoffart@kde.org').
-export([
process/2
]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("web/ejabberd_http.hrl").
-include("web/ejabberd_web_admin.hrl"). %for all the defines
-define(LINK(L) , "/archive/" ++ L).
%-define(P(Els), ?XE("p", Els)).
-define(PC(Text), ?XE("p", [?C(Text)])).
-define(PCT(Text), ?PC(?T(Text))).
-define(MYDEBUG(Format, Args),
io:format("D(~p:~p:~p) : " ++ Format ++ "~n",
[calendar:local_time(), ?MODULE, ?LINE] ++ Args)).
%%%----------------------------------------------------------------------
%%% REQUEST HANDLERS
%%%----------------------------------------------------------------------
%process([], Request) -> process2([], Request, {}).
process(["style.css"], _) ->
{200,[{"Content-Type", "text/css"}], "
#navigation li { list-style:none; display:inline; }
.message_from, .message_to { margin:0; padding:0; }
.message_from .time { color:#BD2134; font-weight:bold; }
.message_from .jid { color:#BD2134; font-weight:bold; }
.message_to .time { color:#1E6CC6; font-weight:bold; }
.message_to .jid { color:#1E6CC6; font-weight:bold; }
.search_result a { display:block; }
.search_result em { display:block; color:green; }
/*a.link_prev { float:left } */
a.link_next { float:right }
.message_body { white-space: pre-wrap; }
"};
process(Path, #request{auth = Auth} = Request) ->
?MYDEBUG("Requested ~p ~p", [Path, Request]),
case get_auth(Auth) of
{User, Server} ->
process2(Path, Request, {User, Server});
unauthorized ->
{401, [{"WWW-Authenticate", "basic realm=\"ejabberd-archives\""}],
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
[{xmlcdata, "401 Unauthorized"}]}])}
end.
%process2(["config" | tail], #request{lang = Lang } = Request , {User, Server}) ->
process2(["config" ], #request{lang = Lang } = _Request , {User, Server}) ->
make_xhtml(?T("Config"),
[?XE("h3", [?CT("Global Settings")]) ] ++ global_config_form({User, Server}, Lang) ++
[?XE("h3", [?CT("Specific Contact Settings")]) ] ++ contact_config_form({User, Server}, Lang) ++
[?X("hr"), ?ACT(?LINK("config/complex"),"Advanced settings")] , Lang);
process2(["config" , "submit", "global"], #request{q = Query } = Request , US) ->
submit_config_global( Query , US),
process2(["config"], Request, US);
% process2(["config" , "submit", "contact"], #request{q = Query } = Request , US) ->
% submit_config_contact( Query , US),
% process2(["config"], Request, US);
process2(["config" , "complex" ], #request{lang = Lang } = _Request , {User, Server}) ->
make_xhtml(?T("Config"),
[?XE("h3", [?CT("Global Settings")]) ] ++ global_config_form_complex({User, Server}, Lang) ++
[?XE("h3", [?CT("Specific Contact Settings")]) ] ++ contact_config_form({User, Server}, Lang) ++
[?X("hr"), ?ACT(?LINK("config"),"Simple settings")] , Lang);
process2(["config" , "submit", "complex_global"], #request{q = Query } = Request , US) ->
submit_config_global_complex( Query , US),
process2(["config/complex"], Request, US);
process2(["contact"], #request{lang = Lang } = _Request , US) ->
make_xhtml(?T("Contact List"), [
?XE("ul", lists:map( fun({Node,Server,Count}) ->
With = jlib:jid_to_string({Node,Server,""}),
?LI([?AC(?LINK("contact/" ++ ejabberd_http:url_encode(With)), With ) ,
?C(" (" ++ Count ++")")] ) end,
get_contacts(US)))
], Lang);
process2(["contact" , Jid], #request{lang = Lang } = _Request , US) ->
make_xhtml(?T("Chat with ") ++ Jid, contact_config(Jid,US,Lang) ++
[?XE("ul", lists:map( fun({Id, Node, Server, Resource, Utc, Subject }) ->
With = jlib:jid_to_string({Node,Server,Resource}),
?LI([?AC(?LINK("show/" ++ integer_to_list(Id)), "On " ++ Utc ++ " with " ++ With ++ " -> " ++ escape_str(Subject) )] ) end,
get_collection_list(jlib:string_to_jid(Jid), US)))
], Lang);
process2(["show" , Id], #request{lang = Lang } = _Request , US) ->
{ With, Utc, Subject, List, NPId } = get_collection(Id, US),
[Date, _Time] = string:tokens(Utc, " "),
make_xhtml(?T("Chat with ") ++ jlib:jid_to_string(With) ++ ?T(" on ") ++ Date ++ ?T(" : ") ++ escape_str(Subject),
lists:map(fun(Msg) -> format_message(Msg,With, US) end, List)
++ links_previous_next(NPId, Lang)
++ [?X("hr"), ?XAE("form",[{"action",?LINK("edit/" ++ Id)},{"metohd","post"}],
[?XE("label",[?CT("Edit subject: "),
?INPUT("text","subject",escape_str(Subject))]),
?INPUT("submit","submit",?T("Ok"))]),
?XAE("form",[{"action",?LINK("delete/" ++ Id)},{"metohd","post"},
{"onsubmit","return confirm('"++ ?T("Do you realy want to delete this chat") ++"')"}],
[?INPUT("hidden","id",Id),?INPUT("submit","delete",?T("Delete"))])]
, Lang);
process2(["edit" , Id], #request{ q = Query} = Request , US) ->
case lists:keysearch("subject", 1, Query) of
{value, {_, Subject}} -> change_subject(Id,Subject,US);
_ -> ok
end,
process2(["show", Id] , Request, US);
process2(["delete" , Id], #request{q = Query, lang=Lang} = _Request , US) ->
case lists:keysearch("id", 1, Query) of
{value, {_, Id2}} when Id==Id2 ->
delete_collection(Id,US), make_xhtml("Chat deleted",[],Lang);
_ -> ?ERR_INTERNAL_SERVER_ERROR
end;
process2(["search"], #request{lang = Lang } = Request , US) ->
make_xhtml(?T("Search"), [
search_form(Request, US) ], Lang);
process2(["search", "results"], #request{lang = Lang } = Request , US) ->
make_xhtml(?T("Search"), [ search_form(Request, US) | search_results(Request, US)], Lang);
process2([], #request{lang = Lang } = _Request , {LUser,LServer}) ->
make_xhtml(?T("Archives viewer"),[?PCT("Welcome " ++ LUser ++ "@" ++ LServer)], Lang);
process2(_, #request{lang = Lang } = _Request , _US) ->
make_xhtml(?T("404 File not found"),[], Lang).
%------------------------------
make_xhtml(Title, Els, Lang) ->
{200, [html],
{xmlelement, "html", [{"xmlns", "http://www.w3.org/1999/xhtml"},
{"xml:lang", Lang},
{"lang", Lang}],
[{xmlelement, "head", [],
[?XE("title", [?C(Title) , ?CT(" - ejabberd Web Archive Viewer")]),
{xmlelement, "meta", [{"http-equiv", "Content-Type"},
{"content", "text/html; charset=utf-8"}], []},
{xmlelement, "link", [{"href", ?LINK("style.css")},
{"type", "text/css"},
{"rel", "stylesheet"}], []}]},
?XE("body",
[?XAE("div", [{"id", "container"}],
[?XAE("div", [{"id", "header"}],
[?XE("h1", [?CT("Archives Viewer")])]),
?XAE("div", [{"id", "navigation"}],
[?XE("ul",
[?LI([?ACT(?LINK("config"), "Config")]), ?C(" "),
?LI([?ACT(?LINK("contact"), "Browse")]), ?C(" "),
?LI([?ACT(?LINK("search"), "Search")])])]), ?C(" "),
?XAE("div", [{"id", "content"}], [ ?XE("h2", [?C(Title)]) | Els])])])]}}.
get_auth(Auth) ->
case Auth of
{SJID, P} ->
case jlib:string_to_jid(SJID) of
error ->
unauthorized;
#jid{user = U, server = S} ->
case ejabberd_auth:check_password(U, S, P) of
true ->
{U, S};
false ->
unauthorized
end
end;
_ ->
unauthorized
end.
select_element(Name, List, Value1) ->
Value = if is_integer(Value1) -> integer_to_list(Value1); true -> Value1 end,
?XAE("select",[{"name",Name}],lists:map(
fun({Key,Text}) -> ?XAE("option",
case Key of
Value -> [{"value",Value},{"selected","selected"}];
_ -> [{"value",Key}]
end, [?C(Text)]) end, List)).
table_element(Rows) ->
?XE("table",lists:map(fun(Cols)-> ?XE("tr", lists:map(fun(Ct)-> ?XE("td",Ct) end, Cols)) end, Rows)).
%------------------------
format_message({ Utc, Dir, Body } ,{WithU,WithS,WithR}, {LUser,LServer} ) ->
{From, Class} = case Dir of
0 -> { jlib:jid_to_string({WithU,WithS,WithR}) , "message_from" } ;
1 -> { jlib:jid_to_string({LUser,LServer,""}) , "message_to" }
end,
[_Date, Time] = string:tokens(Utc, " "),
?XAE("p", [{"class", Class}] , [ ?XAE("span", [{"class","time"}], [?C("["++Time++"]")]), ?C(" "),
?XAE("span", [{"class","jid"}], [?C(From)]), ?C(": "),
?XAE("span", [{"class","message_body"}], [?C(Body)])]).
contact_config(Jid,{LUser,LServer},Lang) ->
%run_sql_transaction(LServer, fun() -> run_sql_query("") end)
%[?XE("p",[?CT("Automatic archive with this contact is " + Au ), ].
[].
global_config_form({LUser,LServer},Lang) ->
{Save,Expire,Auto_save} =
case run_sql_transaction(LServer, fun() -> run_sql_query(
"SELECT save,expire,auto_save"
" FROM archive_global_prefs"
" WHERE us = " ++ get_us_escaped({LUser,LServer}) ) end) of
{selected, _ , [ Ok ]} -> Ok;
{selected, _ , [ ]} -> { -1, -1, -1 }
end,
[?XAE("form",[{"action",?LINK("config/submit/global")}],
[?XE("label",[?CT("Disable or enable automatic archiving globaly: "), select_element("global_auto_save",
[{"-1",?T("--Server Default--")},{"1",?T("Enabled")},{"0",?T("Disabled")}],decode_integer(Auto_save))]),
?BR,
?XE("label",[?CT("Default for contact not specified bellow : "), select_element("global_save",
[{"-1",?T("--Server Default--")},{"1",?T("Enabled")},{"0",?T("Disabled")}],decode_integer(Save))]),
?BR, ?XE("label",[?CT("Default expiration time: "), ?INPUT("text","global_expire",integer_to_list(decode_integer(Expire)))]),
?CT("(number of seconds before deleting message, '-1' = server default)"),
?BR, ?INPUTT("submit","global_submit","Submit")]
)].
global_config_form_complex({LUser,LServer},Lang) ->
{Save,Expire,Otr,Method_auto,Method_local,Method_manual,Auto_save} =
case run_sql_transaction(LServer, fun() -> run_sql_query(
"SELECT save,expire,otr,method_auto,method_local,method_manual,auto_save"
" FROM archive_global_prefs"
" WHERE us = " ++ get_us_escaped({LUser,LServer}) ) end) of
{selected, _ , [ Ok ]} -> Ok;
{selected, _ , [ ]} -> { -1, -1, -1, -1, -1, -1, -1 }
end,
MethodList = [ {"-1",?T("--Undefined--")}, {"0",?T("Prefer")}, {"1",?T("Concede")}, {"2",?T("Forbid")} ],
[?XAE("form",[{"action",?LINK("config/submit/complex_global")}],[table_element([[
[?XE("label",[?CT("Save: "), select_element("global_save",[{"-1",?T("--Default--")},{"1",?T("Enabled")},{"0",?T("Disabled")}],decode_integer(Save))])],
[?XE("label",[?CT("Expire: "), ?INPUT("text","global_expire",integer_to_list(decode_integer(Expire)))])],
[?XE("label",[?CT("Otr: "), select_element("global_otr",[{"-1",?T("--Undefined--")},
{"0",?T("Approve")},
{"1",?T("Concede")},
{"2",?T("Forbid")},
{"3",?T("Oppose")},
{"4",?T("Prefer")},
{"5",?T("Require")} ],decode_integer(Otr))])],
[?XE("label",[?CT("Auto Method: "), select_element("global_method_auto", MethodList,decode_integer(Method_auto))])],
[?XE("label",[?CT("Local Method: "), select_element("global_method_local", MethodList,decode_integer(Method_local))])],
[?XE("label",[?CT("Manual Method: "), select_element("global_method_manual", MethodList,decode_integer(Method_manual))])],
[?XE("label",[?CT("Auto Save "), select_element("global_auto_save",
[{"-1",?T("--Default--")},{"1",?T("Enabled")},{"0",?T("Disabled")}],decode_integer(Auto_save))])],
[?INPUT("submit","global_modify",?T("Modify"))]
]])])].
contact_config_form({LUser,LServer},Lang) ->
{selected, _, List} =
run_sql_transaction(LServer, fun() -> run_sql_query(
"SELECT with_user,with_server,with_resource,save,expire"
" FROM archive_jid_prefs"
" WHERE us = " ++ get_us_escaped({LUser,LServer}) ) end),
[ table_element([[[?CT("JID")],[?CT("Auto archive")],[?CT("Expire")]] |
lists:map(fun({WithU,WithS,WithR,Save,Expire}) ->
[ [?C(jlib:jid_to_string({WithU,WithS,WithR}))],
[case decode_integer(Save) of 1 -> ?CT("Enabled"); 0 -> ?CT("Disabled"); _ -> ?CT("Default") end],
[?C(integer_to_list(decode_integer(Expire)))] ] end , List ) ]) %,
% ?XAE("form",[{"action",?LINK("config/submit/contact")}],
% [?XE("label",[?CT("Add/Modify settings for Jid: "), ?INPUT("text","jid","")]),
% ?BR,
% ?XE("label",[?CT("Archiving : "), select_element("save",
% [{"-1",?T("--Default--")},{"1",?T("Enabled")},{"0",?T("Disabled")}],"-1")]),
% ?BR, ?XE("label",[?CT("Expiration time: "), ?INPUT("text","expire","-1")]),
% ?BR, ?INPUTT("submit","submit","Submit")]
% )
].
get_from_query_escaped(Key,Query) ->
{value, {_, Value}} = lists:keysearch(Key, 1, Query),
case Value of
-1 -> "NULL";
Integer when is_integer(Integer) -> Integer;
"-1" -> "NULL";
Value -> "'" ++ ejabberd_odbc:escape(Value) ++ "'"
end.
submit_config_global(Query , {LUser,LServer}) ->
SUS = get_us_escaped({LUser,LServer}),
SQLQuery =
"UPDATE archive_global_prefs"
" SET save = " ++ get_from_query_escaped("global_save",Query) ++ ","
" expire = " ++ get_from_query_escaped("global_expire",Query) ++ ","
" auto_save = " ++ get_from_query_escaped("global_auto_save",Query) ++
" WHERE us = " ++ SUS,
F = fun() ->
%TODO: use REPLACE
case run_sql_query("SELECT us FROM archive_global_prefs WHERE us = " ++ SUS) of
{selected, _, Rs} when Rs /= [] -> ok;
_ -> run_sql_query("INSERT INTO archive_global_prefs (us) VALUES (" ++ SUS ++ ")")
end,
run_sql_query(SQLQuery) end,
run_sql_transaction(LServer, F).
submit_config_global_complex(Query , {LUser,LServer}) ->
SUS = get_us_escaped({LUser,LServer}),
SQLQuery =
"UPDATE archive_global_prefs"
" SET save = " ++ get_from_query_escaped("global_save",Query) ++ ","
" expire = " ++ get_from_query_escaped("global_expire",Query) ++ ","
" otr = " ++ get_from_query_escaped("global_otr",Query) ++ ","
" method_auto = " ++ get_from_query_escaped("global_method_auto",Query) ++ ","
" method_local = " ++ get_from_query_escaped("global_method_local",Query) ++ ","
" method_manual = " ++ get_from_query_escaped("global_method_manual",Query) ++ ","
" auto_save = " ++ get_from_query_escaped("global_auto_save",Query) ++
" WHERE us = " ++ SUS,
F = fun() ->
%TODO: use REPLACE
case run_sql_query("SELECT us FROM archive_global_prefs WHERE us = " ++ SUS) of
{selected, _, Rs} when Rs /= [] -> ok;
_ -> run_sql_query("INSERT INTO archive_global_prefs (us) VALUES (" ++ SUS ++ ")")
end,
run_sql_query(SQLQuery) end,
run_sql_transaction(LServer, F).
get_from_query_with_default(Key,Query,Default) ->
case lists:keysearch(Key, 1, Query) of
{value, {_, Value}} -> Value;
_ -> Default
end.
search_form( #request{lang = Lang, q = Query } = _Request, US) ->
?XAE("form",[{"method","post"},{"action", ?LINK("search/results")}],
[ ?XE("label", [?CT("With: ") ,
select_element("with" , [ { "", "--All--" } |
lists:map( fun({Node,Server,_Count}) ->
With = jlib:jid_to_string({Node,Server,""}),
{With, With} end,
get_contacts(US)) ],
get_from_query_with_default("with",Query,""))]),
?BR,
?XE("label", [?CT("From: ") , ?INPUT("text","from", get_from_query_with_default("from",Query,"")),
?CT(" (date in SQL format, may be empty)")]),
?BR,
?XE("label", [?CT("To: ") , ?INPUT("text","to", get_from_query_with_default("to",Query,"")),
?CT(" (date in SQL format, may be empty)")]),
?BR,
?XE("label", [?CT("Search keyword: ") , ?INPUT("text","keywords",
get_from_query_with_default("keywords",Query,""))]),
?BR,
?INPUT("submit","search",?T("Search")) ]).
search_results( #request{lang = Lang, q = Query } = _Request, {_, LServer} = US) ->
With = case lists:keysearch("with", 1, Query) of
{value, {_, Value}} -> case jlib:string_to_jid(Value) of
#jid{ luser = Node , lserver = Server , lresource = "" } ->
" AND with_user ='" ++ ejabberd_odbc:escape(Node) ++ "'" ++
" AND with_server ='" ++ ejabberd_odbc:escape(Server) ++ "'";
#jid{ luser = Node , lserver = Server , lresource = Resource } ->
" AND with_user ='" ++ ejabberd_odbc:escape(Node) ++ "'" ++
" AND with_server ='" ++ ejabberd_odbc:escape(Server) ++ "'" ++
" AND with_resource ='" ++ ejabberd_odbc:escape(Resource) ++ "'";
_ -> ""
end;
_ -> ""
end,
From = case lists:keysearch("from", 1, Query) of
{value, {_, V1}} when V1 /= "" -> " AND M.utc >= '" ++ ejabberd_odbc:escape(V1) ++ "'";
_ -> ""
end,
To = case lists:keysearch("to", 1, Query) of
{value, {_, V2}} when V2 /= "" -> " AND M.utc <= '" ++ ejabberd_odbc:escape(V2) ++ "'";
_ -> ""
end,
Kw = case lists:keysearch("keywords", 1, Query) of
{value, {_, V3}} when V3 /= "" -> " AND body LIKE '%" ++ ejabberd_odbc:escape(V3) ++ "%'";
_ -> ""
end,
F = fun() -> run_sql_query(
"SELECT coll_id,subject,with_user,with_server,with_resource,C.utc,body"
" FROM archive_collections as C, archive_messages as M"
" WHERE C.id = M.coll_id AND C.us = " ++ get_us_escaped(US) ++
" AND C.deleted='0'" ++ With ++ From ++ To ++ Kw ++
" GROUP BY coll_id") end,
case run_sql_transaction(LServer,F) of
{selected, _ , []} -> [?PCT("No matches")];
{selected, _ , Results} ->
lists:map(fun(R) -> format_search_result(R,Lang) end, Results)
end.
format_search_result( {Id,Subject,User,Server,Resource,Utc,Body} ,_Lang) ->
?XAE("p",[{"class","search_result"}],
[?AC(?LINK("show/" ++ integer_to_list(Id)), jlib:jid_to_string({User,Server,Resource}) ++ " : " ++ escape_str(Subject)),
?C(Body), ?XE("em",[?C(Utc)]) ] ).
links_previous_next({PrevId,NextId},Lang) ->
[?XAE("p",[{"class","links_previous_next"}],
links_previous_next_aux("link_prev", ?T("Previous"), PrevId) ++ [?C(" ")] ++
links_previous_next_aux("link_next", ?T("Next"), NextId))].
links_previous_next_aux(Class, Text, Id) ->
case Id of
-1 -> [];
_ -> [?XAE("a",[{"href",?LINK("show/" ++ integer_to_list(Id))},{"class",Class}], [?C(Text)])]
end.
%------------------------
get_contacts({LUser, LServer}) ->
Fun = fun() ->
{selected, _ , Contacts} = run_sql_query("SELECT with_user,with_server,COUNT(*)"
" FROM archive_collections"
" WHERE us = " ++ get_us_escaped({LUser,LServer}) ++ " AND deleted=0"
" GROUP BY with_user,with_server"),
Contacts end,
run_sql_transaction(LServer, Fun).
get_collection_list(Jid, {LUser, LServer}) ->
{WithU, WithS, _} = get_jid_escaped(Jid),
Fun = fun() ->
{selected, _ , List} = run_sql_query("SELECT id,with_user,with_server,with_resource,utc,subject"
" FROM archive_collections"
" WHERE us = " ++ get_us_escaped({LUser,LServer}) ++
" AND deleted=0 "
" AND with_user = " ++ WithU ++
" AND with_server = " ++ WithS),
List end,
run_sql_transaction(LServer, Fun).
get_collection(Id,{LUser,LServer}) ->
Fun = fun() ->
SUS = get_us_escaped({LUser,LServer}),
{selected, _ , [{WithU, WithS, WithR, Utc, Subject}] } = run_sql_query(
"SELECT with_user,with_server,with_resource,utc,subject"
" FROM archive_collections"
" WHERE id = '" ++ ejabberd_odbc:escape(Id) ++ "'"
" AND us = " ++ SUS),
%If the previous query fail, that mean the collection doesn't exist or is not
% one of the users connection.
{selected, _ , List} = run_sql_query("SELECT utc,dir,body"
" FROM archive_messages"
" WHERE coll_id = '" ++ ejabberd_odbc:escape(Id) ++ "'"),
NextId = case run_sql_query("SELECT id"
" FROM archive_collections"
" WHERE us = " ++ SUS ++ " AND deleted=0 "
" AND with_user ='" ++ ejabberd_odbc:escape(WithU) ++ "'" ++
" AND with_server ='" ++ ejabberd_odbc:escape(WithS) ++ "'" ++
" AND utc > '" ++ Utc ++ "'" ++
" ORDER BY utc LIMIT 1") of
{selected, _ , [{V1}]} -> V1;
_ -> -1
end,
PrevId = case run_sql_query("SELECT id"
" FROM archive_collections"
" WHERE us = " ++ SUS ++ " AND deleted=0 "
" AND with_user ='" ++ ejabberd_odbc:escape(WithU) ++ "'" ++
" AND with_server ='" ++ ejabberd_odbc:escape(WithS) ++ "'" ++
" AND utc < '" ++ Utc ++ "'" ++
" ORDER BY utc DESC LIMIT 1") of
{selected, _ , [{V2}]} -> V2;
_ -> -1
end,
{ {WithU,WithS,WithR} , Utc, Subject , List, {PrevId,NextId}} end,
run_sql_transaction(LServer, Fun).
change_subject(Id,Subject,{LUser,LServer}) ->
run_sql_transaction(LServer, fun() -> run_sql_query(
"UPDATE archive_collections"
" SET subject='"++ ejabberd_odbc:escape(Subject)++"',"
" change_utc=NOW(), change_by='webview'"
" WHERE id = '" ++ ejabberd_odbc:escape(Id) ++ "'"
" AND us = " ++ get_us_escaped({LUser,LServer})) end).
delete_collection(Id,{LUser,LServer}) ->
run_sql_transaction(LServer, fun() -> run_sql_query(
"UPDATE archive_collections"
" SET deleted=1, subject='', thread = '', extra = '', prev_id = NULL, next_id = NULL,"
" change_utc=NOW(), change_by='webview'"
" WHERE id = '" ++ ejabberd_odbc:escape(Id) ++ "'"
" AND us = " ++ get_us_escaped({LUser,LServer})) end).
%------------------------
% from mod_archive_odbc
run_sql_query(Query) ->
?MYDEBUG("running query: ~p", [lists:flatten(Query)]),
case catch ejabberd_odbc:sql_query_t(Query) of
{'EXIT', Err} ->
?ERROR_MSG("unhandled exception during query: ~p", [Err]),
exit(Err);
{error, Err} ->
?ERROR_MSG("error during query: ~p", [Err]),
throw({error, Err});
aborted ->
?ERROR_MSG("query aborted ~p", [Query]),
throw(aborted);
R -> ?MYDEBUG("query result: ~p", [R]),
R
end.
run_sql_transaction(LServer, F) ->
DBHost = gen_mod:get_module_opt(LServer, ?MODULE, db_host, LServer),
case ejabberd_odbc:sql_transaction(DBHost, F) of
{atomic, R} ->
?MYDEBUG("succeeded transaction: ~p", [R]),
R;
{error, Err} -> {error, Err};
E ->
?ERROR_MSG("failed transaction: ~p, stack: ~p", [E, process_info(self(),backtrace)]),
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
get_us_escaped({LUser, LServer}) ->
"'" ++ ejabberd_odbc:escape(LUser ++ "@" ++ LServer) ++ "'".
get_jid_escaped({LUser, LServer, LResource}) ->
{"'" ++ ejabberd_odbc:escape(LUser), "'" ++ ejabberd_odbc:escape(LServer),
"'" ++ ejabberd_odbc:escape(LResource)};
get_jid_escaped(#jid{luser = LUser, lserver = LServer, lresource=LResource}) ->
{"'" ++ ejabberd_odbc:escape(LUser) ++ "'", "'" ++ ejabberd_odbc:escape(LServer) ++ "'",
"'" ++ ejabberd_odbc:escape(LResource) ++ "'"}.
decode_integer(Val) when is_integer(Val) ->
Val;
decode_integer(null) ->
-1;
decode_integer(Val) ->
list_to_integer(Val).
escape_str(null) -> "";
escape_str(Str) -> Str.