]> granicus.if.org Git - ejabberd/commitdiff
* src/web/ejabberd_web_admin.erl: Added hooks to allow plugins to
authorAlexey Shchepin <alexey@process-one.net>
Thu, 23 Aug 2007 00:51:54 +0000 (00:51 +0000)
committerAlexey Shchepin <alexey@process-one.net>
Thu, 23 Aug 2007 00:51:54 +0000 (00:51 +0000)
add their pages without modifying ejabberd_web_admin.erl (thanks
to Badlop)
* src/web/ejabberd_web_admin.hrl: Macro definitions moved here
* src/mod_shared_roster.erl: Updated
* src/mod_offline.erl: Likewise
* src/mod_offline_odbc.erl: Likewise

SVN Revision: 884

ChangeLog
src/mod_offline.erl
src/mod_offline_odbc.erl
src/mod_shared_roster.erl
src/web/ejabberd_web_admin.erl
src/web/ejabberd_web_admin.hrl [new file with mode: 0644]

index 9c6fe076a181c92414ba6460046a699aebb243b5..e35ca525cdd7e8e88d52134a7efa2dca2e1229b5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2007-08-23  Alexey Shchepin  <alexey@process-one.net>
+
+       * src/web/ejabberd_web_admin.erl: Added hooks to allow plugins to
+       add their pages without modifying ejabberd_web_admin.erl (thanks
+       to Badlop)
+       * src/web/ejabberd_web_admin.hrl: Macro definitions moved here
+       * src/mod_shared_roster.erl: Updated
+       * src/mod_offline.erl: Likewise
+       * src/mod_offline_odbc.erl: Likewise
+
 2007-08-22  Alexey Shchepin  <alexey@process-one.net>
 
        * src/jlib.erl: Use http_base_64:decode if available
index 8a1b5e8b21d6045f81fa522c4a2da0e4794d1eee..b35989268c7a3b1e639f4cc44fd29ac3199c3a51 100644 (file)
         pop_offline_messages/3,
         remove_expired_messages/0,
         remove_old_messages/1,
-        remove_user/2]).
+        remove_user/2,
+        webadmin_page/3,
+        webadmin_user/4]).
 
 -include("ejabberd.hrl").
 -include("jlib.hrl").
+-include("web/ejabberd_http.hrl").
+-include("web/ejabberd_web_admin.hrl").
 
 -record(offline_msg, {us, timestamp, expire, from, to, packet}).
 
@@ -42,6 +46,10 @@ start(Host, Opts) ->
                       ?MODULE, remove_user, 50),
     ejabberd_hooks:add(anonymous_purge_hook, Host,
                       ?MODULE, remove_user, 50),
+    ejabberd_hooks:add(webadmin_page_host, Host,
+                      ?MODULE, webadmin_page, 50),
+    ejabberd_hooks:add(webadmin_user, Host,
+                      ?MODULE, webadmin_user, 50),
     MaxOfflineMsgs = gen_mod:get_opt(user_max_messages, Opts, infinity),
     register(gen_mod:get_module_proc(Host, ?PROCNAME),
             spawn(?MODULE, init, [MaxOfflineMsgs])).
@@ -106,6 +114,10 @@ stop(Host) ->
                          ?MODULE, remove_user, 50),
     ejabberd_hooks:delete(anonymous_purge_hook, Host,
                          ?MODULE, remove_user, 50),
+    ejabberd_hooks:delete(webadmin_page_host, Host,
+                         ?MODULE, webadmin_page, 50),
+    ejabberd_hooks:delete(webadmin_user, Host,
+                         ?MODULE, webadmin_user, 50),
     Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
     exit(whereis(Proc), stop),
     {wait, Proc}.
@@ -420,3 +432,107 @@ discard_warn_sender(Msgs) ->
                To,
                From, Err)
       end, Msgs).
+
+
+webadmin_page(_, Host,
+             #request{us = _US,
+                      path = ["user", U, "queue"],
+                      q = Query,
+                      lang = Lang} = _Request) ->
+    Res = user_queue(U, Host, Query, Lang),
+    {stop, Res};
+
+webadmin_page(Acc, _, _) -> Acc.
+
+user_queue(User, Server, Query, Lang) ->
+    US = {jlib:nodeprep(User), jlib:nameprep(Server)},
+    Res = user_queue_parse_query(US, Query),
+    Msgs = lists:keysort(#offline_msg.timestamp,
+                        mnesia:dirty_read({offline_msg, US})),
+    FMsgs =
+       lists:map(
+         fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
+                          packet = {xmlelement, Name, Attrs, Els}} = Msg) ->
+                 ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
+                 {{Year, Month, Day}, {Hour, Minute, Second}} =
+                     calendar:now_to_local_time(TimeStamp),
+                 Time = lists:flatten(
+                          io_lib:format(
+                            "~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
+                            [Year, Month, Day, Hour, Minute, Second])),
+                 SFrom = jlib:jid_to_string(From),
+                 STo = jlib:jid_to_string(To),
+                 Attrs2 = jlib:replace_from_to_attrs(SFrom, STo, Attrs),
+                 Packet = {xmlelement, Name, Attrs2, Els},
+                 FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
+                 ?XE("tr",
+                     [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
+                      ?XAC("td", [{"class", "valign"}], Time),
+                      ?XAC("td", [{"class", "valign"}], SFrom),
+                      ?XAC("td", [{"class", "valign"}], STo),
+                      ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
+                    )
+         end, Msgs),
+    [?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
+                            [us_to_list(US)]))] ++
+       case Res of
+           ok -> [?CT("Submitted"), ?P];
+           nothing -> []
+       end ++
+       [?XAE("form", [{"action", ""}, {"method", "post"}],
+             [?XE("table",
+                  [?XE("thead",
+                       [?XE("tr",
+                            [?X("td"),
+                             ?XCT("td", "Time"),
+                             ?XCT("td", "From"),
+                             ?XCT("td", "To"),
+                             ?XCT("td", "Packet")
+                            ])]),
+                   ?XE("tbody",
+                       if
+                           FMsgs == [] ->
+                               [?XE("tr",
+                                    [?XAC("td", [{"colspan", "4"}], " ")]
+                                   )];
+                           true ->
+                               FMsgs
+                       end
+                      )]),
+              ?BR,
+              ?INPUTT("submit", "delete", "Delete Selected")
+             ])].
+
+user_queue_parse_query(US, Query) ->
+    case lists:keysearch("delete", 1, Query) of
+       {value, _} ->
+           Msgs = lists:keysort(#offline_msg.timestamp,
+                                mnesia:dirty_read({offline_msg, US})),
+           F = fun() ->
+                       lists:foreach(
+                         fun(Msg) ->
+                                 ID = jlib:encode_base64(
+                                        binary_to_list(term_to_binary(Msg))),
+                                 case lists:member({"selected", ID}, Query) of
+                                     true ->
+                                         mnesia:delete_object(Msg);
+                                     false ->
+                                         ok
+                                 end
+                         end, Msgs)
+               end,
+           mnesia:transaction(F),
+           ok;
+       false ->
+           nothing
+    end.
+
+us_to_list({User, Server}) ->
+    jlib:jid_to_string({User, Server, ""}).
+
+webadmin_user(Acc, User, Server, Lang) ->
+    US = {jlib:nodeprep(User), jlib:nameprep(Server)},
+    QueueLen = length(mnesia:dirty_read({offline_msg, US})),
+    FQueueLen = [?AC("queue/",
+                    integer_to_list(QueueLen))],
+    Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen.
index 30c0f2bb595b2640c263e02b6b176325711e89da..324ea55e1c2e854f714d06ee5997b9b8a5b7ee35 100644 (file)
@@ -3,8 +3,8 @@
 %%% Author  : Alexey Shchepin <alexey@sevcom.net>
 %%% Purpose : 
 %%% Created :  5 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
-%%% Id      : $Id$
 %%%----------------------------------------------------------------------
+
 -module(mod_offline_odbc).
 -author('alexey@sevcom.net').
 
         stop/1,
         store_packet/3,
         pop_offline_messages/3,
-        remove_user/2]).
+        remove_user/2,
+        webadmin_page/3,
+        webadmin_user/4]).
 
 -include("ejabberd.hrl").
 -include("jlib.hrl").
+-include("web/ejabberd_http.hrl").
+-include("web/ejabberd_web_admin.hrl").
 
 -record(offline_msg, {user, timestamp, expire, from, to, packet}).
 
@@ -34,6 +38,10 @@ start(Host, _Opts) ->
                       ?MODULE, remove_user, 50),
     ejabberd_hooks:add(anonymous_purge_hook, Host,
                       ?MODULE, remove_user, 50),
+    ejabberd_hooks:add(webadmin_page_host, Host,
+                      ?MODULE, webadmin_page, 50),
+    ejabberd_hooks:add(webadmin_user, Host,
+                      ?MODULE, webadmin_user, 50),
     register(gen_mod:get_module_proc(Host, ?PROCNAME),
             spawn(?MODULE, init, [Host])).
 
@@ -98,6 +106,10 @@ stop(Host) ->
                          ?MODULE, remove_user, 50),
     ejabberd_hooks:delete(anonymous_purge_hook, Host,
                          ?MODULE, remove_user, 50),
+    ejabberd_hooks:delete(webadmin_page_host, Host,
+                         ?MODULE, webadmin_page, 50),
+    ejabberd_hooks:delete(webadmin_user, Host,
+                         ?MODULE, webadmin_user, 50),
     Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
     exit(whereis(Proc), stop),
     ok.
@@ -180,20 +192,16 @@ find_x_expire(TimeStamp, [{xmlcdata, _} | Els]) ->
 find_x_expire(TimeStamp, [El | Els]) ->
     case xml:get_tag_attr_s("xmlns", El) of
        ?NS_EXPIRE ->
-           case xml:get_tag_attr_s("seconds", El) of
-               Val ->
-                   case catch list_to_integer(Val) of
-                       {'EXIT', _} ->
-                           never;
-                       Int when Int > 0 ->
-                           {MegaSecs, Secs, MicroSecs} = TimeStamp,
-                           S = MegaSecs * 1000000 + Secs + Int,
-                           MegaSecs1 = S div 1000000,
-                           Secs1 = S rem 1000000,
-                           {MegaSecs1, Secs1, MicroSecs};
-                       _ ->
-                           never
-                   end;
+           Val = xml:get_tag_attr_s("seconds", El),
+           case catch list_to_integer(Val) of
+               {'EXIT', _} ->
+                   never;
+               Int when Int > 0 ->
+                   {MegaSecs, Secs, MicroSecs} = TimeStamp,
+                   S = MegaSecs * 1000000 + Secs + Int,
+                   MegaSecs1 = S div 1000000,
+                   Secs1 = S rem 1000000,
+                   {MegaSecs1, Secs1, MicroSecs};
                _ ->
                    never
            end;
@@ -238,3 +246,139 @@ remove_user(User, Server) ->
     Username = ejabberd_odbc:escape(LUser),
     odbc_queries:del_spool_msg(LServer, Username).
 
+
+webadmin_page(_, Host,
+             #request{us = _US,
+                      path = ["user", U, "queue"],
+                      q = Query,
+                      lang = Lang} = _Request) ->
+    Res = user_queue(U, Host, Query, Lang),
+    {stop, Res};
+
+webadmin_page(Acc, _, _) -> Acc.
+
+user_queue(User, Server, Query, Lang) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    Username = ejabberd_odbc:escape(LUser),
+    US = {LUser, LServer},
+    Res = user_queue_parse_query(Username, LServer, Query),
+    Msgs = case catch ejabberd_odbc:sql_query(
+                       LServer,
+                       ["select username, xml from spool"
+                        "  where username='", Username, "'"
+                        "  order by seq;"]) of
+              {selected, ["username", "xml"], Rs} ->
+                  lists:flatmap(
+                    fun({_, XML}) ->
+                            case xml_stream:parse_element(XML) of
+                                {error, _Reason} ->
+                                    [];
+                                El ->
+                                    [El]
+                            end
+                    end, Rs);
+              _ ->
+                  []
+          end,
+    FMsgs =
+       lists:map(
+         fun({xmlelement, Name, Attrs, Els} = Msg) ->
+                 ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
+                 Packet = Msg,
+                 FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
+                 ?XE("tr",
+                     [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
+                      ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
+                    )
+         end, Msgs),
+    [?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
+                            [us_to_list(US)]))] ++
+       case Res of
+           ok -> [?CT("Submitted"), ?P];
+           nothing -> []
+       end ++
+       [?XAE("form", [{"action", ""}, {"method", "post"}],
+             [?XE("table",
+                  [?XE("thead",
+                       [?XE("tr",
+                            [?X("td"),
+                             ?XCT("td", "Packet")
+                            ])]),
+                   ?XE("tbody",
+                       if
+                           FMsgs == [] ->
+                               [?XE("tr",
+                                    [?XAC("td", [{"colspan", "4"}], " ")]
+                                   )];
+                           true ->
+                               FMsgs
+                       end
+                      )]),
+              ?BR,
+              ?INPUTT("submit", "delete", "Delete Selected")
+             ])].
+
+user_queue_parse_query(Username, LServer, Query) ->
+    case lists:keysearch("delete", 1, Query) of
+       {value, _} ->
+           Msgs = case catch ejabberd_odbc:sql_query(
+                               LServer,
+                               ["select xml, seq from spool"
+                                "  where username='", Username, "'"
+                                "  order by seq;"]) of
+                      {selected, ["xml", "seq"], Rs} ->
+                          lists:flatmap(
+                            fun({XML, Seq}) ->
+                                    case xml_stream:parse_element(XML) of
+                                        {error, _Reason} ->
+                                            [];
+                                        El ->
+                                            [{El, Seq}]
+                                    end
+                            end, Rs);
+                      _ ->
+                          []
+                  end,
+           F = fun() ->
+                       lists:foreach(
+                         fun({Msg, Seq}) ->
+                                 ID = jlib:encode_base64(
+                                        binary_to_list(term_to_binary(Msg))),
+                                 case lists:member({"selected", ID}, Query) of
+                                     true ->
+                                         SSeq = ejabberd_odbc:escape(Seq),
+                                         catch ejabberd_odbc:sql_query(
+                                                 LServer,
+                                                 ["delete from spool"
+                                                  "  where username='", Username, "'"
+                                                  "  and seq='", SSeq, "';"]);
+                                     false ->
+                                         ok
+                                 end
+                         end, Msgs)
+               end,
+           mnesia:transaction(F),
+           ok;
+       false ->
+           nothing
+    end.
+
+us_to_list({User, Server}) ->
+    jlib:jid_to_string({User, Server, ""}).
+
+webadmin_user(Acc, User, Server, Lang) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    Username = ejabberd_odbc:escape(LUser),
+    QueueLen = case catch ejabberd_odbc:sql_query(
+                           LServer,
+                           ["select count(*) from spool"
+                            "  where username='", Username, "';"]) of
+                  {selected, [_], [{SCount}]} ->
+                      SCount;
+                  _ ->
+                      0
+              end,
+    FQueueLen = [?AC("queue/", QueueLen)],
+    Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen.
index a0160b95993de25d2ae13cdc58fe452fb5eed6a6..11fe36863c9404f38ca92076603af87e83762214 100644 (file)
@@ -12,6 +12,7 @@
 -behaviour(gen_mod).
 
 -export([start/2, stop/1,
+        webadmin_menu/2, webadmin_page/3,
         get_user_roster/2,
         get_subscription_lists/3,
         get_jid_info/4,
@@ -32,6 +33,8 @@
 -include("ejabberd.hrl").
 -include("jlib.hrl").
 -include("mod_roster.hrl").
+-include("web/ejabberd_http.hrl").
+-include("web/ejabberd_web_admin.hrl").
 
 -record(sr_group, {group_host, opts}).
 -record(sr_user, {us, group_host}).
@@ -45,6 +48,10 @@ start(Host, _Opts) ->
                         {type, bag},
                         {attributes, record_info(fields, sr_user)}]),
     mnesia:add_table_index(sr_user, group_host),
+    ejabberd_hooks:add(webadmin_menu_host, Host,
+                      ?MODULE, webadmin_menu, 70),
+    ejabberd_hooks:add(webadmin_page_host, Host,
+                      ?MODULE, webadmin_page, 50),
     ejabberd_hooks:add(roster_get, Host,
                       ?MODULE, get_user_roster, 70),
     ejabberd_hooks:add(roster_in_subscription, Host,
@@ -61,6 +68,10 @@ start(Host, _Opts) ->
     %                 ?MODULE, remove_user, 50),
 
 stop(Host) ->
+    ejabberd_hooks:delete(webadmin_menu_host, Host,
+                         ?MODULE, webadmin_menu, 70),
+    ejabberd_hooks:delete(webadmin_page_host, Host,
+                         ?MODULE, webadmin_page, 50),
     ejabberd_hooks:delete(roster_get, Host,
                          ?MODULE, get_user_roster, 70),
     ejabberd_hooks:delete(roster_in_subscription, Host,
@@ -352,3 +363,249 @@ remove_user_from_group(Host, US, Group) ->
                mnesia:delete_object(R)
        end,
     mnesia:transaction(F).
+
+
+%%---------------------
+%% Web Admin
+%%---------------------
+
+webadmin_menu(Acc, _Host) ->
+    [{"shared-roster", "Shared Roster"} | Acc].
+
+webadmin_page(_, Host,
+             #request{us = US,
+                      path = ["shared-roster"],
+                      q = Query,
+                      lang = Lang} = Request) ->
+    Res = list_shared_roster_groups(Host, Query, Lang),
+    {stop, Res};
+
+webadmin_page(_, Host,
+             #request{us = US,
+                      path = ["shared-roster", Group],
+                      q = Query,
+                      lang = Lang} = Request) ->
+    Res = shared_roster_group(Host, Group, Query, Lang),
+    {stop, Res};
+
+webadmin_page(Acc, _, _) -> Acc.
+
+list_shared_roster_groups(Host, Query, Lang) ->
+    Res = list_sr_groups_parse_query(Host, Query),
+    SRGroups = mod_shared_roster:list_groups(Host),
+    FGroups =
+       ?XAE("table", [],
+            [?XE("tbody",
+                 lists:map(
+                   fun(Group) ->
+                           ?XE("tr",
+                               [?XE("td", [?INPUT("checkbox", "selected",
+                                                  Group)]),
+                                ?XE("td", [?AC(Group ++ "/", Group)])
+                               ]
+                              )
+                   end, lists:sort(SRGroups)) ++
+                 [?XE("tr",
+                      [?X("td"),
+                       ?XE("td", [?INPUT("text", "namenew", "")]),
+                       ?XE("td", [?INPUTT("submit", "addnew", "Add New")])
+                      ]
+                     )]
+                )]),
+    [?XC("h1", ?T("Shared Roster Groups"))] ++
+       case Res of
+           ok -> [?CT("Submitted"), ?P];
+           error -> [?CT("Bad format"), ?P];
+           nothing -> []
+       end ++
+       [?XAE("form", [{"action", ""}, {"method", "post"}],
+             [FGroups,
+              ?BR,
+              ?INPUTT("submit", "delete", "Delete Selected")
+             ])
+       ].
+
+list_sr_groups_parse_query(Host, Query) ->
+    case lists:keysearch("addnew", 1, Query) of
+       {value, _} ->
+           list_sr_groups_parse_addnew(Host, Query);
+       _ ->
+           case lists:keysearch("delete", 1, Query) of
+               {value, _} ->
+                   list_sr_groups_parse_delete(Host, Query);
+               _ ->
+                   nothing
+           end
+    end.
+
+list_sr_groups_parse_addnew(Host, Query) ->
+    case lists:keysearch("namenew", 1, Query) of
+       {value, {_, Group}} when Group /= "" ->
+           mod_shared_roster:create_group(Host, Group),
+           ok;
+       _ ->
+           error
+    end.
+
+list_sr_groups_parse_delete(Host, Query) ->
+    SRGroups = mod_shared_roster:list_groups(Host),
+    lists:foreach(
+      fun(Group) ->
+             case lists:member({"selected", Group}, Query) of
+                 true ->
+                     mod_shared_roster:delete_group(Host, Group);
+                 _ ->
+                     ok
+             end
+      end, SRGroups),
+    ok.
+
+
+shared_roster_group(Host, Group, Query, Lang) ->
+    Res = shared_roster_group_parse_query(Host, Group, Query),
+    GroupOpts = mod_shared_roster:get_group_opts(Host, Group),
+    Name = get_opt(GroupOpts, name, ""),
+    Description = get_opt(GroupOpts, description, ""),
+    AllUsers = get_opt(GroupOpts, all_users, false),
+    Disabled = false,
+    DisplayedGroups = get_opt(GroupOpts, displayed_groups, []),
+    Members = mod_shared_roster:get_group_explicit_users(Host, Group),
+    FMembers =
+       if
+           AllUsers ->
+               "@all@\n";
+           true ->
+               []
+       end ++ [[us_to_list(Member), $\n] || Member <- Members],
+    FDisplayedGroups = [[DG, $\n] || DG <- DisplayedGroups],
+    FGroup =
+       ?XAE("table", [],
+            [?XE("tbody",
+                 [?XE("tr",
+                      [?XCT("td", "Name:"),
+                       ?XE("td", [?INPUT("text", "name", Name)])
+                      ]
+                     ),
+                  ?XE("tr",
+                      [?XCT("td", "Description:"),
+                       ?XE("td", [?XAC("textarea", [{"name", "description"},
+                                                    {"rows", "3"},
+                                                    {"cols", "20"}],
+                                       Description)])
+                      ]
+                     ),
+                  ?XE("tr",
+                      [?XCT("td", "Members:"),
+                       ?XE("td", [?XAC("textarea", [{"name", "members"},
+                                                    {"rows", "3"},
+                                                    {"cols", "20"}],
+                                       FMembers)])
+                      ]
+                     ),
+                  ?XE("tr",
+                      [?XCT("td", "Displayed Groups:"),
+                       ?XE("td", [?XAC("textarea", [{"name", "dispgroups"},
+                                                    {"rows", "3"},
+                                                    {"cols", "20"}],
+                                       FDisplayedGroups)])
+                      ]
+                     )]
+                )]),
+    [?XC("h1", ?T("Shared Roster Groups"))] ++
+       [?XC("h2", ?T("Group ") ++ Group)] ++
+       case Res of
+           ok -> [?CT("Submitted"), ?P];
+           error -> [?CT("Bad format"), ?P];
+           nothing -> []
+       end ++
+       [?XAE("form", [{"action", ""}, {"method", "post"}],
+             [FGroup,
+              ?BR,
+              ?INPUTT("submit", "submit", "Submit")
+             ])
+       ].
+
+shared_roster_group_parse_query(Host, Group, Query) ->
+    case lists:keysearch("submit", 1, Query) of
+       {value, _} ->
+           {value, {_, Name}} = lists:keysearch("name", 1, Query),
+           {value, {_, Description}} = lists:keysearch("description", 1, Query),
+           {value, {_, SMembers}} = lists:keysearch("members", 1, Query),
+           {value, {_, SDispGroups}} = lists:keysearch("dispgroups", 1, Query),
+           NameOpt =
+               if
+                   Name == "" -> [];
+                   true -> [{name, Name}]
+               end,
+           DescriptionOpt =
+               if
+                   Description == "" -> [];
+                   true -> [{description, Description}]
+               end,
+           DispGroups = string:tokens(SDispGroups, "\r\n"),
+           DispGroupsOpt =
+               if
+                   DispGroups == [] -> [];
+                   true -> [{displayed_groups, DispGroups}]
+               end,
+
+           OldMembers = mod_shared_roster:get_group_explicit_users(
+                          Host, Group),
+           SJIDs = string:tokens(SMembers, ", \r\n"),
+           NewMembers =
+               lists:foldl(
+                 fun(_SJID, error) -> error;
+                    (SJID, USs) ->
+                         case SJID of
+                             "@all@" ->
+                                 USs;
+                             _ ->
+                                 case jlib:string_to_jid(SJID) of
+                                     JID when is_record(JID, jid) ->
+                                         [{JID#jid.luser, JID#jid.lserver} | USs];
+                                     error ->
+                                         error
+                                 end
+                         end
+                 end, [], SJIDs),
+           AllUsersOpt =
+               case lists:member("@all@", SJIDs) of
+                   true -> [{all_users, true}];
+                   false -> []
+               end,
+
+           mod_shared_roster:set_group_opts(
+             Host, Group,
+             NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt),
+
+           if
+               NewMembers == error -> error;
+               true ->
+                   AddedMembers = NewMembers -- OldMembers,
+                   RemovedMembers = OldMembers -- NewMembers,
+                   lists:foreach(
+                     fun(US) ->
+                             mod_shared_roster:remove_user_from_group(
+                               Host, US, Group)
+                     end, RemovedMembers),
+                   lists:foreach(
+                     fun(US) ->
+                             mod_shared_roster:add_user_to_group(
+                               Host, US, Group)
+                     end, AddedMembers),
+                   ok
+           end;
+       _ ->
+           nothing
+    end.
+
+get_opt(Opts, Opt, Default) ->
+    case lists:keysearch(Opt, 1, Opts) of
+       {value, {_, Val}} ->
+           Val;
+       false ->
+           Default
+    end.
+
+us_to_list({User, Server}) ->
+    jlib:jid_to_string({User, Server, ""}).
index 4744520382fc1c82e6ed9e5099583da5061c2aff..c760a823317edba6102b1c96b8dc1e4c8af54226 100644 (file)
@@ -3,7 +3,6 @@
 %%% Author  : Alexey Shchepin <alexey@sevcom.net>
 %%% Purpose : Administration web interface
 %%% Created :  9 Apr 2004 by Alexey Shchepin <alexey@sevcom.net>
-%%% Id      : $Id$
 %%%----------------------------------------------------------------------
 %%% Copyright (c) 2004-2006 Alexey Shchepin
 %%% Copyright (c) 2004-2006 Process One
 
 %% External exports
 -export([process/2,
-         %% XXX bard: unexported, since it is only called from process/2 now
-         %% process_admin/2,
         list_users/4,
-        list_users_in_diapason/4]).
+        list_users_in_diapason/4,
+        pretty_print_xml/1]).
 
 -include("ejabberd.hrl").
 -include("jlib.hrl").
 -include("ejabberd_http.hrl").
-
--define(X(Name), {xmlelement, Name, [], []}).
--define(XA(Name, Attrs), {xmlelement, Name, Attrs, []}).
--define(XE(Name, Els), {xmlelement, Name, [], Els}).
--define(XAE(Name, Attrs, Els), {xmlelement, Name, Attrs, Els}).
--define(C(Text), {xmlcdata, Text}).
--define(XC(Name, Text), ?XE(Name, [?C(Text)])).
--define(XAC(Name, Attrs, Text), ?XAE(Name, Attrs, [?C(Text)])).
-
--define(T(Text), translate:translate(Lang, Text)).
--define(CT(Text), ?C(?T(Text))).
--define(XCT(Name, Text), ?XC(Name, ?T(Text))).
--define(XACT(Name, Attrs, Text), ?XAC(Name, Attrs, ?T(Text))).
-
-
--define(LI(Els), ?XE("li", Els)).
--define(A(URL, Els), ?XAE("a", [{"href", URL}], Els)).
--define(AC(URL, Text), ?A(URL, [?C(Text)])).
--define(ACT(URL, Text), ?AC(URL, ?T(Text))).
--define(P, ?X("p")).
--define(BR, ?X("br")).
--define(INPUT(Type, Name, Value),
-       ?XA("input", [{"type", Type},
-                     {"name", Name},
-                     {"value", Value}])).
--define(INPUTT(Type, Name, Value), ?INPUT(Type, Name, ?T(Value))).
--define(INPUTS(Type, Name, Value, Size),
-       ?XA("input", [{"type", Type},
-                     {"name", Name},
-                     {"value", Value},
-                     {"size", Size}])).
--define(INPUTST(Type, Name, Value, Size), ?INPUT(Type, Name, ?T(Value), Size)).
+-include("ejabberd_web_admin.hrl").
 
 
 process(["server", SHost | RPath], #request{auth = Auth,
@@ -126,6 +93,8 @@ get_auth(Auth) ->
     end.
 
 make_xhtml(Els, global, Lang) ->
+    MenuItems1 = ejabberd_hooks:run_fold(webadmin_menu_main, [], []),
+    MenuItems2 = [?LI([?ACT("/admin/"++MI_uri++"/", MI_name)]) || {MI_uri, MI_name} <- MenuItems1],
     {200, [html],
      {xmlelement, "html", [{"xmlns", "http://www.w3.org/1999/xhtml"},
                           {"xml:lang", Lang},
@@ -153,7 +122,7 @@ make_xhtml(Els, global, Lang) ->
                             ?LI([?ACT("/admin/vhosts/", "Virtual Hosts")]),
                             ?LI([?ACT("/admin/nodes/", "Nodes")]),
                             ?LI([?ACT("/admin/stats/", "Statistics")])
-                           ]
+                           ] ++ MenuItems2
                           )]),
                  ?XAE("div",
                       [{"id", "content"}],
@@ -172,6 +141,8 @@ make_xhtml(Els, global, Lang) ->
 
 make_xhtml(Els, Host, Lang) ->
     Base = "/admin/server/" ++ Host ++ "/",
+    MenuItems1 = ejabberd_hooks:run_fold(webadmin_menu_host, Host, [], [Host]),
+    MenuItems2 = [?LI([?ACT(Base ++ MI_uri ++ "/", MI_name)]) || {MI_uri, MI_name} <- MenuItems1],
     {200, [html],
      {xmlelement, "html", [{"xmlns", "http://www.w3.org/1999/xhtml"},
                           {"xml:lang", Lang},
@@ -205,14 +176,7 @@ make_xhtml(Els, Host, Lang) ->
                             ?LI([?ACT(Base ++ "last-activity/", "Last Activity")]),
                             ?LI([?ACT(Base ++ "nodes/", "Nodes")]),
                             ?LI([?ACT(Base ++ "stats/", "Statistics")])
-                           ] ++
-                           case lists:member(mod_shared_roster,
-                                             gen_mod:loaded_modules(Host)) of
-                               true ->
-                                   [?LI([?ACT(Base ++ "shared-roster/", "Shared Roster")])];
-                               false ->
-                                   []
-                           end
+                           ] ++ MenuItems2
                           )]),
                  ?XAE("div",
                       [{"id", "content"}],
@@ -607,6 +571,8 @@ process_admin(global,
                       path = [],
                       q = Query,
                       lang = Lang} = Request) ->
+    MenuItems1 = ejabberd_hooks:run_fold(webadmin_menu_main, [], []),
+    MenuItems2 = [?LI([?ACT("/admin/"++MI_uri++"/", MI_name)]) || {MI_uri, MI_name} <- MenuItems1],
     make_xhtml([?XCT("h1", "Administration"),
                ?XE("ul",
                    [?LI([?ACT("/admin/acls/", "Access Control Lists"), ?C(" "),
@@ -616,7 +582,7 @@ process_admin(global,
                     ?LI([?ACT("/admin/vhosts/", "Virtual Hosts")]),
                     ?LI([?ACT("/admin/nodes/", "Nodes")]),
                     ?LI([?ACT("/admin/stats/", "Statistics")])
-                   ]
+                   ] ++ MenuItems2
                   )
               ], global, Lang);
 
@@ -626,6 +592,8 @@ process_admin(Host,
                       q = Query,
                       lang = Lang} = Request) ->
     Base = "/admin/server/" ++ Host ++ "/",
+    MenuItems1 = ejabberd_hooks:run_fold(webadmin_menu_host, Host, [], [Host]),
+    MenuItems2 = [?LI([?ACT(Base ++ MI_uri ++ "/", MI_name)]) || {MI_uri, MI_name} <- MenuItems1],
     make_xhtml([?XCT("h1", "Administration"),
                ?XE("ul",
                    [?LI([?ACT(Base ++ "acls/", "Access Control Lists"), ?C(" "),
@@ -637,14 +605,7 @@ process_admin(Host,
                     ?LI([?ACT(Base ++ "last-activity/", "Last Activity")]),
                     ?LI([?ACT(Base ++ "nodes/", "Nodes")]),
                     ?LI([?ACT(Base ++ "stats/", "Statistics")])
-                   ] ++
-                   case lists:member(mod_shared_roster,
-                                     gen_mod:loaded_modules(Host)) of
-                       true ->
-                           [?LI([?ACT(Base ++ "shared-roster/", "Shared Roster")])];
-                       false ->
-                           []
-                   end
+                   ] ++ MenuItems2
                   )
               ], Host, Lang);
 
@@ -996,14 +957,6 @@ process_admin(Host,
     Res = user_info(U, Host, Query, Lang),
     make_xhtml(Res, Host, Lang);
 
-process_admin(Host,
-             #request{us = US,
-                      path = ["user", U, "queue"],
-                      q = Query,
-                      lang = Lang} = Request) ->
-    Res = user_queue(U, Host, Query, Lang),
-    make_xhtml(Res, Host, Lang);
-
 process_admin(Host,
              #request{us = US,
                       path = ["user", U, "roster"],
@@ -1033,25 +986,15 @@ process_admin(Host,
            make_xhtml(Res, Host, Lang)
     end;
 
-process_admin(Host,
-             #request{us = US,
-                      path = ["shared-roster"],
-                      q = Query,
-                      lang = Lang} = Request) ->
-    Res = list_shared_roster_groups(Host, Query, Lang),
-    make_xhtml(Res, Host, Lang);
-
-process_admin(Host,
-             #request{us = US,
-                      path = ["shared-roster", Group],
-                      q = Query,
-                      lang = Lang} = Request) ->
-    Res = shared_roster_group(Host, Group, Query, Lang),
-    make_xhtml(Res, Host, Lang);
-
-process_admin(Host,
-             #request{lang = Lang}) ->
-    setelement(1, make_xhtml([?XC("h1", "Not Found")], Host, Lang), 404).
+process_admin(Host, #request{lang = Lang} = Request) ->
+    {Hook, Opts} = case Host of
+                      global -> {webadmin_page_main, [Request]};
+                      Host -> {webadmin_page_host, [Host, Request]}
+                  end,
+    case ejabberd_hooks:run_fold(Hook, Host, [], Opts) of
+       [] -> setelement(1, make_xhtml([?XC("h1", "Not Found")], Host, Lang), 404);
+       Res -> make_xhtml(Res, Host, Lang)
+    end.
 
 
 
@@ -1076,8 +1019,6 @@ acls_to_xhtml(ACLs) ->
                  )]
             )]).
 
--define(ACLINPUT(Text), ?XE("td", [?INPUT("text", "value" ++ ID, Text)])).
-
 acl_spec_to_text({user, U}) ->
     {user, U};
 
@@ -1489,7 +1430,8 @@ list_online_users(Host, _Lang) ->
       end, SUsers).
 
 user_info(User, Server, Query, Lang) ->
-    US = {jlib:nodeprep(User), jlib:nameprep(Server)},
+    LServer = jlib:nameprep(Server),
+    US = {jlib:nodeprep(User), LServer},
     Res = user_parse_query(User, Server, Query),
     Resources = ejabberd_sm:get_user_resources(User, Server),
     FResources =
@@ -1516,9 +1458,8 @@ user_info(User, Server, Query, Lang) ->
     Password = ejabberd_auth:get_password_s(User, Server),
     FPassword = [?INPUT("password", "password", Password), ?C(" "),
                 ?INPUTT("submit", "chpassword", "Change Password")],
-    QueueLen = length(mnesia:dirty_read({offline_msg, US})),
-    FQueueLen = [?AC("queue/",
-                    integer_to_list(QueueLen))],
+    UserItems = ejabberd_hooks:run_fold(webadmin_user, LServer, [],
+                                       [User, Server, Lang]),
     [?XC("h1", ?T("User ") ++ us_to_list(US))] ++
        case Res of
            ok -> [?CT("Submitted"), ?P];
@@ -1528,9 +1469,9 @@ user_info(User, Server, Query, Lang) ->
        [?XAE("form", [{"action", ""}, {"method", "post"}],
              [?XCT("h3", "Connected Resources:")] ++ FResources ++
              [?XCT("h3", "Password:")] ++ FPassword ++
-             [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++
              [?XE("h3", [?ACT("roster/", "Roster")])] ++
-             [?BR, ?INPUTT("submit", "removeuser", "Remove User")])].
+             UserItems ++
+             [?P, ?INPUTT("submit", "removeuser", "Remove User")])].
 
 
 user_parse_query(User, Server, Query) ->
@@ -1556,89 +1497,6 @@ user_parse_query(User, Server, Query) ->
     end.
 
 
-user_queue(User, Server, Query, Lang) ->
-    US = {jlib:nodeprep(User), jlib:nameprep(Server)},
-    Res = user_queue_parse_query(US, Query),
-    Msgs = lists:keysort(3, mnesia:dirty_read({offline_msg, US})),
-    FMsgs =
-       lists:map(
-         fun({offline_msg, _US, TimeStamp, _Expire, From, To,
-              {xmlelement, Name, Attrs, Els}} = Msg) ->
-                 ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
-                 {{Year, Month, Day}, {Hour, Minute, Second}} =
-                     calendar:now_to_local_time(TimeStamp),
-                 Time = lists:flatten(
-                          io_lib:format(
-                            "~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
-                            [Year, Month, Day, Hour, Minute, Second])),
-                 SFrom = jlib:jid_to_string(From),
-                 STo = jlib:jid_to_string(To),
-                 Attrs2 = jlib:replace_from_to_attrs(SFrom, STo, Attrs),
-                 Packet = jlib:remove_attr(
-                            "jeai-id", {xmlelement, Name, Attrs2, Els}),
-                 FPacket = pretty_print(Packet),
-                 ?XE("tr",
-                     [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
-                      ?XAC("td", [{"class", "valign"}], Time),
-                      ?XAC("td", [{"class", "valign"}], SFrom),
-                      ?XAC("td", [{"class", "valign"}], STo),
-                      ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
-                    )
-         end, Msgs),
-    [?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
-                            [us_to_list(US)]))] ++
-       case Res of
-           ok -> [?CT("Submitted"), ?P];
-           nothing -> []
-       end ++
-       [?XAE("form", [{"action", ""}, {"method", "post"}],
-             [?XE("table",
-                  [?XE("thead",
-                       [?XE("tr",
-                            [?X("td"),
-                             ?XCT("td", "Time"),
-                             ?XCT("td", "From"),
-                             ?XCT("td", "To"),
-                             ?XCT("td", "Packet")
-                            ])]),
-                   ?XE("tbody",
-                       if
-                           FMsgs == [] ->
-                               [?XE("tr",
-                                    [?XAC("td", [{"colspan", "4"}], " ")]
-                                   )];
-                           true ->
-                               FMsgs
-                       end
-                      )]),
-              ?BR,
-              ?INPUTT("submit", "delete", "Delete Selected")
-             ])].
-
-user_queue_parse_query(US, Query) ->
-    case lists:keysearch("delete", 1, Query) of
-       {value, _} ->
-           Msgs = lists:keysort(3, mnesia:dirty_read({offline_msg, US})),
-           F = fun() ->
-                       lists:foreach(
-                         fun(Msg) ->
-                                 ID = jlib:encode_base64(
-                                        binary_to_list(term_to_binary(Msg))),
-                                 case lists:member({"selected", ID}, Query) of
-                                     true ->
-                                         mnesia:delete_object(Msg);
-                                     false ->
-                                         ok
-                                 end
-                         end, Msgs)
-               end,
-           mnesia:transaction(F),
-           ok;
-       false ->
-           nothing
-    end.
-
-
 
 -record(roster, {usj,
                 us,
@@ -1922,6 +1780,8 @@ search_running_node(SNode, [Node | Nodes]) ->
 
 get_node(global, Node, [], Query, Lang) ->
     Res = node_parse_query(Node, Query),
+    MenuItems1 = ejabberd_hooks:run_fold(webadmin_menu_node, [], [Node]),
+    MenuItems2 = [?LI([?ACT(MI_uri++"/", MI_name)]) || {MI_uri, MI_name} <- MenuItems1],
     [?XC("h1", ?T("Node ") ++ atom_to_list(Node))] ++
        case Res of
            ok -> [?CT("Submitted"), ?P];
@@ -1934,7 +1794,7 @@ get_node(global, Node, [], Query, Lang) ->
              ?LI([?ACT("ports/", "Listened Ports")]),
              ?LI([?ACT("stats/", "Statistics")]),
              ?LI([?ACT("update/", "Update")])
-            ]),
+            ] ++ MenuItems2),
         ?XAE("form", [{"action", ""}, {"method", "post"}],
              [?INPUTT("submit", "restart", "Restart"),
               ?C(" "),
@@ -1942,9 +1802,11 @@ get_node(global, Node, [], Query, Lang) ->
        ];
 
 get_node(Host, Node, [], Query, Lang) ->
+    MenuItems1 = ejabberd_hooks:run_fold(webadmin_menu_hostnode, Host, [], [Host, Node]),
+    MenuItems2 = [?LI([?ACT(MI_uri++"/", MI_name)]) || {MI_uri, MI_name} <- MenuItems1],
     [?XC("h1", ?T("Node ") ++ atom_to_list(Node)),
      ?XE("ul",
-        [?LI([?ACT("modules/", "Modules")])])
+        [?LI([?ACT("modules/", "Modules")])] ++ MenuItems2)
     ];
 
 get_node(global, Node, ["db"], Query, Lang) ->
@@ -2176,7 +2038,15 @@ get_node(global, Node, ["update"], Query, Lang) ->
        ];
 
 get_node(Host, Node, NPath, Query, Lang) ->
-    [?XCT("h1", "Not Found")].
+    {Hook, Opts} = case Host of
+                      global -> {webadmin_page_node, [Node, NPath, Query]};
+                      Host -> {webadmin_page_hostnode, [Host, Node, NPath, Query]}
+                  end,
+    case ejabberd_hooks:run_fold(Hook, Host, [], Opts) of
+       [] -> [?XC("h1", "Not Found")];
+       Res -> Res
+    end.
+
 
 
 node_parse_query(Node, Query) ->
@@ -2464,12 +2334,12 @@ node_update_parse_query(Node, Query) ->
     end.
 
 
-pretty_print(El) ->
-    lists:flatten(pretty_print(El, "")).
+pretty_print_xml(El) ->
+    lists:flatten(pretty_print_xml(El, "")).
 
-pretty_print({xmlcdata, CData}, Prefix) ->
+pretty_print_xml({xmlcdata, CData}, Prefix) ->
     [Prefix, CData, $\n];
-pretty_print({xmlelement, Name, Attrs, Els}, Prefix) ->
+pretty_print_xml({xmlelement, Name, Attrs, Els}, Prefix) ->
     [Prefix, $<, Name,
      case Attrs of
         [] ->
@@ -2499,7 +2369,7 @@ pretty_print({xmlelement, Name, Attrs, Els}, Prefix) ->
                 true ->
                     [$>, $\n,
                      lists:map(fun(E) ->
-                                       pretty_print(E, [Prefix, "  "])
+                                       pretty_print_xml(E, [Prefix, "  "])
                                end, Els),
                      Prefix, $<, $/, Name, $>, $\n
                     ]
@@ -2507,225 +2377,6 @@ pretty_print({xmlelement, Name, Attrs, Els}, Prefix) ->
      end].
 
 
-list_shared_roster_groups(Host, Query, Lang) ->
-    Res = list_sr_groups_parse_query(Host, Query),
-    SRGroups = mod_shared_roster:list_groups(Host),
-    FGroups =
-       ?XAE("table", [],
-            [?XE("tbody",
-                 lists:map(
-                   fun(Group) ->
-                           ?XE("tr",
-                               [?XE("td", [?INPUT("checkbox", "selected",
-                                                  Group)]),
-                                ?XE("td", [?AC(Group ++ "/", Group)])
-                               ]
-                              )
-                   end, lists:sort(SRGroups)) ++
-                 [?XE("tr",
-                      [?X("td"),
-                       ?XE("td", [?INPUT("text", "namenew", "")]),
-                       ?XE("td", [?INPUTT("submit", "addnew", "Add New")])
-                      ]
-                     )]
-                )]),
-    [?XC("h1", ?T("Shared Roster Groups"))] ++
-       case Res of
-           ok -> [?CT("Submitted"), ?P];
-           error -> [?CT("Bad format"), ?P];
-           nothing -> []
-       end ++
-       [?XAE("form", [{"action", ""}, {"method", "post"}],
-             [FGroups,
-              ?BR,
-              ?INPUTT("submit", "delete", "Delete Selected")
-             ])
-       ].
-
-list_sr_groups_parse_query(Host, Query) ->
-    case lists:keysearch("addnew", 1, Query) of
-       {value, _} ->
-           list_sr_groups_parse_addnew(Host, Query);
-       _ ->
-           case lists:keysearch("delete", 1, Query) of
-               {value, _} ->
-                   list_sr_groups_parse_delete(Host, Query);
-               _ ->
-                   nothing
-           end
-    end.
-
-list_sr_groups_parse_addnew(Host, Query) ->
-    case lists:keysearch("namenew", 1, Query) of
-       {value, {_, Group}} when Group /= "" ->
-           mod_shared_roster:create_group(Host, Group),
-           ok;
-       _ ->
-           error
-    end.
-
-list_sr_groups_parse_delete(Host, Query) ->
-    SRGroups = mod_shared_roster:list_groups(Host),
-    lists:foreach(
-      fun(Group) ->
-             case lists:member({"selected", Group}, Query) of
-                 true ->
-                     mod_shared_roster:delete_group(Host, Group);
-                 _ ->
-                     ok
-             end
-      end, SRGroups),
-    ok.
-
-
-shared_roster_group(Host, Group, Query, Lang) ->
-    Res = shared_roster_group_parse_query(Host, Group, Query),
-    GroupOpts = mod_shared_roster:get_group_opts(Host, Group),
-    Name = get_opt(GroupOpts, name, ""),
-    Description = get_opt(GroupOpts, description, ""),
-    AllUsers = get_opt(GroupOpts, all_users, false),
-    Disabled = false,
-    DisplayedGroups = get_opt(GroupOpts, displayed_groups, []),
-    Members = mod_shared_roster:get_group_explicit_users(Host, Group),
-    FMembers =
-       if
-           AllUsers ->
-               "@all@\n";
-           true ->
-               []
-       end ++ [[us_to_list(Member), $\n] || Member <- Members],
-    FDisplayedGroups = [[DG, $\n] || DG <- DisplayedGroups],
-    FGroup =
-       ?XAE("table", [],
-            [?XE("tbody",
-                 [?XE("tr",
-                      [?XCT("td", "Name:"),
-                       ?XE("td", [?INPUT("text", "name", Name)])
-                      ]
-                     ),
-                  ?XE("tr",
-                      [?XCT("td", "Description:"),
-                       ?XE("td", [?XAC("textarea", [{"name", "description"},
-                                                    {"rows", "3"},
-                                                    {"cols", "20"}],
-                                       Description)])
-                      ]
-                     ),
-                  ?XE("tr",
-                      [?XCT("td", "Members:"),
-                       ?XE("td", [?XAC("textarea", [{"name", "members"},
-                                                    {"rows", "3"},
-                                                    {"cols", "20"}],
-                                       FMembers)])
-                      ]
-                     ),
-                  ?XE("tr",
-                      [?XCT("td", "Displayed Groups:"),
-                       ?XE("td", [?XAC("textarea", [{"name", "dispgroups"},
-                                                    {"rows", "3"},
-                                                    {"cols", "20"}],
-                                       FDisplayedGroups)])
-                      ]
-                     )]
-                )]),
-    [?XC("h1", ?T("Shared Roster Groups"))] ++
-    [?XC("h2", ?T("Group ") ++ Group)] ++
-       case Res of
-           ok -> [?CT("Submitted"), ?P];
-           error -> [?CT("Bad format"), ?P];
-           nothing -> []
-       end ++
-       [?XAE("form", [{"action", ""}, {"method", "post"}],
-             [FGroup,
-              ?BR,
-              ?INPUTT("submit", "submit", "Submit")
-             ])
-       ].
-
-shared_roster_group_parse_query(Host, Group, Query) ->
-    case lists:keysearch("submit", 1, Query) of
-       {value, _} ->
-           {value, {_, Name}} = lists:keysearch("name", 1, Query),
-           {value, {_, Description}} = lists:keysearch("description", 1, Query),
-           {value, {_, SMembers}} = lists:keysearch("members", 1, Query),
-           {value, {_, SDispGroups}} = lists:keysearch("dispgroups", 1, Query),
-           NameOpt =
-               if
-                   Name == "" -> [];
-                   true -> [{name, Name}]
-               end,
-           DescriptionOpt =
-               if
-                   Description == "" -> [];
-                   true -> [{description, Description}]
-               end,
-           DispGroups = string:tokens(SDispGroups, "\r\n"),
-           DispGroupsOpt =
-               if
-                   DispGroups == [] -> [];
-                   true -> [{displayed_groups, DispGroups}]
-               end,
-
-           OldMembers = mod_shared_roster:get_group_explicit_users(
-                          Host, Group),
-           SJIDs = string:tokens(SMembers, ", \r\n"),
-           NewMembers =
-               lists:foldl(
-                 fun(_SJID, error) -> error;
-                    (SJID, USs) ->
-                         case SJID of
-                             "@all@" ->
-                                 USs;
-                             _ ->
-                                 case jlib:string_to_jid(SJID) of
-                                     JID when is_record(JID, jid) ->
-                                         [{JID#jid.luser, JID#jid.lserver} | USs];
-                                     error ->
-                                         error
-                                 end
-                         end
-                 end, [], SJIDs),
-           AllUsersOpt =
-               case lists:member("@all@", SJIDs) of
-                   true -> [{all_users, true}];
-                   false -> []
-               end,
-
-           mod_shared_roster:set_group_opts(
-             Host, Group,
-             NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt),
-
-           if
-               NewMembers == error -> error;
-               true ->
-                   AddedMembers = NewMembers -- OldMembers,
-                   RemovedMembers = OldMembers -- NewMembers,
-                   lists:foreach(
-                     fun(US) ->
-                             mod_shared_roster:remove_user_from_group(
-                               Host, US, Group)
-                     end, RemovedMembers),
-                   lists:foreach(
-                     fun(US) ->
-                             mod_shared_roster:add_user_to_group(
-                               Host, US, Group)
-                     end, AddedMembers),
-                   ok
-           end;
-       _ ->
-           nothing
-    end.
-
-
-get_opt(Opts, Opt, Default) ->
-    case lists:keysearch(Opt, 1, Opts) of
-       {value, {_, Val}} ->
-           Val;
-       false ->
-           Default
-    end.
-
-
 url_func({user_diapason, From, To}) ->
     integer_to_list(From) ++ "-" ++ integer_to_list(To) ++ "/";
 url_func({users_queue, Prefix, User, Server}) ->
diff --git a/src/web/ejabberd_web_admin.hrl b/src/web/ejabberd_web_admin.hrl
new file mode 100644 (file)
index 0000000..03209d1
--- /dev/null
@@ -0,0 +1,38 @@
+%%%----------------------------------------------------------------------
+%%% File    : ejabberd_web_admin.hrl
+%%% Author  : Alexey Shchepin <alexey@process-one.net>
+%%% Purpose :
+%%% Created : 22 Aug 2007 by Alexey Shchepin <alexey@process-one.net>
+%%%----------------------------------------------------------------------
+
+-define(X(Name), {xmlelement, Name, [], []}).
+-define(XA(Name, Attrs), {xmlelement, Name, Attrs, []}).
+-define(XE(Name, Els), {xmlelement, Name, [], Els}).
+-define(XAE(Name, Attrs, Els), {xmlelement, Name, Attrs, Els}).
+-define(C(Text), {xmlcdata, Text}).
+-define(XC(Name, Text), ?XE(Name, [?C(Text)])).
+-define(XAC(Name, Attrs, Text), ?XAE(Name, Attrs, [?C(Text)])).
+
+-define(T(Text), translate:translate(Lang, Text)).
+-define(CT(Text), ?C(?T(Text))).
+-define(XCT(Name, Text), ?XC(Name, ?T(Text))).
+-define(XACT(Name, Attrs, Text), ?XAC(Name, Attrs, ?T(Text))).
+
+-define(LI(Els), ?XE("li", Els)).
+-define(A(URL, Els), ?XAE("a", [{"href", URL}], Els)).
+-define(AC(URL, Text), ?A(URL, [?C(Text)])).
+-define(ACT(URL, Text), ?AC(URL, ?T(Text))).
+-define(P, ?X("p")).
+-define(BR, ?X("br")).
+-define(INPUT(Type, Name, Value),
+       ?XA("input", [{"type", Type},
+                     {"name", Name},
+                     {"value", Value}])).
+-define(INPUTT(Type, Name, Value), ?INPUT(Type, Name, ?T(Value))).
+-define(INPUTS(Type, Name, Value, Size),
+       ?XA("input", [{"type", Type},
+                     {"name", Name},
+                     {"value", Value},
+                     {"size", Size}])).
+-define(INPUTST(Type, Name, Value, Size), ?INPUT(Type, Name, ?T(Value), Size)).
+-define(ACLINPUT(Text), ?XE("td", [?INPUT("text", "value" ++ ID, Text)])).