mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-20 17:27:00 +01:00
WebAdmin: Move content to commands; new pages; hook changes; new commands
Also: - Added support to view user subpages in the menu - Webadmin hooks now get the full request - New commands added to be used in webadmin pages
This commit is contained in:
parent
2b1d4ff98d
commit
5a34020d23
@ -32,11 +32,16 @@
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
%% WebAdmin
|
||||
-export([webadmin_menu_node/3, webadmin_page_node/3]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("ejabberd_web_admin.hrl").
|
||||
-include_lib("public_key/include/public_key.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
|
||||
-define(CALL_TIMEOUT, timer:minutes(10)).
|
||||
|
||||
@ -108,6 +113,8 @@ init([]) ->
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, register_certfiles, 40),
|
||||
ejabberd_hooks:add(ejabberd_started, ?MODULE, ejabberd_started, 110),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, ejabberd_started, 110),
|
||||
ejabberd_hooks:add(webadmin_menu_node, ?MODULE, webadmin_menu_node, 110),
|
||||
ejabberd_hooks:add(webadmin_page_node, ?MODULE, webadmin_page_node, 110),
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
register_certfiles(),
|
||||
{ok, #state{}}.
|
||||
@ -153,6 +160,8 @@ terminate(_Reason, _State) ->
|
||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, register_certfiles, 40),
|
||||
ejabberd_hooks:delete(ejabberd_started, ?MODULE, ejabberd_started, 110),
|
||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, ejabberd_started, 110),
|
||||
ejabberd_hooks:delete(webadmin_menu_node, ?MODULE, webadmin_menu_node, 110),
|
||||
ejabberd_hooks:delete(webadmin_page_node, ?MODULE, webadmin_page_node, 110),
|
||||
ejabberd_commands:unregister_commands(get_commands_spec()).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
@ -547,6 +556,21 @@ list_certificates() ->
|
||||
{Domain, Path, sets:is_element(E, Used)}
|
||||
end, Known)).
|
||||
|
||||
%%%===================================================================
|
||||
%%% WebAdmin
|
||||
%%%===================================================================
|
||||
|
||||
webadmin_menu_node(Acc, _Node, _Lang) ->
|
||||
Acc ++ [{<<"acme">>, <<"ACME">>}].
|
||||
|
||||
webadmin_page_node(_, Node, #request{path = [<<"acme">>]} = R) ->
|
||||
Head = ?H1GLraw(<<"ACME Certificates">>, <<"admin/configuration/basic/#acme">>, <<"ACME">>),
|
||||
Set = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [request_certificate, R]),
|
||||
ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [revoke_certificate, R])],
|
||||
Get = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [list_certificates, R])],
|
||||
{stop, Head ++ Get ++ Set};
|
||||
webadmin_page_node(Acc, _, _) -> Acc.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Other stuff
|
||||
%%%===================================================================
|
||||
|
@ -39,7 +39,9 @@
|
||||
dump_config/1,
|
||||
convert_to_yaml/2,
|
||||
%% Cluster
|
||||
join_cluster/1, leave_cluster/1, list_cluster/0,
|
||||
join_cluster/1, leave_cluster/1,
|
||||
list_cluster/0, list_cluster_detailed/0,
|
||||
get_cluster_node_details3/0,
|
||||
%% Erlang
|
||||
update_list/0, update/1, update/0,
|
||||
%% Accounts
|
||||
@ -50,7 +52,7 @@
|
||||
%% Purge DB
|
||||
delete_expired_messages/0, delete_old_messages/1,
|
||||
%% Mnesia
|
||||
set_master/1,
|
||||
get_master/0, set_master/1,
|
||||
backup_mnesia/1, restore_mnesia/1,
|
||||
dump_mnesia/1, dump_table/2, load_mnesia/1,
|
||||
mnesia_info/0, mnesia_table_info/1,
|
||||
@ -61,13 +63,27 @@
|
||||
clear_cache/0,
|
||||
gc/0,
|
||||
get_commands_spec/0,
|
||||
delete_old_messages_batch/4, delete_old_messages_status/1, delete_old_messages_abort/1]).
|
||||
delete_old_messages_batch/4, delete_old_messages_status/1, delete_old_messages_abort/1,
|
||||
%% Internal
|
||||
mnesia_list_tables/0,
|
||||
mnesia_table_details/1,
|
||||
mnesia_table_change_storage/2,
|
||||
mnesia_table_clear/1,
|
||||
mnesia_table_delete/1,
|
||||
echo/1, echo3/3]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-export([web_menu_main/2, web_page_main/2,
|
||||
web_menu_node/3, web_page_node/3]).
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("ejabberd_web_admin.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("translate.hrl"). %+++ TODO
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
@ -77,6 +93,10 @@ start_link() ->
|
||||
init([]) ->
|
||||
process_flag(trap_exit, true),
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
ejabberd_hooks:add(webadmin_menu_main, ?MODULE, web_menu_main, 50),
|
||||
ejabberd_hooks:add(webadmin_page_main, ?MODULE, web_page_main, 50),
|
||||
ejabberd_hooks:add(webadmin_menu_node, ?MODULE, web_menu_node, 50),
|
||||
ejabberd_hooks:add(webadmin_page_node, ?MODULE, web_page_node, 50),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(Request, From, State) ->
|
||||
@ -92,6 +112,10 @@ handle_info(Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ejabberd_hooks:delete(webadmin_menu_main, ?MODULE, web_menu_main, 50),
|
||||
ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50),
|
||||
ejabberd_hooks:delete(webadmin_menu_node, ?MODULE, web_menu_node, 50),
|
||||
ejabberd_hooks:delete(webadmin_page_node, ?MODULE, web_page_node, 50),
|
||||
ejabberd_commands:unregister_commands(get_commands_spec()).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
@ -179,8 +203,9 @@ get_commands_spec() ->
|
||||
desc = "Update the given module",
|
||||
longdesc = "To update all the possible modules, use `all`.",
|
||||
module = ?MODULE, function = update,
|
||||
args_example = ["mod_vcard"],
|
||||
args_example = ["all"],
|
||||
args = [{module, string}],
|
||||
result_example = {ok, <<"Updated modules: mod_configure, mod_vcard">>},
|
||||
result = {res, restuple}},
|
||||
|
||||
#ejabberd_commands{name = register, tags = [accounts],
|
||||
@ -225,14 +250,12 @@ get_commands_spec() ->
|
||||
|
||||
#ejabberd_commands{name = join_cluster, tags = [cluster],
|
||||
desc = "Join this node into the cluster handled by Node",
|
||||
longdesc = "This command works only with ejabberdctl, "
|
||||
"not mod_http_api or other code that runs inside the "
|
||||
"same ejabberd node that will be joined.",
|
||||
note = "improved in 24.xx",
|
||||
module = ?MODULE, function = join_cluster,
|
||||
args_desc = ["Nodename of the node to join"],
|
||||
args_example = [<<"ejabberd1@machine7">>],
|
||||
args = [{node, binary}],
|
||||
result = {res, rescode}},
|
||||
result = {res, restuple}},
|
||||
#ejabberd_commands{name = leave_cluster, tags = [cluster],
|
||||
desc = "Remove and shutdown Node from the running cluster",
|
||||
longdesc = "This command can be run from any running "
|
||||
@ -247,11 +270,27 @@ get_commands_spec() ->
|
||||
result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = list_cluster, tags = [cluster],
|
||||
desc = "List nodes that are part of the cluster handled by Node",
|
||||
desc = "List running nodes that are part of this cluster",
|
||||
module = ?MODULE, function = list_cluster,
|
||||
result_example = [ejabberd1@machine7, ejabberd1@machine8],
|
||||
args = [],
|
||||
result = {nodes, {list, {node, atom}}}},
|
||||
#ejabberd_commands{name = list_cluster_detailed, tags = [cluster],
|
||||
desc = "List nodes (both running and known) and some stats",
|
||||
note = "added in 24.xx",
|
||||
module = ?MODULE, function = list_cluster_detailed,
|
||||
args = [],
|
||||
result_example = [{'ejabberd@localhost', "true",
|
||||
"The node ejabberd is started. Status...",
|
||||
7, 348, 60, none}],
|
||||
result = {nodes, {list, {node, {tuple, [{name, atom},
|
||||
{running, string},
|
||||
{status, string},
|
||||
{online_users, integer},
|
||||
{processes, integer},
|
||||
{uptime_seconds, integer},
|
||||
{master_node, atom}
|
||||
]}}}}},
|
||||
|
||||
#ejabberd_commands{name = import_file, tags = [mnesia],
|
||||
desc = "Import user data from jabberd14 spool file",
|
||||
@ -377,6 +416,12 @@ get_commands_spec() ->
|
||||
args_example = ["example.com", "/var/lib/ejabberd/example.com.sql"],
|
||||
args = [{host, string}, {file, string}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = get_master, tags = [cluster],
|
||||
desc = "Get master node of the clustered Mnesia tables",
|
||||
note = "added in 24.xx",
|
||||
longdesc = "If there is no master, returns `none`.",
|
||||
module = ?MODULE, function = get_master,
|
||||
result = {nodename, atom}},
|
||||
#ejabberd_commands{name = set_master, tags = [cluster],
|
||||
desc = "Set master node of the clustered Mnesia tables",
|
||||
longdesc = "If `nodename` is set to `self`, then this "
|
||||
@ -472,9 +517,100 @@ get_commands_spec() ->
|
||||
desc = "Generate Unix manpage for current ejabberd version",
|
||||
note = "added in 20.01",
|
||||
module = ejabberd_doc, function = man,
|
||||
args = [], result = {res, restuple}}
|
||||
].
|
||||
args = [], result = {res, restuple}},
|
||||
|
||||
#ejabberd_commands{name = webadmin_host_user_queue, tags = [internal],
|
||||
desc = "Generate WebAdmin offline queue HTML",
|
||||
module = mod_offline, function = webadmin_host_user_queue,
|
||||
args = [{user, binary}, {host, binary}, {query, any}, {lang, binary}],
|
||||
result = {res, any}},
|
||||
|
||||
#ejabberd_commands{name = webadmin_host_last_activity, tags = [internal],
|
||||
desc = "Generate WebAdmin Last Activity HTML",
|
||||
module = ejabberd_web_admin, function = webadmin_host_last_activity,
|
||||
args = [{host, binary}, {query, any}, {lang, binary}],
|
||||
result = {res, any}},
|
||||
#ejabberd_commands{name = webadmin_host_srg, tags = [internal],
|
||||
desc = "Generate WebAdmin Shared Roster Group HTML",
|
||||
module = mod_shared_roster, function = webadmin_host_srg,
|
||||
args = [{host, binary}, {query, any}, {lang, binary}],
|
||||
result = {res, any}},
|
||||
#ejabberd_commands{name = webadmin_host_srg_group, tags = [internal],
|
||||
desc = "Generate WebAdmin Shared Roster Group HTML for a group",
|
||||
module = mod_shared_roster, function = webadmin_host_srg_group,
|
||||
args = [{host, binary}, {group, binary}, {query, any}, {lang, binary}],
|
||||
result = {res, any}},
|
||||
|
||||
#ejabberd_commands{name = webadmin_node_contrib, tags = [internal],
|
||||
desc = "Generate WebAdmin ejabberd-contrib HTML",
|
||||
module = ext_mod, function = webadmin_node_contrib,
|
||||
args = [{node, atom}, {query, any}, {lang, binary}],
|
||||
result = {res, any}},
|
||||
#ejabberd_commands{name = webadmin_node_db, tags = [internal],
|
||||
desc = "Generate WebAdmin Mnesia database HTML",
|
||||
module = ejabberd_web_admin, function = webadmin_node_db,
|
||||
args = [{node, atom}, {query, any}, {lang, binary}],
|
||||
result = {res, any}},
|
||||
#ejabberd_commands{name = webadmin_node_db_table, tags = [internal],
|
||||
desc = "Generate WebAdmin Mnesia database HTML for a table",
|
||||
module = ejabberd_web_admin, function = webadmin_node_db_table,
|
||||
args = [{node, atom}, {table, binary}, {lang, binary}],
|
||||
result = {res, any}},
|
||||
#ejabberd_commands{name = webadmin_node_db_table_page, tags = [internal],
|
||||
desc = "Generate WebAdmin Mnesia database HTML for a table content",
|
||||
module = ejabberd_web_admin, function = webadmin_node_db_table_page,
|
||||
args = [{node, atom}, {table, binary}, {page, integer}],
|
||||
result = {res, any}},
|
||||
|
||||
#ejabberd_commands{name = mnesia_list_tables, tags = [internal, mnesia],
|
||||
desc = "List of Mnesia tables",
|
||||
module = ?MODULE, function = mnesia_list_tables,
|
||||
result = {tables, {list, {table, {tuple, [{name, atom},
|
||||
{storage_type, binary},
|
||||
{elements, integer},
|
||||
{memory_kb, integer},
|
||||
{memory_mb, integer}
|
||||
]}}}}},
|
||||
#ejabberd_commands{name = mnesia_table_details, tags = [internal, mnesia],
|
||||
desc = "Get details of a Mnesia table",
|
||||
module = ?MODULE, function = mnesia_table_details,
|
||||
args = [{table, binary}],
|
||||
result = {details, {list, {detail, {tuple, [{name, atom},
|
||||
{value, binary}
|
||||
]}}}}},
|
||||
|
||||
#ejabberd_commands{name = mnesia_table_change_storage, tags = [internal, mnesia],
|
||||
desc = "Change storage type of a Mnesia table to: ram_copies, disc_copies, or disc_only_copies.",
|
||||
module = ?MODULE, function = mnesia_table_change_storage,
|
||||
args = [{table, binary}, {storage_type, binary}],
|
||||
result = {res, restuple}},
|
||||
#ejabberd_commands{name = mnesia_table_clear, tags = [internal, mnesia],
|
||||
desc = "Delete all content in a Mnesia table",
|
||||
module = ?MODULE, function = mnesia_table_clear,
|
||||
args = [{table, binary}],
|
||||
result = {res, restuple}},
|
||||
#ejabberd_commands{name = mnesia_table_destroy, tags = [internal, mnesia],
|
||||
desc = "Destroy a Mnesia table",
|
||||
module = ?MODULE, function = mnesia_table_destroy,
|
||||
args = [{table, binary}],
|
||||
result = {res, restuple}},
|
||||
#ejabberd_commands{name = echo, tags = [internal],
|
||||
desc = "Return the same sentence that was provided",
|
||||
module = ?MODULE, function = echo,
|
||||
args_desc = ["Sentence to echoe"],
|
||||
args_example = [<<"Test Sentence">>],
|
||||
args = [{sentence, binary}],
|
||||
result = {sentence, string},
|
||||
result_example = "Test Sentence"},
|
||||
#ejabberd_commands{name = echo3, tags = [internal],
|
||||
desc = "Return the same sentence that was provided",
|
||||
module = ?MODULE, function = echo3,
|
||||
args_desc = ["First argument", "Second argument", "Sentence to echoe"],
|
||||
args_example = [<<"example.com">>, <<"Group1">>, <<"Test Sentence">>],
|
||||
args = [{first, binary}, {second, binary}, {sentence, binary}],
|
||||
result = {sentence, string},
|
||||
result_example = "Test Sentence"}
|
||||
].
|
||||
|
||||
%%%
|
||||
%%% Server management
|
||||
@ -491,7 +627,7 @@ status() ->
|
||||
{value, {_, _, Version}} ->
|
||||
{ok, io_lib:format("ejabberd ~s is running in that node", [Version])}
|
||||
end,
|
||||
{Is_running, String1 ++ String2}.
|
||||
{Is_running, String1 ++ " " ++String2}.
|
||||
|
||||
stop() ->
|
||||
_ = supervisor:terminate_child(ejabberd_sup, ejabberd_sm),
|
||||
@ -583,8 +719,14 @@ update_list() ->
|
||||
[atom_to_list(Beam) || Beam <- UpdatedBeams].
|
||||
|
||||
update("all") ->
|
||||
[update_module(ModStr) || ModStr <- update_list()],
|
||||
{ok, []};
|
||||
ResList = [{ModStr, update_module(ModStr)} || ModStr <- update_list()],
|
||||
String = case string:join([Mod || {Mod, {ok, _}} <- ResList], ", ") of
|
||||
[] ->
|
||||
"No modules updated";
|
||||
ModulesString ->
|
||||
"Updated modules: " ++ ModulesString
|
||||
end,
|
||||
{ok, String};
|
||||
update(ModStr) ->
|
||||
update_module(ModStr).
|
||||
|
||||
@ -593,7 +735,10 @@ update_module(ModuleNameBin) when is_binary(ModuleNameBin) ->
|
||||
update_module(ModuleNameString) ->
|
||||
ModuleName = list_to_atom(ModuleNameString),
|
||||
case ejabberd_update:update([ModuleName]) of
|
||||
{ok, _Res} -> {ok, []};
|
||||
{ok, []} ->
|
||||
{ok, "Not updated: "++ModuleNameString};
|
||||
{ok, [{ModuleName, _}]} ->
|
||||
{ok, "Updated: "++ModuleNameString};
|
||||
{error, Reason} -> {error, Reason}
|
||||
end.
|
||||
|
||||
@ -681,7 +826,25 @@ convert_to_yaml(In, Out) ->
|
||||
%%%
|
||||
|
||||
join_cluster(NodeBin) ->
|
||||
ejabberd_cluster:join(list_to_atom(binary_to_list(NodeBin))).
|
||||
Node = list_to_atom(binary_to_list(NodeBin)),
|
||||
IsNodes = lists:member(Node, ejabberd_cluster:get_nodes()),
|
||||
IsKnownNodes = lists:member(Node, ejabberd_cluster:get_known_nodes()),
|
||||
Ping = net_adm:ping(Node),
|
||||
join_cluster(Node, IsNodes, IsKnownNodes, Ping).
|
||||
|
||||
join_cluster(_Node, true, _IsKnownNodes, _Ping) ->
|
||||
{error, "This node already joined that running node."};
|
||||
join_cluster(_Node, _IsNodes, true, _Ping) ->
|
||||
{error, "This node already joined that known node."};
|
||||
join_cluster(_Node, _IsNodes, _IsKnownNodes, pang) ->
|
||||
{error, "This node cannot reach that node."};
|
||||
join_cluster(Node, false, false, pong) ->
|
||||
case timer:apply_after(1000, ejabberd_cluster, join, [Node]) of
|
||||
{ok, _} ->
|
||||
{ok, "Trying to join the cluster, wait a few seconds and check the list of nodes."};
|
||||
Error ->
|
||||
{error, io_lib:format("Can't join cluster: ~p", [Error])}
|
||||
end.
|
||||
|
||||
leave_cluster(NodeBin) ->
|
||||
ejabberd_cluster:leave(list_to_atom(binary_to_list(NodeBin))).
|
||||
@ -689,6 +852,33 @@ leave_cluster(NodeBin) ->
|
||||
list_cluster() ->
|
||||
ejabberd_cluster:get_nodes().
|
||||
|
||||
list_cluster_detailed() ->
|
||||
KnownNodes = ejabberd_cluster:get_known_nodes(),
|
||||
RunningNodes = ejabberd_cluster:get_nodes(),
|
||||
[get_cluster_node_details(Node, RunningNodes) || Node <- KnownNodes].
|
||||
|
||||
get_cluster_node_details(Node, RunningNodes) ->
|
||||
get_cluster_node_details2(Node, lists:member(Node, RunningNodes)).
|
||||
|
||||
get_cluster_node_details2(Node, false) ->
|
||||
{Node, "false", "", -1, -1, -1, "unknown"};
|
||||
get_cluster_node_details2(Node, true) ->
|
||||
try ejabberd_cluster:call(Node, ejabberd_admin, get_cluster_node_details3, []) of
|
||||
Result -> Result
|
||||
catch
|
||||
E:R ->
|
||||
Status = io_lib:format("~p: ~p", [E, R]),
|
||||
{Node, "true", Status, -1, -1, -1, "unknown"}
|
||||
end.
|
||||
|
||||
get_cluster_node_details3() ->
|
||||
{ok, StatusString} = status(),
|
||||
UptimeSeconds = mod_admin_extra:stats(<<"uptimeseconds">>),
|
||||
Processes = mod_admin_extra:stats(<<"processes">>),
|
||||
OnlineUsers = mod_admin_extra:stats(<<"onlineusersnode">>),
|
||||
GetMaster = get_master(),
|
||||
{node(), "true", StatusString, OnlineUsers, Processes, UptimeSeconds, GetMaster}.
|
||||
|
||||
%%%
|
||||
%%% Migration management
|
||||
%%%
|
||||
@ -791,6 +981,12 @@ delete_old_messages_abort(Server) ->
|
||||
%%% Mnesia management
|
||||
%%%
|
||||
|
||||
get_master() ->
|
||||
case mnesia:table_info(session, master_nodes) of
|
||||
[] -> none;
|
||||
[Node] -> Node
|
||||
end.
|
||||
|
||||
set_master("self") ->
|
||||
set_master(node());
|
||||
set_master(NodeString) when is_list(NodeString) ->
|
||||
@ -798,7 +994,7 @@ set_master(NodeString) when is_list(NodeString) ->
|
||||
set_master(Node) when is_atom(Node) ->
|
||||
case mnesia:set_master_nodes([Node]) of
|
||||
ok ->
|
||||
{ok, ""};
|
||||
{ok, "ok"};
|
||||
{error, Reason} ->
|
||||
String = io_lib:format("Can't set master node ~p at node ~p:~n~p",
|
||||
[Node, node(), Reason]),
|
||||
@ -1008,3 +1204,222 @@ is_my_host(Host) ->
|
||||
try ejabberd_router:is_my_host(Host)
|
||||
catch _:{invalid_domain, _} -> false
|
||||
end.
|
||||
|
||||
%%%
|
||||
%%% Internal
|
||||
%%%
|
||||
|
||||
%% @format-begin
|
||||
|
||||
%% mnesia:del_table_copy(Table, Node);
|
||||
%% mnesia:change_table_copy_type(Table, Node, Type);
|
||||
|
||||
mnesia_table_change_storage(STable, SType) ->
|
||||
Table = binary_to_existing_atom(STable, latin1),
|
||||
Type =
|
||||
case SType of
|
||||
<<"ram_copies">> ->
|
||||
ram_copies;
|
||||
<<"disc_copies">> ->
|
||||
disc_copies;
|
||||
<<"disc_only_copies">> ->
|
||||
disc_only_copies;
|
||||
_ ->
|
||||
false
|
||||
end,
|
||||
mnesia:add_table_copy(Table, node(), Type).
|
||||
|
||||
mnesia_table_clear(STable) ->
|
||||
Table = binary_to_existing_atom(STable, latin1),
|
||||
mnesia:clear_table(Table).
|
||||
|
||||
mnesia_table_delete(STable) ->
|
||||
Table = binary_to_existing_atom(STable, latin1),
|
||||
mnesia:delete_table(Table).
|
||||
|
||||
mnesia_table_details(STable) ->
|
||||
Table = binary_to_existing_atom(STable, latin1),
|
||||
[{Name, iolist_to_binary(str:format("~p", [Value]))}
|
||||
|| {Name, Value} <- mnesia:table_info(Table, all)].
|
||||
|
||||
mnesia_list_tables() ->
|
||||
STables =
|
||||
lists:sort(
|
||||
mnesia:system_info(tables)),
|
||||
lists:map(fun(Table) ->
|
||||
TInfo = mnesia:table_info(Table, all),
|
||||
{value, {storage_type, Type}} = lists:keysearch(storage_type, 1, TInfo),
|
||||
{value, {size, Size}} = lists:keysearch(size, 1, TInfo),
|
||||
{value, {memory, Memory}} = lists:keysearch(memory, 1, TInfo),
|
||||
MemoryB = Memory * erlang:system_info(wordsize),
|
||||
MemoryKB = MemoryB div 1024,
|
||||
MemoryMB = MemoryKB div 1024,
|
||||
{Table, storage_type_bin(Type), Size, MemoryKB, MemoryMB}
|
||||
end,
|
||||
STables).
|
||||
|
||||
storage_type_bin(ram_copies) ->
|
||||
<<"RAM copy">>;
|
||||
storage_type_bin(disc_copies) ->
|
||||
<<"RAM and disc copy">>;
|
||||
storage_type_bin(disc_only_copies) ->
|
||||
<<"Disc only copy">>;
|
||||
storage_type_bin(unknown) ->
|
||||
<<"Remote copy">>.
|
||||
|
||||
echo(Sentence) ->
|
||||
Sentence.
|
||||
|
||||
echo3(_, _, Sentence) ->
|
||||
Sentence.
|
||||
|
||||
%%%
|
||||
%%% Web Admin: Main
|
||||
%%%
|
||||
|
||||
web_menu_main(Acc, _Lang) ->
|
||||
Acc ++ [{<<"purge">>, <<"Purge">>}, {<<"stanza">>, <<"Stanza">>}].
|
||||
|
||||
web_page_main(_, #request{path = [<<"purge">>]} = R) ->
|
||||
Types =
|
||||
[{<<"#erlang">>, <<"Erlang">>},
|
||||
{<<"#users">>, <<"Users">>},
|
||||
{<<"#offline">>, <<"Offline">>},
|
||||
{<<"#mam">>, <<"MAM">>},
|
||||
{<<"#pubsub">>, <<"PubSub">>},
|
||||
{<<"#push">>, <<"Push">>}],
|
||||
Head = [?XC(<<"h1">>, <<"Purge">>)],
|
||||
Set = [?XE(<<"ul">>, [?LI([?AC(MIU, MIN)]) || {MIU, MIN} <- Types]),
|
||||
?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"erlang">>}], <<"Erlang">>),
|
||||
?XE(<<"blockquote">>,
|
||||
[ejabberd_web_admin:make_command(clear_cache, R),
|
||||
ejabberd_web_admin:make_command(gc, R)]),
|
||||
?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"users">>}], <<"Users">>),
|
||||
?XE(<<"blockquote">>, [ejabberd_web_admin:make_command(delete_old_users, R)]),
|
||||
?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"offline">>}], <<"Offline">>),
|
||||
?XE(<<"blockquote">>,
|
||||
[ejabberd_web_admin:make_command(delete_expired_messages, R),
|
||||
ejabberd_web_admin:make_command(delete_old_messages, R),
|
||||
ejabberd_web_admin:make_command(delete_old_messages_batch, R),
|
||||
ejabberd_web_admin:make_command(delete_old_messages_status, R)]),
|
||||
?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"mam">>}], <<"MAM">>),
|
||||
?XE(<<"blockquote">>,
|
||||
[ejabberd_web_admin:make_command(delete_old_mam_messages, R),
|
||||
ejabberd_web_admin:make_command(delete_old_mam_messages_batch, R),
|
||||
ejabberd_web_admin:make_command(delete_old_mam_messages_status, R)]),
|
||||
?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"pubsub">>}], <<"PubSub">>),
|
||||
?XE(<<"blockquote">>,
|
||||
[ejabberd_web_admin:make_command(delete_expired_pubsub_items, R),
|
||||
ejabberd_web_admin:make_command(delete_old_pubsub_items, R)]),
|
||||
?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"push">>}], <<"Push">>),
|
||||
?XE(<<"blockquote">>, [ejabberd_web_admin:make_command(delete_old_push_sessions, R)])],
|
||||
{stop, Head ++ Set};
|
||||
web_page_main(_, #request{path = [<<"stanza">>]} = R) ->
|
||||
Head = [?XC(<<"h1">>, <<"Stanza">>)],
|
||||
Set = [ejabberd_web_admin:make_command(send_message, R),
|
||||
ejabberd_web_admin:make_command(send_stanza, R),
|
||||
ejabberd_web_admin:make_command(send_stanza_c2s, R)],
|
||||
{stop, Head ++ Set};
|
||||
web_page_main(Acc, _) ->
|
||||
Acc.
|
||||
|
||||
%%%
|
||||
%%% Web Admin: Node
|
||||
%%%
|
||||
|
||||
web_menu_node(Acc, _Node, _Lang) ->
|
||||
Acc
|
||||
++ [{<<"cluster">>, <<"Clustering">>},
|
||||
{<<"update">>, <<"Code Update">>},
|
||||
{<<"config-file">>, <<"Configuration File">>},
|
||||
{<<"logs">>, <<"Logs">>},
|
||||
{<<"stop">>, <<"Stop Node">>}].
|
||||
|
||||
web_page_node(_, Node, #request{path = [<<"cluster">>]} = R) ->
|
||||
{ok, Names} = net_adm:names(),
|
||||
NodeNames = lists:join(", ", [Name || {Name, _Port} <- Names]),
|
||||
Hint =
|
||||
list_to_binary(io_lib:format("Hint: Erlang nodes found in this machine that may be running ejabberd: ~s",
|
||||
[NodeNames])),
|
||||
Head = ?H1GLraw(<<"Clustering">>, <<"admin/guide/clustering/">>, <<"Clustering">>),
|
||||
Set1 =
|
||||
[ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[join_cluster, R, [], [{style, danger}]]),
|
||||
?XE(<<"blockquote">>, [?C(Hint)]),
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[leave_cluster, R, [], [{style, danger}]])],
|
||||
Set2 =
|
||||
[ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[set_master, R, [], [{style, danger}]])],
|
||||
timer:sleep(100), % leaving a cluster takes a while, let's delay the get commands
|
||||
Get1 =
|
||||
[ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[list_cluster_detailed,
|
||||
R,
|
||||
[],
|
||||
[{result_links, [{name, node, 3, <<"">>}]}]])],
|
||||
Get2 =
|
||||
[ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[get_master,
|
||||
R,
|
||||
[],
|
||||
[{result_named, true},
|
||||
{result_links, [{nodename, node, 3, <<"">>}]}]])],
|
||||
{stop, Head ++ Get1 ++ Set1 ++ Get2 ++ Set2};
|
||||
web_page_node(_, Node, #request{path = [<<"update">>]} = R) ->
|
||||
Head = [?XC(<<"h1">>, <<"Code Update">>)],
|
||||
Set = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [update, R])],
|
||||
Get = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [update_list, R])],
|
||||
{stop, Head ++ Get ++ Set};
|
||||
web_page_node(_, Node, #request{path = [<<"config-file">>]} = R) ->
|
||||
Res = ?H1GLraw(<<"Configuration File">>,
|
||||
<<"admin/configuration/file-format/">>,
|
||||
<<"File Format">>)
|
||||
++ [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [convert_to_yaml, R]),
|
||||
ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [dump_config, R]),
|
||||
ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [reload_config, R])],
|
||||
{stop, Res};
|
||||
web_page_node(_, Node, #request{path = [<<"stop">>]} = R) ->
|
||||
Res = [?XC(<<"h1">>, <<"Stop This Node">>),
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[restart, R, [], [{style, danger}]]),
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[stop_kindly, R, [], [{style, danger}]]),
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[stop, R, [], [{style, danger}]]),
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[halt, R, [], [{style, danger}]])],
|
||||
{stop, Res};
|
||||
web_page_node(_, Node, #request{path = [<<"logs">>]} = R) ->
|
||||
Res = ?H1GLraw(<<"Logs">>, <<"admin/configuration/basic/#logging">>, <<"Logging">>)
|
||||
++ [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [set_loglevel, R]),
|
||||
ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [get_loglevel, R]),
|
||||
ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [reopen_log, R]),
|
||||
ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [rotate_log, R])],
|
||||
{stop, Res};
|
||||
web_page_node(Acc, _, _) ->
|
||||
Acc.
|
||||
|
@ -54,6 +54,8 @@
|
||||
oauth_add_client_implicit/3,
|
||||
oauth_remove_client/1]).
|
||||
|
||||
-export([web_menu_main/2, web_page_main/2]).
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
@ -230,6 +232,8 @@ init([]) ->
|
||||
application:set_env(oauth2, expiry_time, Expire div 1000),
|
||||
application:start(oauth2),
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
ejabberd_hooks:add(webadmin_menu_main, ?MODULE, web_menu_main, 50),
|
||||
ejabberd_hooks:add(webadmin_page_main, ?MODULE, web_page_main, 50),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
|
||||
erlang:send_after(expire(), self(), clean),
|
||||
{ok, ok}.
|
||||
@ -255,6 +259,8 @@ handle_info(Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ejabberd_hooks:delete(webadmin_menu_main, ?MODULE, web_menu_main, 50),
|
||||
ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50),
|
||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
@ -794,3 +800,30 @@ logo() ->
|
||||
{error, _} ->
|
||||
<<>>
|
||||
end.
|
||||
|
||||
%%%
|
||||
%%% WebAdmin
|
||||
%%%
|
||||
|
||||
%% @format-begin
|
||||
|
||||
web_menu_main(Acc, _Lang) ->
|
||||
Acc ++ [{<<"oauth">>, <<"OAuth">>}].
|
||||
|
||||
web_page_main(_, #request{path = [<<"oauth">>]} = R) ->
|
||||
Head = ?H1GLraw(<<"OAuth">>, <<"developer/ejabberd-api/oauth/">>, <<"OAuth">>),
|
||||
Set = [?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"token">>}], <<"Token">>),
|
||||
?XE(<<"blockquote">>,
|
||||
[ejabberd_web_admin:make_command(oauth_list_tokens, R),
|
||||
ejabberd_web_admin:make_command(oauth_issue_token, R),
|
||||
ejabberd_web_admin:make_command(oauth_revoke_token, R)]),
|
||||
?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"client">>}], <<"Client">>),
|
||||
?XE(<<"blockquote">>,
|
||||
[ejabberd_web_admin:make_command(oauth_add_client_implicit, R),
|
||||
ejabberd_web_admin:make_command(oauth_add_client_password, R),
|
||||
ejabberd_web_admin:make_command(oauth_remove_client, R)])],
|
||||
{stop, Head ++ Set};
|
||||
web_page_main(Acc, _) ->
|
||||
Acc.
|
||||
|
File diff suppressed because it is too large
Load Diff
163
src/ext_mod.erl
163
src/ext_mod.erl
@ -37,13 +37,14 @@
|
||||
config_dir/0, get_commands_spec/0]).
|
||||
-export([modules_configs/0, module_ebin_dir/1]).
|
||||
-export([compile_erlang_file/2, compile_elixir_file/2]).
|
||||
-export([web_menu_node/3, web_page_node/5, get_page/3]).
|
||||
-export([web_menu_node/3, web_page_node/3, webadmin_node_contrib/3]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("ejabberd_web_admin.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("translate.hrl").
|
||||
@ -924,26 +925,156 @@ parse_details(Body) ->
|
||||
)
|
||||
).
|
||||
|
||||
web_menu_node(Acc, _Node, Lang) ->
|
||||
Acc ++ [{<<"contrib">>, translate:translate(Lang, ?T("Contrib Modules"))}].
|
||||
%% @format-begin
|
||||
|
||||
web_page_node(_, Node, [<<"contrib">>], Query, Lang) ->
|
||||
Res = rpc:call(Node, ?MODULE, get_page, [Node, Query, Lang]),
|
||||
{stop, Res};
|
||||
web_page_node(Acc, _, _, _, _) ->
|
||||
Acc.
|
||||
web_menu_node(Acc, _Node, _Lang) ->
|
||||
Acc
|
||||
++ [{<<"contrib">>, <<"Contrib Modules (Detailed)">>},
|
||||
{<<"contrib-api">>, <<"Contrib Modules (API)">>}].
|
||||
|
||||
get_page(Node, Query, Lang) ->
|
||||
QueryRes = list_modules_parse_query(Query),
|
||||
Title = ?H1GL(translate:translate(Lang, ?T("Contrib Modules")),
|
||||
web_page_node(_,
|
||||
Node,
|
||||
#request{path = [<<"contrib">>],
|
||||
q = Query,
|
||||
lang = Lang} =
|
||||
R) ->
|
||||
Title =
|
||||
?H1GL(<<"Contrib Modules (Detailed)">>,
|
||||
<<"../../developer/extending-ejabberd/modules/#ejabberd-contrib">>,
|
||||
<<"ejabberd-contrib">>),
|
||||
Contents = get_content(Node, Query, Lang),
|
||||
Result = case QueryRes of
|
||||
ok -> [?XREST(?T("Submitted"))];
|
||||
nothing -> []
|
||||
Res = [ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[webadmin_node_contrib,
|
||||
R,
|
||||
[{<<"node">>, Node}, {<<"query">>, Query}, {<<"lang">>, Lang}],
|
||||
[]])],
|
||||
{stop, Title ++ Res};
|
||||
web_page_node(_, Node, #request{path = [<<"contrib-api">> | RPath]} = R) ->
|
||||
Title =
|
||||
?H1GL(<<"Contrib Modules (API)">>,
|
||||
<<"../../developer/extending-ejabberd/modules/#ejabberd-contrib">>,
|
||||
<<"ejabberd-contrib">>),
|
||||
_TableInstalled = make_table_installed(Node, R, RPath),
|
||||
_TableAvailable = make_table_available(Node, R, RPath),
|
||||
TableInstalled = make_table_installed(Node, R, RPath),
|
||||
TableAvailable = make_table_available(Node, R, RPath),
|
||||
Res = [?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"specs">>}], <<"Specs">>),
|
||||
?XE(<<"blockquote">>,
|
||||
[ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[modules_update_specs, R])]),
|
||||
?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"installed">>}], <<"Installed">>),
|
||||
?XE(<<"blockquote">>,
|
||||
[ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[modules_installed, R, [], [{only, presentation}]]),
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[module_uninstall, R, [], [{only, presentation}]]),
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[module_upgrade, R, [], [{only, presentation}]]),
|
||||
TableInstalled]),
|
||||
?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"available">>}], <<"Available">>),
|
||||
?XE(<<"blockquote">>,
|
||||
[ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[modules_available, R, [], [{only, presentation}]]),
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[module_install, R, [], [{only, presentation}]]),
|
||||
TableAvailable,
|
||||
ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [module_check, R])])],
|
||||
{stop, Title ++ Res};
|
||||
web_page_node(Acc, _, _) ->
|
||||
Acc.
|
||||
|
||||
make_table_installed(Node, R, RPath) ->
|
||||
Columns = [<<"Name">>, <<"Summary">>, <<"">>, <<"">>],
|
||||
ModulesInstalled =
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command_raw_value,
|
||||
[modules_installed, R, []]),
|
||||
Rows =
|
||||
lists:map(fun({Name, Summary}) ->
|
||||
NameBin = misc:atom_to_binary(Name),
|
||||
Upgrade =
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[module_upgrade,
|
||||
R,
|
||||
[{<<"module">>, NameBin}],
|
||||
[{only, button}, {input_name_append, [NameBin]}]]),
|
||||
Uninstall =
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[module_uninstall,
|
||||
R,
|
||||
[{<<"module">>, NameBin}],
|
||||
[{only, button},
|
||||
{style, danger},
|
||||
{input_name_append, [NameBin]}]]),
|
||||
{?C(NameBin), ?C(list_to_binary(Summary)), Upgrade, Uninstall}
|
||||
end,
|
||||
Title ++ Result ++ Contents.
|
||||
ModulesInstalled),
|
||||
ejabberd_web_admin:make_table(200, RPath, Columns, Rows).
|
||||
|
||||
make_table_available(Node, R, RPath) ->
|
||||
Columns = [<<"Name">>, <<"Summary">>, <<"">>],
|
||||
ModulesAll =
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command_raw_value,
|
||||
[modules_available, R, []]),
|
||||
ModulesInstalled =
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command_raw_value,
|
||||
[modules_installed, R, []]),
|
||||
ModulesNotInstalled =
|
||||
lists:filter(fun({Mod, _}) -> not lists:keymember(Mod, 1, ModulesInstalled) end,
|
||||
ModulesAll),
|
||||
Rows =
|
||||
lists:map(fun({Name, Summary}) ->
|
||||
NameBin = misc:atom_to_binary(Name),
|
||||
Install =
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[module_install,
|
||||
R,
|
||||
[{<<"module">>, NameBin}],
|
||||
[{only, button}, {input_name_append, [NameBin]}]]),
|
||||
{?C(NameBin), ?C(list_to_binary(Summary)), Install}
|
||||
end,
|
||||
ModulesNotInstalled),
|
||||
ejabberd_web_admin:make_table(200, RPath, Columns, Rows).
|
||||
|
||||
webadmin_node_contrib(Node, Query, Lang) ->
|
||||
QueryRes = list_modules_parse_query(Query),
|
||||
Contents = get_content(Node, Query, Lang),
|
||||
Result =
|
||||
case QueryRes of
|
||||
ok ->
|
||||
[?XREST(?T("Submitted"))];
|
||||
nothing ->
|
||||
[]
|
||||
end,
|
||||
Result ++ Contents.
|
||||
%% @format-end
|
||||
|
||||
get_module_home(Module, Attrs) ->
|
||||
case get_module_information(home, Attrs) of
|
||||
|
@ -59,7 +59,7 @@
|
||||
|
||||
% Roster
|
||||
add_rosteritem/7, delete_rosteritem/4,
|
||||
get_roster/2, push_roster/3,
|
||||
get_roster/2, get_roster_count/2, push_roster/3,
|
||||
push_roster_all/1, push_alltoall/2,
|
||||
push_roster_item/5, build_roster_item/3,
|
||||
|
||||
@ -67,8 +67,10 @@
|
||||
private_get/4, private_set/3,
|
||||
|
||||
% Shared roster
|
||||
srg_create/5,
|
||||
srg_create/5, srg_add/2,
|
||||
srg_delete/2, srg_list/1, srg_get_info/2,
|
||||
srg_set_info/4,
|
||||
srg_get_displayed/2, srg_add_displayed/3, srg_del_displayed/3,
|
||||
srg_get_members/2, srg_user_add/4, srg_user_del/4,
|
||||
|
||||
% Send message
|
||||
@ -80,9 +82,17 @@
|
||||
% Stats
|
||||
stats/1, stats/2
|
||||
]).
|
||||
-export([web_menu_main/2, web_page_main/2,
|
||||
web_menu_host/3, web_page_host/3,
|
||||
web_menu_hostuser/4, web_page_hostuser/4,
|
||||
web_menu_hostnode/4, web_page_hostnode/4,
|
||||
web_menu_node/3, web_page_node/3]).
|
||||
|
||||
-import(ejabberd_web_admin, [make_command/4, make_table/2]).
|
||||
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("ejabberd_web_admin.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
-include("mod_privacy.hrl").
|
||||
-include("ejabberd_sm.hrl").
|
||||
@ -94,7 +104,17 @@
|
||||
%%%
|
||||
|
||||
start(_Host, _Opts) ->
|
||||
ejabberd_commands:register_commands(?MODULE, get_commands_spec()).
|
||||
ejabberd_commands:register_commands(?MODULE, get_commands_spec()),
|
||||
{ok, [{hook, webadmin_menu_main, web_menu_main, 50, global},
|
||||
{hook, webadmin_page_main, web_page_main, 50, global},
|
||||
{hook, webadmin_menu_host, web_menu_host, 50},
|
||||
{hook, webadmin_page_host, web_page_host, 50},
|
||||
{hook, webadmin_menu_hostuser, web_menu_hostuser, 50},
|
||||
{hook, webadmin_page_hostuser, web_page_hostuser, 50},
|
||||
{hook, webadmin_menu_hostnode, web_menu_hostnode, 50},
|
||||
{hook, webadmin_page_hostnode, web_page_hostnode, 50},
|
||||
{hook, webadmin_menu_node, web_menu_node, 50, global},
|
||||
{hook, webadmin_page_node, web_page_node, 50, global}]}.
|
||||
|
||||
stop(Host) ->
|
||||
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
|
||||
@ -670,6 +690,16 @@ get_commands_spec() ->
|
||||
{pending, string},
|
||||
{groups, {list, {group, string}}}
|
||||
]}}}}},
|
||||
#ejabberd_commands{name = get_roster_count, tags = [roster],
|
||||
desc = "Get number of contacts in a local user roster",
|
||||
note = "added in 24.xx",
|
||||
policy = user,
|
||||
module = ?MODULE, function = get_roster_count,
|
||||
args = [],
|
||||
args_rename = [{server, host}],
|
||||
result_example = 5,
|
||||
result_desc = "Number",
|
||||
result = {value, integer}},
|
||||
#ejabberd_commands{name = push_roster, tags = [roster],
|
||||
desc = "Push template roster from file to a user",
|
||||
longdesc = "The text file must contain an erlang term: a list "
|
||||
@ -777,6 +807,14 @@ get_commands_spec() ->
|
||||
args_desc = ["Group identifier", "Group server name", "Group name",
|
||||
"Group description", "List of groups to display"],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = srg_add, tags = [shared_roster_group],
|
||||
desc = "Add/Create a Shared Roster Group (without details)",
|
||||
module = ?MODULE, function = srg_add,
|
||||
note = "added in 24.xx",
|
||||
args = [{group, binary}, {host, binary}],
|
||||
args_example = [<<"group3">>, <<"myserver.com">>],
|
||||
args_desc = ["Group identifier", "Group server name"],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = srg_delete, tags = [shared_roster_group],
|
||||
desc = "Delete a Shared Roster Group",
|
||||
module = ?MODULE, function = srg_delete,
|
||||
@ -802,6 +840,48 @@ get_commands_spec() ->
|
||||
result_example = [{<<"name">>, "Group 3"}, {<<"displayed_groups">>, "group1"}],
|
||||
result_desc = "List of group information, as key and value",
|
||||
result = {informations, {list, {information, {tuple, [{key, string}, {value, string}]}}}}},
|
||||
#ejabberd_commands{name = srg_set_info, tags = [shared_roster_group],
|
||||
desc = "Set info of a Shared Roster Group",
|
||||
module = ?MODULE, function = srg_set_info,
|
||||
note = "added in 24.xx",
|
||||
args = [{group, binary}, {host, binary}, {key, binary}, {value, binary}],
|
||||
args_example = [<<"group3">>, <<"myserver.com">>, <<"label">>, <<"Family">>],
|
||||
args_desc = ["Group identifier", "Group server name",
|
||||
"Information key: label, description",
|
||||
"Information value"],
|
||||
result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = srg_get_displayed, tags = [shared_roster_group],
|
||||
desc = "Get displayed groups of a Shared Roster Group",
|
||||
module = ?MODULE, function = srg_get_displayed,
|
||||
note = "added in 24.xx",
|
||||
args = [{group, binary}, {host, binary}],
|
||||
args_example = [<<"group3">>, <<"myserver.com">>],
|
||||
args_desc = ["Group identifier", "Group server name"],
|
||||
result_example = [<<"group1">>, <<"group2">>],
|
||||
result_desc = "List of groups to display",
|
||||
result = {display, {list, {group, binary}}}},
|
||||
#ejabberd_commands{name = srg_add_displayed, tags = [shared_roster_group],
|
||||
desc = "Add a group to displayed_groups of a Shared Roster Group",
|
||||
module = ?MODULE, function = srg_add_displayed,
|
||||
note = "added in 24.xx",
|
||||
args = [{group, binary}, {host, binary},
|
||||
{add, binary}],
|
||||
args_example = [<<"group3">>, <<"myserver.com">>, <<"group1">>],
|
||||
args_desc = ["Group identifier", "Group server name",
|
||||
"Group to add to displayed_groups"],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = srg_del_displayed, tags = [shared_roster_group],
|
||||
desc = "Delete a group from displayed_groups of a Shared Roster Group",
|
||||
module = ?MODULE, function = srg_del_displayed,
|
||||
note = "added in 24.xx",
|
||||
args = [{group, binary}, {host, binary},
|
||||
{del, binary}],
|
||||
args_example = [<<"group3">>, <<"myserver.com">>, <<"group1">>],
|
||||
args_desc = ["Group identifier", "Group server name",
|
||||
"Group to delete from displayed_groups"],
|
||||
result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = srg_get_members, tags = [shared_roster_group],
|
||||
desc = "Get members of a Shared Roster Group",
|
||||
module = ?MODULE, function = srg_get_members,
|
||||
@ -836,6 +916,18 @@ get_commands_spec() ->
|
||||
result_example = 5,
|
||||
result_desc = "Number",
|
||||
result = {value, integer}},
|
||||
#ejabberd_commands{name = get_offline_messages,
|
||||
tags = [internal, offline],
|
||||
desc = "Get the offline messages",
|
||||
policy = user,
|
||||
module = mod_offline, function = get_offline_messages,
|
||||
args = [],
|
||||
result = {queue, {list, {messages, {tuple, [{time, string},
|
||||
{from, string},
|
||||
{to, string},
|
||||
{packet, string}
|
||||
]}}}}},
|
||||
|
||||
#ejabberd_commands{name = send_message, tags = [stanza],
|
||||
desc = "Send a message to a local or remote bare of full JID",
|
||||
longdesc = "When sending a groupchat message to a MUC room, "
|
||||
@ -1586,6 +1678,15 @@ make_roster_xmlrpc(Roster) ->
|
||||
end,
|
||||
Roster).
|
||||
|
||||
get_roster_count(User, Server) ->
|
||||
case jid:make(User, Server) of
|
||||
error ->
|
||||
throw({error, "Invalid 'user'/'server'"});
|
||||
#jid{luser = U, lserver = S} ->
|
||||
Items = ejabberd_hooks:run_fold(roster_get, S, [], [{U, S}]),
|
||||
length(Items)
|
||||
end.
|
||||
|
||||
%%-----------------------------
|
||||
%% Push Roster from file
|
||||
%%-----------------------------
|
||||
@ -1748,12 +1849,29 @@ srg_create(Group, Host, Label, Description, Display) when is_binary(Display) ->
|
||||
srg_create(Group, Host, Label, Description, DisplayList);
|
||||
|
||||
srg_create(Group, Host, Label, Description, DisplayList) ->
|
||||
{_DispGroups, WrongDispGroups} = filter_groups_existence(Host, DisplayList),
|
||||
case (WrongDispGroups -- [Group]) /= [] of
|
||||
true ->
|
||||
{wrong_displayed_groups, WrongDispGroups};
|
||||
false ->
|
||||
srg_create2(Group, Host, Label, Description, DisplayList)
|
||||
end.
|
||||
|
||||
srg_create2(Group, Host, Label, Description, DisplayList) ->
|
||||
Opts = [{label, Label},
|
||||
{displayed_groups, DisplayList},
|
||||
{description, Description}],
|
||||
{atomic, _} = mod_shared_roster:create_group(Host, Group, Opts),
|
||||
ok.
|
||||
|
||||
srg_add(Group, Host) ->
|
||||
Opts = [{label, <<"">>},
|
||||
{description, <<"">>},
|
||||
{displayed_groups, []}
|
||||
],
|
||||
{atomic, _} = mod_shared_roster:create_group(Host, Group, Opts),
|
||||
ok.
|
||||
|
||||
srg_delete(Group, Host) ->
|
||||
{atomic, _} = mod_shared_roster:delete_group(Host, Group),
|
||||
ok.
|
||||
@ -1769,9 +1887,109 @@ srg_get_info(Group, Host) ->
|
||||
[{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts].
|
||||
|
||||
to_list([]) -> [];
|
||||
to_list([H|T]) -> [to_list(H)|to_list(T)];
|
||||
to_list([H|_]=List) when is_binary(H) -> lists:join(", ", [to_list(E) || E <- List]);
|
||||
to_list(E) when is_atom(E) -> atom_to_list(E);
|
||||
to_list(E) -> binary_to_list(E).
|
||||
to_list(E) when is_binary(E) -> binary_to_list(E).
|
||||
|
||||
%% @format-begin
|
||||
|
||||
srg_set_info(Group, Host, Key, Value) ->
|
||||
Opts =
|
||||
case mod_shared_roster:get_group_opts(Host, Group) of
|
||||
Os when is_list(Os) ->
|
||||
Os;
|
||||
error ->
|
||||
[]
|
||||
end,
|
||||
Opts2 = srg_set_info(Key, Value, Opts),
|
||||
case mod_shared_roster:set_group_opts(Host, Group, Opts2) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
Problem ->
|
||||
?INFO_MSG("Problem: ~n ~p", [Problem]), %+++
|
||||
error
|
||||
end.
|
||||
|
||||
srg_set_info(<<"description">>, Value, Opts) ->
|
||||
[{description, Value} | proplists:delete(description, Opts)];
|
||||
srg_set_info(<<"label">>, Value, Opts) ->
|
||||
[{label, Value} | proplists:delete(label, Opts)];
|
||||
srg_set_info(<<"all_users">>, <<"true">>, Opts) ->
|
||||
[{all_users, true} | proplists:delete(all_users, Opts)];
|
||||
srg_set_info(<<"online_users">>, <<"true">>, Opts) ->
|
||||
[{online_users, true} | proplists:delete(online_users, Opts)];
|
||||
srg_set_info(<<"all_users">>, _, Opts) ->
|
||||
proplists:delete(all_users, Opts);
|
||||
srg_set_info(<<"online_users">>, _, Opts) ->
|
||||
proplists:delete(online_users, Opts);
|
||||
srg_set_info(Key, _Value, Opts) ->
|
||||
?ERROR_MSG("Unknown Key in srg_set_info: ~p", [Key]),
|
||||
Opts.
|
||||
|
||||
srg_get_displayed(Group, Host) ->
|
||||
Opts =
|
||||
case mod_shared_roster:get_group_opts(Host, Group) of
|
||||
Os when is_list(Os) ->
|
||||
Os;
|
||||
error ->
|
||||
[]
|
||||
end,
|
||||
proplists:get_value(displayed_groups, Opts).
|
||||
|
||||
srg_add_displayed(Group, Host, NewGroup) ->
|
||||
Opts =
|
||||
case mod_shared_roster:get_group_opts(Host, Group) of
|
||||
Os when is_list(Os) ->
|
||||
Os;
|
||||
error ->
|
||||
[]
|
||||
end,
|
||||
{DispGroups, WrongDispGroups} = filter_groups_existence(Host, [NewGroup]),
|
||||
case WrongDispGroups /= [] of
|
||||
true ->
|
||||
{wrong_displayed_groups, WrongDispGroups};
|
||||
false ->
|
||||
DisplayedOld = proplists:get_value(displayed_groups, Opts, []),
|
||||
Opts2 =
|
||||
[{displayed_groups, lists:flatten(DisplayedOld, DispGroups)}
|
||||
| proplists:delete(displayed_groups, Opts)],
|
||||
case mod_shared_roster:set_group_opts(Host, Group, Opts2) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
Problem ->
|
||||
?INFO_MSG("Problem: ~n ~p", [Problem]), %+++
|
||||
error
|
||||
end
|
||||
end.
|
||||
|
||||
srg_del_displayed(Group, Host, OldGroup) ->
|
||||
Opts =
|
||||
case mod_shared_roster:get_group_opts(Host, Group) of
|
||||
Os when is_list(Os) ->
|
||||
Os;
|
||||
error ->
|
||||
[]
|
||||
end,
|
||||
DisplayedOld = proplists:get_value(displayed_groups, Opts, []),
|
||||
{DispGroups, OldDispGroups} = lists:partition(fun(G) -> G /= OldGroup end, DisplayedOld),
|
||||
case OldDispGroups == [] of
|
||||
true ->
|
||||
{inexistent_displayed_groups, OldGroup};
|
||||
false ->
|
||||
Opts2 = [{displayed_groups, DispGroups} | proplists:delete(displayed_groups, Opts)],
|
||||
case mod_shared_roster:set_group_opts(Host, Group, Opts2) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
Problem ->
|
||||
?INFO_MSG("Problem: ~n ~p", [Problem]), %+++
|
||||
error
|
||||
end
|
||||
end.
|
||||
|
||||
filter_groups_existence(Host, Groups) ->
|
||||
lists:partition(fun(Group) -> error /= mod_shared_roster:get_group_opts(Host, Group) end,
|
||||
Groups).
|
||||
%% @format-end
|
||||
|
||||
srg_get_members(Group, Host) ->
|
||||
Members = mod_shared_roster:get_group_explicit_users(Host,Group),
|
||||
@ -1915,6 +2133,256 @@ num_prio(Priority) when is_integer(Priority) ->
|
||||
num_prio(_) ->
|
||||
-1.
|
||||
|
||||
%%%
|
||||
%%% Web Admin
|
||||
%%%
|
||||
|
||||
%% @format-begin
|
||||
|
||||
%%% Main
|
||||
|
||||
web_menu_main(Acc, _Lang) ->
|
||||
Acc ++ [{<<"stats">>, <<"Statistics">>}].
|
||||
|
||||
web_page_main(_, #request{path = [<<"stats">>]} = R) ->
|
||||
Res = ?H1GL(<<"Statistics">>, <<"modules/#mod_stats">>, <<"mod_stats">>)
|
||||
++ [make_command(stats_host, R, [], [{only, presentation}]),
|
||||
make_command(incoming_s2s_number, R, [], [{only, presentation}]),
|
||||
make_command(outgoing_s2s_number, R, [], [{only, presentation}]),
|
||||
make_table([<<"stat name">>, {<<"stat value">>, right}],
|
||||
[{?C(<<"Registered Users:">>),
|
||||
make_command(stats,
|
||||
R,
|
||||
[{<<"name">>, <<"registeredusers">>}],
|
||||
[{only, value}])},
|
||||
{?C(<<"Online Users:">>),
|
||||
make_command(stats,
|
||||
R,
|
||||
[{<<"name">>, <<"onlineusers">>}],
|
||||
[{only, value}])},
|
||||
{?C(<<"S2S Connections Incoming:">>),
|
||||
make_command(incoming_s2s_number, R, [], [{only, value}])},
|
||||
{?C(<<"S2S Connections Outgoing:">>),
|
||||
make_command(outgoing_s2s_number, R, [], [{only, value}])}])],
|
||||
{stop, Res};
|
||||
web_page_main(Acc, _) ->
|
||||
Acc.
|
||||
|
||||
%%% Host
|
||||
|
||||
web_menu_host(Acc, _Host, _Lang) ->
|
||||
Acc ++ [{<<"purge">>, <<"Purge">>}, {<<"stats">>, <<"Statistics">>}].
|
||||
|
||||
web_page_host(_, Host, #request{path = [<<"purge">>]} = R) ->
|
||||
Head = [?XC(<<"h1">>, <<"Purge">>)],
|
||||
Set = [ejabberd_web_admin:make_command(delete_old_users_vhost,
|
||||
R,
|
||||
[{<<"host">>, Host}],
|
||||
[])],
|
||||
{stop, Head ++ Set};
|
||||
web_page_host(_, Host, #request{path = [<<"stats">>]} = R) ->
|
||||
Res = ?H1GL(<<"Statistics">>, <<"modules/#mod_stats">>, <<"mod_stats">>)
|
||||
++ [make_command(stats_host, R, [], [{only, presentation}]),
|
||||
make_table([<<"stat name">>, {<<"stat value">>, right}],
|
||||
[{?C(<<"Registered Users:">>),
|
||||
make_command(stats_host,
|
||||
R,
|
||||
[{<<"host">>, Host}, {<<"name">>, <<"registeredusers">>}],
|
||||
[{only, value},
|
||||
{result_links, [{stat, arg_host, 3, <<"users">>}]}])},
|
||||
{?C(<<"Online Users:">>),
|
||||
make_command(stats_host,
|
||||
R,
|
||||
[{<<"host">>, Host}, {<<"name">>, <<"onlineusers">>}],
|
||||
[{only, value},
|
||||
{result_links,
|
||||
[{stat, arg_host, 3, <<"online-users">>}]}])}])],
|
||||
{stop, Res};
|
||||
web_page_host(Acc, _, _) ->
|
||||
Acc.
|
||||
|
||||
%%% HostUser
|
||||
|
||||
web_menu_hostuser(Acc, _Host, _Username, _Lang) ->
|
||||
Acc
|
||||
++ [{<<"auth">>, <<"Authentication">>},
|
||||
{<<"mam">>, <<"MAM">>},
|
||||
{<<"privacy">>, <<"Privacy Lists">>},
|
||||
{<<"private">>, <<"Private XML Storage">>},
|
||||
{<<"session">>, <<"Sessions">>},
|
||||
{<<"vcard">>, <<"vCard">>}].
|
||||
|
||||
web_page_hostuser(_, Host, User, #request{path = [<<"auth">>]} = R) ->
|
||||
Ban = make_command(ban_account,
|
||||
R,
|
||||
[{<<"user">>, User}, {<<"host">>, Host}],
|
||||
[{style, danger}]),
|
||||
Unban = make_command(unban_account, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
Res = ?H1GLraw(<<"Authentication">>,
|
||||
<<"admin/configuration/authentication/">>,
|
||||
<<"Authentication">>)
|
||||
++ [make_command(register, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(check_account, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
?X(<<"hr">>),
|
||||
make_command(check_password, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(check_password_hash, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(change_password,
|
||||
R,
|
||||
[{<<"user">>, User}, {<<"host">>, Host}],
|
||||
[{style, danger}]),
|
||||
?X(<<"hr">>),
|
||||
make_command(get_ban_details, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
Ban,
|
||||
Unban,
|
||||
?X(<<"hr">>),
|
||||
make_command(unregister,
|
||||
R,
|
||||
[{<<"user">>, User}, {<<"host">>, Host}],
|
||||
[{style, danger}])],
|
||||
{stop, Res};
|
||||
web_page_hostuser(_, Host, User, #request{path = [<<"mam">>]} = R) ->
|
||||
Res = ?H1GL(<<"MAM">>, <<"modules/#mod_mam">>, <<"mod_mam">>)
|
||||
++ [make_command(remove_mam_for_user,
|
||||
R,
|
||||
[{<<"user">>, User}, {<<"host">>, Host}],
|
||||
[{style, danger}]),
|
||||
make_command(remove_mam_for_user_with_peer,
|
||||
R,
|
||||
[{<<"user">>, User}, {<<"host">>, Host}],
|
||||
[{style, danger}])],
|
||||
{stop, Res};
|
||||
web_page_hostuser(_, Host, User, #request{path = [<<"privacy">>]} = R) ->
|
||||
Res = ?H1GL(<<"Privacy Lists">>, <<"modules/#mod_privacy">>, <<"mod_privacy">>)
|
||||
++ [make_command(privacy_set, R, [{<<"user">>, User}, {<<"host">>, Host}], [])],
|
||||
{stop, Res};
|
||||
web_page_hostuser(_, Host, User, #request{path = [<<"private">>]} = R) ->
|
||||
Res = ?H1GL(<<"Private XML Storage">>, <<"modules/#mod_private">>, <<"mod_private">>)
|
||||
++ [make_command(private_set, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(private_get, R, [{<<"user">>, User}, {<<"host">>, Host}], [])],
|
||||
{stop, Res};
|
||||
web_page_hostuser(_, Host, User, #request{path = [<<"session">>]} = R) ->
|
||||
Head = [?XC(<<"h1">>, <<"Sessions">>), ?BR],
|
||||
Set = [make_command(resource_num, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(set_presence, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(kick_user, R, [{<<"user">>, User}, {<<"host">>, Host}], [{style, danger}]),
|
||||
make_command(kick_session,
|
||||
R,
|
||||
[{<<"user">>, User}, {<<"host">>, Host}],
|
||||
[{style, danger}])],
|
||||
timer:sleep(100), % kicking sessions takes a while, let's delay the get commands
|
||||
Get = [make_command(user_sessions_info,
|
||||
R,
|
||||
[{<<"user">>, User}, {<<"host">>, Host}],
|
||||
[{result_links, [{node, node, 5, <<>>}]}]),
|
||||
make_command(user_resources, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(get_presence, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(num_resources, R, [{<<"user">>, User}, {<<"host">>, Host}], [])],
|
||||
{stop, Head ++ Get ++ Set};
|
||||
web_page_hostuser(_, Host, User, #request{path = [<<"vcard">>]} = R) ->
|
||||
Head = ?H1GL(<<"vCard">>, <<"modules/#mod_vcard">>, <<"mod_vcard">>),
|
||||
Set = [make_command(set_nickname, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(set_vcard, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(set_vcard2, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(set_vcard2_multi, R, [{<<"user">>, User}, {<<"host">>, Host}], [])],
|
||||
timer:sleep(100), % setting vcard takes a while, let's delay the get commands
|
||||
FieldNames = [<<"VERSION">>, <<"FN">>, <<"NICKNAME">>, <<"BDAY">>],
|
||||
FieldNames2 =
|
||||
[{<<"N">>, <<"FAMILY">>},
|
||||
{<<"N">>, <<"GIVEN">>},
|
||||
{<<"N">>, <<"MIDDLE">>},
|
||||
{<<"ADR">>, <<"CTRY">>},
|
||||
{<<"ADR">>, <<"LOCALITY">>},
|
||||
{<<"EMAIL">>, <<"USERID">>}],
|
||||
Get = [make_command(get_vcard, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
?XE(<<"blockquote">>,
|
||||
[make_table([<<"name">>, <<"value">>],
|
||||
[{?C(FieldName),
|
||||
make_command(get_vcard,
|
||||
R,
|
||||
[{<<"user">>, User},
|
||||
{<<"host">>, Host},
|
||||
{<<"name">>, FieldName}],
|
||||
[{only, value}])}
|
||||
|| FieldName <- FieldNames])]),
|
||||
make_command(get_vcard2, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
?XE(<<"blockquote">>,
|
||||
[make_table([<<"name">>, <<"subname">>, <<"value">>],
|
||||
[{?C(FieldName),
|
||||
?C(FieldSubName),
|
||||
make_command(get_vcard2,
|
||||
R,
|
||||
[{<<"user">>, User},
|
||||
{<<"host">>, Host},
|
||||
{<<"name">>, FieldName},
|
||||
{<<"subname">>, FieldSubName}],
|
||||
[{only, value}])}
|
||||
|| {FieldName, FieldSubName} <- FieldNames2])]),
|
||||
make_command(get_vcard2_multi, R, [{<<"user">>, User}, {<<"host">>, Host}], [])],
|
||||
{stop, Head ++ Get ++ Set};
|
||||
web_page_hostuser(Acc, _, _, _) ->
|
||||
Acc.
|
||||
|
||||
%%% HostNode
|
||||
|
||||
web_menu_hostnode(Acc, _Host, _Username, _Lang) ->
|
||||
Acc ++ [{<<"modules">>, <<"Modules">>}].
|
||||
|
||||
web_page_hostnode(_, Host, Node, #request{path = [<<"modules">>]} = R) ->
|
||||
Res = ?H1GLraw(<<"Modules">>, <<"admin/configuration/modules/">>, <<"Modules Options">>)
|
||||
++ [ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[restart_module, R, [{<<"host">>, Host}], []])],
|
||||
{stop, Res};
|
||||
web_page_hostnode(Acc, _Host, _Node, _Request) ->
|
||||
Acc.
|
||||
|
||||
%%% Node
|
||||
|
||||
web_menu_node(Acc, _Node, _Lang) ->
|
||||
Acc ++ [{<<"stats">>, <<"Statistics">>}].
|
||||
|
||||
web_page_node(_, Node, #request{path = [<<"stats">>]} = R) ->
|
||||
UpSecs =
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[stats, R, [{<<"name">>, <<"uptimeseconds">>}], [{only, value}]]),
|
||||
UpDaysBin = integer_to_binary(binary_to_integer(fxml:get_tag_cdata(UpSecs)) div 24000),
|
||||
UpDays =
|
||||
#xmlel{name = <<"code">>,
|
||||
attrs = [],
|
||||
children = [{xmlcdata, UpDaysBin}]},
|
||||
Res = ?H1GL(<<"Statistics">>, <<"modules/#mod_stats">>, <<"mod_stats">>)
|
||||
++ [make_command(stats, R, [], [{only, presentation}]),
|
||||
make_table([<<"stat name">>, {<<"stat value">>, right}],
|
||||
[{?C(<<"Online Users in this node:">>),
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[stats,
|
||||
R,
|
||||
[{<<"name">>, <<"onlineusersnode">>}],
|
||||
[{only, value}]])},
|
||||
{?C(<<"Uptime Seconds:">>), UpSecs},
|
||||
{?C(<<"Uptime Seconds (rounded to days):">>), UpDays},
|
||||
{?C(<<"Processes:">>),
|
||||
ejabberd_cluster:call(Node,
|
||||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[stats,
|
||||
R,
|
||||
[{<<"name">>, <<"processes">>}],
|
||||
[{only, value}]])}])],
|
||||
{stop, Res};
|
||||
web_page_node(Acc, _, _) ->
|
||||
Acc.
|
||||
%% @format-end
|
||||
|
||||
%%%
|
||||
%%% Document
|
||||
%%%
|
||||
|
||||
mod_options(_) -> [].
|
||||
|
||||
mod_doc() ->
|
||||
|
@ -34,7 +34,7 @@
|
||||
process_iq/1,
|
||||
get_mix_roster_items/2,
|
||||
webadmin_user/4,
|
||||
webadmin_page/3]).
|
||||
webadmin_menu_hostuser/4, webadmin_page_hostuser/4]).
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
@ -69,7 +69,8 @@ start(Host, Opts) ->
|
||||
ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:add(roster_get, Host, ?MODULE, get_mix_roster_items, 50),
|
||||
ejabberd_hooks:add(webadmin_user, Host, ?MODULE, webadmin_user, 50),
|
||||
ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, webadmin_page, 50),
|
||||
ejabberd_hooks:add(webadmin_menu_hostuser, Host, ?MODULE, webadmin_menu_hostuser, 50),
|
||||
ejabberd_hooks:add(webadmin_page_hostuser, Host, ?MODULE, webadmin_page_hostuser, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_0, ?MODULE, process_iq),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_2, ?MODULE, process_iq);
|
||||
Err ->
|
||||
@ -82,7 +83,8 @@ stop(Host) ->
|
||||
ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:delete(roster_get, Host, ?MODULE, get_mix_roster_items, 50),
|
||||
ejabberd_hooks:delete(webadmin_user, Host, ?MODULE, webadmin_user, 50),
|
||||
ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, webadmin_page, 50),
|
||||
ejabberd_hooks:delete(webadmin_menu_hostuser, Host, ?MODULE, webadmin_menu_hostuser, 50),
|
||||
ejabberd_hooks:delete(webadmin_page_hostuser, Host, ?MODULE, webadmin_page_hostuser, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_0),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_2).
|
||||
|
||||
@ -469,7 +471,7 @@ delete_cache(Mod, JID, Channel) ->
|
||||
%%%===================================================================
|
||||
%%% Webadmin interface
|
||||
%%%===================================================================
|
||||
webadmin_user(Acc, User, Server, Lang) ->
|
||||
webadmin_user(Acc, User, Server, #request{lang = Lang}) ->
|
||||
QueueLen = case get_channels({jid:nodeprep(User), jid:nameprep(Server), <<>>}) of
|
||||
{ok, Channels} -> length(Channels);
|
||||
error -> -1
|
||||
@ -482,12 +484,13 @@ webadmin_user(Acc, User, Server, Lang) ->
|
||||
?C(<<" | ">>),
|
||||
FQueueView].
|
||||
|
||||
webadmin_page(_, Host,
|
||||
#request{us = _US, path = [<<"user">>, U, <<"mix_channels">>],
|
||||
lang = Lang} = _Request) ->
|
||||
webadmin_menu_hostuser(Acc, _Host, _Username, _Lang) ->
|
||||
Acc ++ [{<<"mix_channels">>, <<"MIX Channels">>}].
|
||||
|
||||
webadmin_page_hostuser(_, Host, U, #request{path = [<<"mix_channels">>], lang = Lang}) ->
|
||||
Res = web_mix_channels(U, Host, Lang),
|
||||
{stop, Res};
|
||||
webadmin_page(Acc, _, _) -> Acc.
|
||||
webadmin_page_hostuser(Acc, _, _, _) -> Acc.
|
||||
|
||||
web_mix_channels(User, Server, Lang) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
|
@ -34,20 +34,24 @@
|
||||
create_room_with_opts/4, create_room/3, destroy_room/2,
|
||||
create_rooms_file/1, destroy_rooms_file/1,
|
||||
rooms_unused_list/2, rooms_unused_destroy/2,
|
||||
rooms_empty_list/1, rooms_empty_destroy/1,
|
||||
rooms_empty_list/1, rooms_empty_destroy/1, rooms_empty_destroy_restuple/1,
|
||||
get_user_rooms/2, get_user_subscriptions/2, get_room_occupants/2,
|
||||
get_room_occupants_number/2, send_direct_invitation/5,
|
||||
change_room_option/4, get_room_options/2,
|
||||
set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3,
|
||||
web_menu_main/2, web_page_main/2, web_menu_host/3,
|
||||
subscribe_room/4, subscribe_room_many/3,
|
||||
unsubscribe_room/2, get_subscribers/2,
|
||||
get_room_serverhost/1,
|
||||
web_page_host/3,
|
||||
web_menu_main/2, web_page_main/2,
|
||||
web_menu_host/3, web_page_host/3,
|
||||
web_menu_hostuser/4, web_page_hostuser/4,
|
||||
webadmin_muc/2,
|
||||
mod_opt_type/1, mod_options/1,
|
||||
get_commands_spec/0, find_hosts/1, room_diagnostics/2,
|
||||
get_room_pid/2, get_room_history/2]).
|
||||
|
||||
-import(ejabberd_web_admin, [make_command/4, make_command_raw_value/3, make_table/4]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("mod_muc.hrl").
|
||||
@ -66,7 +70,10 @@ start(_Host, _Opts) ->
|
||||
{ok, [{hook, webadmin_menu_main, web_menu_main, 50, global},
|
||||
{hook, webadmin_page_main, web_page_main, 50, global},
|
||||
{hook, webadmin_menu_host, web_menu_host, 50},
|
||||
{hook, webadmin_page_host, web_page_host, 50}]}.
|
||||
{hook, webadmin_page_host, web_page_host, 50},
|
||||
{hook, webadmin_menu_hostuser, web_menu_hostuser, 50},
|
||||
{hook, webadmin_page_hostuser, web_page_hostuser, 50}
|
||||
]}.
|
||||
|
||||
stop(Host) ->
|
||||
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
|
||||
@ -235,6 +242,19 @@ get_commands_spec() ->
|
||||
args = [{service, binary}],
|
||||
args_rename = [{host, service}],
|
||||
result = {rooms, {list, {room, string}}}},
|
||||
#ejabberd_commands{name = rooms_empty_destroy, tags = [muc],
|
||||
desc = "Destroy the rooms that have no messages in archive",
|
||||
longdesc = "The MUC service argument can be `global` to get all hosts.",
|
||||
module = ?MODULE, function = rooms_empty_destroy_restuple,
|
||||
version = 2,
|
||||
note = "modified in 24.xx",
|
||||
args_desc = ["MUC service, or `global` for all"],
|
||||
args_example = ["conference.example.com"],
|
||||
result_desc = "List of empty rooms that have been destroyed",
|
||||
result_example = {ok, <<"Destroyed rooms: 2">>},
|
||||
args = [{service, binary}],
|
||||
args_rename = [{host, service}],
|
||||
result = {res, restuple}},
|
||||
|
||||
#ejabberd_commands{name = get_user_rooms, tags = [muc],
|
||||
desc = "Get the list of rooms where this user is occupant",
|
||||
@ -478,7 +498,13 @@ get_commands_spec() ->
|
||||
result = {history, {list,
|
||||
{entry, {tuple,
|
||||
[{timestamp, string},
|
||||
{message, string}]}}}}}
|
||||
{message, string}]}}}}},
|
||||
|
||||
#ejabberd_commands{name = webadmin_muc, tags = [internal],
|
||||
desc = "Generate WebAdmin MUC Rooms HTML",
|
||||
module = ?MODULE, function = webadmin_muc,
|
||||
args = [{request, any}, {lang, binary}],
|
||||
result = {res, any}}
|
||||
].
|
||||
|
||||
|
||||
@ -580,6 +606,8 @@ get_user_subscriptions(User, Server) ->
|
||||
%% Web Admin
|
||||
%%----------------------------
|
||||
|
||||
%% @format-begin
|
||||
|
||||
%%---------------
|
||||
%% Web Admin Menu
|
||||
|
||||
@ -589,74 +617,372 @@ web_menu_main(Acc, Lang) ->
|
||||
web_menu_host(Acc, _Host, Lang) ->
|
||||
Acc ++ [{<<"muc">>, translate:translate(Lang, ?T("Multi-User Chat"))}].
|
||||
|
||||
|
||||
%%---------------
|
||||
%% Web Admin Page
|
||||
|
||||
-define(TDTD(L, N),
|
||||
?XE(<<"tr">>, [?XCT(<<"td">>, L),
|
||||
?XC(<<"td">>, integer_to_binary(N))
|
||||
])).
|
||||
?XE(<<"tr">>, [?XCT(<<"td">>, L), ?XC(<<"td">>, integer_to_binary(N))])).
|
||||
|
||||
web_page_main(_, #request{path=[<<"muc">>], lang = Lang} = _Request) ->
|
||||
OnlineRoomsNumber = lists:foldl(
|
||||
fun(Host, Acc) ->
|
||||
Acc + mod_muc:count_online_rooms(Host)
|
||||
end, 0, find_hosts(global)),
|
||||
web_page_main(_, #request{path = [<<"muc">>], lang = Lang} = R) ->
|
||||
PageTitle = translate:translate(Lang, ?T("Multi-User Chat")),
|
||||
Res = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>) ++
|
||||
[?XCT(<<"h3">>, ?T("Statistics")),
|
||||
?XAE(<<"table">>, [],
|
||||
[?XE(<<"tbody">>, [?TDTD(?T("Total rooms"), OnlineRoomsNumber)
|
||||
])
|
||||
]),
|
||||
?XE(<<"ul">>, [?LI([?ACT(<<"rooms/">>, ?T("List of rooms"))])])
|
||||
],
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Res = [make_command(webadmin_muc, R, [{<<"request">>, R}, {<<"lang">>, Lang}], [])],
|
||||
{stop, Title ++ Res};
|
||||
web_page_main(Acc, _) ->
|
||||
Acc.
|
||||
|
||||
web_page_host(_, Host, #request{path = [<<"muc">> | RPath], lang = Lang} = R) ->
|
||||
PageTitle = translate:translate(Lang, ?T("Multi-User Chat")),
|
||||
Service = find_service(Host),
|
||||
Level = length(RPath),
|
||||
Res = webadmin_muc_host(Host, Service, RPath, R, Lang, Level, PageTitle),
|
||||
{stop, Res};
|
||||
web_page_host(Acc, _, _) ->
|
||||
Acc.
|
||||
|
||||
web_page_main(_, #request{path=[<<"muc">>, <<"rooms">>], q = Q, lang = Lang} = _Request) ->
|
||||
Sort_query = get_sort_query(Q),
|
||||
Res = make_rooms_page(global, Lang, Sort_query),
|
||||
{stop, Res};
|
||||
%%---------------
|
||||
%% WebAdmin MUC Host Page
|
||||
|
||||
web_page_main(Acc, _) -> Acc.
|
||||
webadmin_muc_host(Host,
|
||||
Service,
|
||||
[<<"create-room">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb = make_breadcrumb({service_section, Level, Service, <<"Create Room">>, RPath}),
|
||||
Set = [make_command(create_room, R, [{<<"service">>, Service}, {<<"host">>, Host}], []),
|
||||
make_command(create_room_with_opts,
|
||||
R,
|
||||
[{<<"service">>, Service}, {<<"host">>, Host}],
|
||||
[])],
|
||||
Title ++ Breadcrumb ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"nick-register">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({service_section, Level, Service, <<"Nick Register">>, RPath}),
|
||||
Set = [make_command(muc_register_nick, R, [{<<"service">>, Service}], []),
|
||||
make_command(muc_unregister_nick, R, [{<<"service">>, Service}], [])],
|
||||
Title ++ Breadcrumb ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms-empty">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb = make_breadcrumb({service_section, Level, Service, <<"Rooms Empty">>, RPath}),
|
||||
Set = [make_command(rooms_empty_list,
|
||||
R,
|
||||
[{<<"service">>, Service}],
|
||||
[{table_options, {2, RPath}},
|
||||
{result_links, [{room, room, 3 + Level, <<"">>}]}]),
|
||||
make_command(rooms_empty_destroy, R, [{<<"service">>, Service}], [])],
|
||||
Title ++ Breadcrumb ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms-unused">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({service_section, Level, Service, <<"Rooms Unused">>, RPath}),
|
||||
Set = [make_command(rooms_unused_list,
|
||||
R,
|
||||
[{<<"service">>, Service}],
|
||||
[{result_links, [{room, room, 3 + Level, <<"">>}]}]),
|
||||
make_command(rooms_unused_destroy, R, [{<<"service">>, Service}], [])],
|
||||
Title ++ Breadcrumb ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms-regex">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({service_section, Level, Service, <<"Rooms by Regex">>, RPath}),
|
||||
Set = [make_command(muc_online_rooms_by_regex,
|
||||
R,
|
||||
[{<<"service">>, Service}],
|
||||
[{result_links, [{jid, room, 3 + Level, <<"">>}]}])],
|
||||
Title ++ Breadcrumb ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms">>, <<"room">>, Name, <<"affiliations">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({room_section, Level, Service, <<"Affiliations">>, Name, R, RPath}),
|
||||
Set = [make_command(set_room_affiliation,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[])],
|
||||
Get = [make_command(get_room_affiliations,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{table_options, {20, RPath}}])],
|
||||
Title ++ Breadcrumb ++ Get ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms">>, <<"room">>, Name, <<"history">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({room_section, Level, Service, <<"History">>, Name, R, RPath}),
|
||||
Get = [make_command(get_room_history,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{table_options, {10, RPath}},
|
||||
{result_links, [{message, paragraph, 1, <<"">>}]}])],
|
||||
Title ++ Breadcrumb ++ Get;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms">>, <<"room">>, Name, <<"invite">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({room_section, Level, Service, <<"Invite">>, Name, R, RPath}),
|
||||
Set = [make_command(send_direct_invitation,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[])],
|
||||
Title ++ Breadcrumb ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms">>, <<"room">>, Name, <<"occupants">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({room_section, Level, Service, <<"Occupants">>, Name, R, RPath}),
|
||||
Get = [make_command(get_room_occupants,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{table_options, {20, RPath}},
|
||||
{result_links, [{jid, user, 3 + Level, <<"">>}]}])],
|
||||
Title ++ Breadcrumb ++ Get;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms">>, <<"room">>, Name, <<"options">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({room_section, Level, Service, <<"Options">>, Name, R, RPath}),
|
||||
Set = [make_command(change_room_option,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[])],
|
||||
Get = [make_command(get_room_options,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[])],
|
||||
Title ++ Breadcrumb ++ Get ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms">>, <<"room">>, Name, <<"subscribers">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title =
|
||||
?H1GLraw(PageTitle,
|
||||
<<"developer/xmpp-clients-bots/extensions/muc-sub/">>,
|
||||
<<"MUC/Sub Extension">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({room_section, Level, Service, <<"Subscribers">>, Name, R, RPath}),
|
||||
Set = [make_command(subscribe_room,
|
||||
R,
|
||||
[{<<"room">>, jid:encode({Name, Service, <<"">>})}],
|
||||
[]),
|
||||
make_command(unsubscribe_room,
|
||||
R,
|
||||
[{<<"room">>, jid:encode({Name, Service, <<"">>})}],
|
||||
[{style, danger}])],
|
||||
Get = [make_command(get_subscribers,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{table_options, {20, RPath}},
|
||||
{result_links, [{jid, user, 3 + Level, <<"">>}]}])],
|
||||
Title ++ Breadcrumb ++ Get ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms">>, <<"room">>, Name, <<"destroy">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({room_section, Level, Service, <<"Destroy">>, Name, R, RPath}),
|
||||
Set = [make_command(destroy_room,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{style, danger}])],
|
||||
Title ++ Breadcrumb ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms">>, <<"room">>, Name | _RPath],
|
||||
_R,
|
||||
Lang,
|
||||
Level,
|
||||
PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb = make_breadcrumb({room, Level, Service, Name}),
|
||||
MenuItems =
|
||||
[{<<"affiliations/">>, <<"Affiliations">>},
|
||||
{<<"history/">>, <<"History">>},
|
||||
{<<"invite/">>, <<"Invite">>},
|
||||
{<<"occupants/">>, <<"Occupants">>},
|
||||
{<<"options/">>, <<"Options">>},
|
||||
{<<"subscribers/">>, <<"Subscribers">>},
|
||||
{<<"destroy/">>, <<"Destroy">>}],
|
||||
Get = [?XE(<<"ul">>, [?LI([?ACT(MIU, MIN)]) || {MIU, MIN} <- MenuItems])],
|
||||
Title ++ Breadcrumb ++ Get;
|
||||
webadmin_muc_host(_Host, Service, [<<"rooms">> | RPath], R, _Lang, Level, PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb = make_breadcrumb({service_section, Level, Service, <<"Rooms">>, RPath}),
|
||||
Columns = [<<"jid">>, <<"occupants">>],
|
||||
Rows =
|
||||
lists:map(fun(NameService) ->
|
||||
#jid{user = Name} = jid:decode(NameService),
|
||||
{make_command(echo,
|
||||
R,
|
||||
[{<<"sentence">>, jid:encode({Name, Service, <<"">>})}],
|
||||
[{only, raw_and_value},
|
||||
{result_links, [{sentence, room, 3 + Level, <<"">>}]}]),
|
||||
make_command(get_room_occupants_number,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{only, raw_and_value}])}
|
||||
end,
|
||||
make_command_raw_value(muc_online_rooms, R, [{<<"service">>, Service}])),
|
||||
Get = [make_command(muc_online_rooms, R, [], [{only, presentation}]),
|
||||
make_command(get_room_occupants_number, R, [], [{only, presentation}]),
|
||||
make_table(20, RPath, Columns, Rows)],
|
||||
Title ++ Breadcrumb ++ Get;
|
||||
webadmin_muc_host(_Host, Service, [], _R, Lang, _Level, PageTitle) ->
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb = make_breadcrumb({service, Service}),
|
||||
MenuItems =
|
||||
[{<<"create-room/">>, <<"Create Room">>},
|
||||
{<<"rooms/">>, <<"Rooms">>},
|
||||
{<<"rooms-regex/">>, <<"Rooms by Regex">>},
|
||||
{<<"rooms-empty/">>, <<"Rooms Empty">>},
|
||||
{<<"rooms-unused/">>, <<"Rooms Unused">>},
|
||||
{<<"nick-register/">>, <<"Nick Register">>}],
|
||||
Get = [?XE(<<"ul">>, [?LI([?ACT(MIU, MIN)]) || {MIU, MIN} <- MenuItems])],
|
||||
Title ++ Breadcrumb ++ Get;
|
||||
webadmin_muc_host(_Host, _Service, _RPath, _R, _Lang, _Level, _PageTitle) ->
|
||||
[].
|
||||
|
||||
web_page_host(_, Host,
|
||||
#request{path = [<<"muc">>],
|
||||
q = Q,
|
||||
lang = Lang} = _Request) ->
|
||||
Sort_query = get_sort_query(Q),
|
||||
Res = make_rooms_page(Host, Lang, Sort_query),
|
||||
{stop, Res};
|
||||
web_page_host(Acc, _, _) -> Acc.
|
||||
make_breadcrumb({service, Service}) ->
|
||||
make_breadcrumb([Service]);
|
||||
make_breadcrumb({service_section, Level, Service, Section, RPath}) ->
|
||||
make_breadcrumb([{Level, Service}, separator, Section | RPath]);
|
||||
make_breadcrumb({room, Level, Service, Name}) ->
|
||||
make_breadcrumb([{Level, Service},
|
||||
separator,
|
||||
{Level - 1, <<"Rooms">>},
|
||||
separator,
|
||||
jid:encode({Name, Service, <<"">>})]);
|
||||
make_breadcrumb({room_section, Level, Service, Section, Name, R, RPath}) ->
|
||||
make_breadcrumb([{Level, Service},
|
||||
separator,
|
||||
{Level - 1, <<"Rooms">>},
|
||||
separator,
|
||||
make_command(echo,
|
||||
R,
|
||||
[{<<"sentence">>, jid:encode({Name, Service, <<"">>})}],
|
||||
[{only, value},
|
||||
{result_links, [{sentence, room, 3 + Level, <<"">>}]}]),
|
||||
separator,
|
||||
Section
|
||||
| RPath]);
|
||||
make_breadcrumb(Elements) ->
|
||||
lists:map(fun ({xmlel, _, _, _} = Xmlel) ->
|
||||
Xmlel;
|
||||
(<<"sort">>) ->
|
||||
?C(<<" +">>);
|
||||
(<<"page">>) ->
|
||||
?C(<<" #">>);
|
||||
(separator) ->
|
||||
?C(<<" > ">>);
|
||||
(Bin) when is_binary(Bin) ->
|
||||
?C(Bin);
|
||||
({Level, Bin}) when is_integer(Level) and is_binary(Bin) ->
|
||||
?AC(binary:copy(<<"../">>, Level), Bin)
|
||||
end,
|
||||
Elements).
|
||||
|
||||
%%---------------
|
||||
%%
|
||||
|
||||
%% Returns: {normal | reverse, Integer}
|
||||
get_sort_query(Q) ->
|
||||
case catch get_sort_query2(Q) of
|
||||
{ok, Res} -> Res;
|
||||
_ -> {normal, 1}
|
||||
{ok, Res} ->
|
||||
Res;
|
||||
_ ->
|
||||
{normal, 1}
|
||||
end.
|
||||
|
||||
get_sort_query2(Q) ->
|
||||
{value, {_, Binary}} = lists:keysearch(<<"sort">>, 1, Q),
|
||||
Integer = list_to_integer(string:strip(binary_to_list(Binary), right, $/)),
|
||||
case Integer >= 0 of
|
||||
true -> {ok, {normal, Integer}};
|
||||
false -> {ok, {reverse, abs(Integer)}}
|
||||
true ->
|
||||
{ok, {normal, Integer}};
|
||||
false ->
|
||||
{ok, {reverse, abs(Integer)}}
|
||||
end.
|
||||
|
||||
make_rooms_page(Host, Lang, {Sort_direction, Sort_column}) ->
|
||||
webadmin_muc(#request{q = Q} = R, Lang) ->
|
||||
{Sort_direction, Sort_column} = get_sort_query(Q),
|
||||
Host = global,
|
||||
Service = find_service(Host),
|
||||
Rooms_names = get_online_rooms(Service),
|
||||
Rooms_infos = build_info_rooms(Rooms_names),
|
||||
Rooms_sorted = sort_rooms(Sort_direction, Sort_column, Rooms_infos),
|
||||
Rooms_prepared = prepare_rooms_infos(Rooms_sorted),
|
||||
TList = lists:map(
|
||||
fun(Room) ->
|
||||
?XE(<<"tr">>, [?XC(<<"td">>, E) || E <- Room])
|
||||
end, Rooms_prepared),
|
||||
Titles = [?T("Jabber ID"),
|
||||
TList =
|
||||
lists:map(fun([RoomJid | Room]) ->
|
||||
JidLink =
|
||||
make_command(echo,
|
||||
R,
|
||||
[{<<"sentence">>, RoomJid}],
|
||||
[{only, value},
|
||||
{result_links, [{sentence, room, 1, <<"">>}]}]),
|
||||
?XE(<<"tr">>, [?XE(<<"td">>, [JidLink]) | [?XC(<<"td">>, E) || E <- Room]])
|
||||
end,
|
||||
Rooms_prepared),
|
||||
Titles =
|
||||
[?T("Jabber ID"),
|
||||
?T("# participants"),
|
||||
?T("Last message"),
|
||||
?T("Public"),
|
||||
@ -666,10 +992,10 @@ make_rooms_page(Host, Lang, {Sort_direction, Sort_column}) ->
|
||||
?T("Room title"),
|
||||
?T("Node")],
|
||||
{Titles_TR, _} =
|
||||
lists:mapfoldl(
|
||||
fun(Title, Num_column) ->
|
||||
lists:mapfoldl(fun(Title, Num_column) ->
|
||||
NCS = integer_to_binary(Num_column),
|
||||
TD = ?XE(<<"td">>, [?CT(Title),
|
||||
TD = ?XE(<<"td">>,
|
||||
[?CT(Title),
|
||||
?C(<<" ">>),
|
||||
?AC(<<"?sort=", NCS/binary>>, <<"<">>),
|
||||
?C(<<" ">>),
|
||||
@ -678,23 +1004,17 @@ make_rooms_page(Host, Lang, {Sort_direction, Sort_column}) ->
|
||||
end,
|
||||
1,
|
||||
Titles),
|
||||
PageTitle = translate:translate(Lang, ?T("Multi-User Chat")),
|
||||
?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>) ++
|
||||
[?XCT(<<"h2">>, ?T("Chatrooms")),
|
||||
?XE(<<"table">>,
|
||||
[?XE(<<"thead">>,
|
||||
[?XE(<<"tr">>, Titles_TR)]
|
||||
),
|
||||
?XE(<<"tbody">>, TList)
|
||||
]
|
||||
)
|
||||
].
|
||||
[?XE(<<"thead">>, [?XE(<<"tr">>, Titles_TR)]), ?XE(<<"tbody">>, TList)])].
|
||||
|
||||
sort_rooms(Direction, Column, Rooms) ->
|
||||
Rooms2 = lists:keysort(Column, Rooms),
|
||||
case Direction of
|
||||
normal -> Rooms2;
|
||||
reverse -> lists:reverse(Rooms2)
|
||||
normal ->
|
||||
Rooms2;
|
||||
reverse ->
|
||||
lists:reverse(Rooms2)
|
||||
end.
|
||||
|
||||
build_info_rooms(Rooms) ->
|
||||
@ -712,7 +1032,7 @@ build_info_room({Name, Host, _ServerHost, Pid}) ->
|
||||
Num_participants = maps:size(S#state.users),
|
||||
Node = node(Pid),
|
||||
|
||||
History = (S#state.history)#lqueue.queue,
|
||||
History = S#state.history#lqueue.queue,
|
||||
Ts_last_message =
|
||||
case p1_queue:is_empty(History) of
|
||||
true ->
|
||||
@ -739,6 +1059,7 @@ get_queue_last(Queue) ->
|
||||
|
||||
prepare_rooms_infos(Rooms) ->
|
||||
[prepare_room_info(Room) || Room <- Rooms].
|
||||
|
||||
prepare_room_info(Room_info) ->
|
||||
{NameHost,
|
||||
Num_participants,
|
||||
@ -748,7 +1069,8 @@ prepare_room_info(Room_info) ->
|
||||
Logging,
|
||||
Just_created,
|
||||
Title,
|
||||
Node} = Room_info,
|
||||
Node} =
|
||||
Room_info,
|
||||
[NameHost,
|
||||
integer_to_binary(Num_participants),
|
||||
Ts_last_message,
|
||||
@ -767,6 +1089,57 @@ justcreated_to_binary(J) when is_integer(J) ->
|
||||
justcreated_to_binary(J) when is_atom(J) ->
|
||||
misc:atom_to_binary(J).
|
||||
|
||||
%%--------------------
|
||||
%% Web Admin Host User
|
||||
|
||||
web_menu_hostuser(Acc, _Host, _Username, _Lang) ->
|
||||
Acc
|
||||
++ [{<<"muc-rooms">>, <<"MUC Rooms Online">>},
|
||||
{<<"muc-affiliations">>, <<"MUC Rooms Affiliations">>},
|
||||
{<<"muc-sub">>, <<"MUC Rooms Subscriptions">>},
|
||||
{<<"muc-register">>, <<"MUC Service Registration">>}].
|
||||
|
||||
web_page_hostuser(_, Host, User, #request{path = [<<"muc-rooms">> | RPath]} = R) ->
|
||||
Level = 5 + length(RPath),
|
||||
Res = ?H1GL(<<"MUC Rooms Online">>, <<"modules/#mod_muc">>, <<"mod_muc">>)
|
||||
++ [make_command(get_user_rooms,
|
||||
R,
|
||||
[{<<"user">>, User}, {<<"host">>, Host}],
|
||||
[{table_options, {2, RPath}},
|
||||
{result_links, [{room, room, Level, <<"">>}]}])],
|
||||
{stop, Res};
|
||||
web_page_hostuser(_, Host, User, #request{path = [<<"muc-affiliations">>]} = R) ->
|
||||
Jid = jid:encode(
|
||||
jid:make(User, Host)),
|
||||
Res = ?H1GL(<<"MUC Rooms Affiliations">>, <<"modules/#mod_muc">>, <<"mod_muc">>)
|
||||
++ [make_command(set_room_affiliation, R, [{<<"jid">>, Jid}], []),
|
||||
make_command(get_room_affiliation, R, [{<<"jid">>, Jid}], [])],
|
||||
{stop, Res};
|
||||
web_page_hostuser(_, Host, User, #request{path = [<<"muc-sub">> | RPath]} = R) ->
|
||||
Title =
|
||||
?H1GLraw(<<"MUC Rooms Subscriptions">>,
|
||||
<<"developer/xmpp-clients-bots/extensions/muc-sub/">>,
|
||||
<<"MUC/Sub">>),
|
||||
Level = 5 + length(RPath),
|
||||
Set = [make_command(subscribe_room, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
|
||||
make_command(unsubscribe_room, R, [{<<"user">>, User}, {<<"host">>, Host}], [])],
|
||||
Get = [make_command(get_user_subscriptions,
|
||||
R,
|
||||
[{<<"user">>, User}, {<<"host">>, Host}],
|
||||
[{table_options, {20, RPath}},
|
||||
{result_links, [{roomjid, room, Level, <<"">>}]}])],
|
||||
{stop, Title ++ Get ++ Set};
|
||||
web_page_hostuser(_, Host, User, #request{path = [<<"muc-register">>]} = R) ->
|
||||
Jid = jid:encode(
|
||||
jid:make(User, Host)),
|
||||
Res = ?H1GL(<<"MUC Service Registration">>, <<"modules/#mod_muc">>, <<"mod_muc">>)
|
||||
++ [make_command(muc_register_nick, R, [{<<"jid">>, Jid}], []),
|
||||
make_command(muc_unregister_nick, R, [{<<"jid">>, Jid}], [])],
|
||||
{stop, Res};
|
||||
web_page_hostuser(Acc, _, _, _) ->
|
||||
Acc.
|
||||
%% @format-end
|
||||
|
||||
%%----------------------------
|
||||
%% Create/Delete Room
|
||||
%%----------------------------
|
||||
@ -898,6 +1271,10 @@ rooms_empty_list(Service) ->
|
||||
rooms_empty_destroy(Service) ->
|
||||
rooms_report(empty, destroy, Service, 0).
|
||||
|
||||
rooms_empty_destroy_restuple(Service) ->
|
||||
DestroyedRooms = rooms_report(empty, destroy, Service, 0),
|
||||
NumberBin = integer_to_binary(length(DestroyedRooms)),
|
||||
{ok, <<"Destroyed rooms: ", NumberBin/binary>>}.
|
||||
|
||||
rooms_report(Method, Action, Service, Days) ->
|
||||
{NA, NP, RP} = muc_unused(Method, Action, Service, Days),
|
||||
@ -1413,7 +1790,8 @@ get_room_history(Name, Service) ->
|
||||
History = p1_queue:to_list((StateData#state.history)#lqueue.queue),
|
||||
lists:map(
|
||||
fun({_Nick, Packet, _HaveSubject, TimeStamp, _Size}) ->
|
||||
{xmpp_util:encode_timestamp(TimeStamp), fxml:element_to_binary(xmpp:encode(Packet))}
|
||||
{xmpp_util:encode_timestamp(TimeStamp),
|
||||
ejabberd_web_admin:pretty_print_xml(xmpp:encode(Packet))}
|
||||
end, History);
|
||||
_ ->
|
||||
throw({error, "Unable to fetch room state."})
|
||||
|
@ -60,13 +60,17 @@
|
||||
find_x_expire/2,
|
||||
c2s_handle_info/2,
|
||||
c2s_copy_session/2,
|
||||
webadmin_page/3,
|
||||
get_offline_messages/2,
|
||||
webadmin_menu_hostuser/4,
|
||||
webadmin_page_hostuser/4,
|
||||
webadmin_user/4,
|
||||
webadmin_user_parse_query/5,
|
||||
c2s_handle_bind2_inline/1]).
|
||||
|
||||
-export([mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]).
|
||||
|
||||
-import(ejabberd_web_admin, [make_command/4, make_command/2]).
|
||||
|
||||
-deprecated({get_queue_length,2}).
|
||||
|
||||
-include("logger.hrl").
|
||||
@ -133,7 +137,8 @@ start(Host, Opts) ->
|
||||
{hook, c2s_handle_info, c2s_handle_info, 50},
|
||||
{hook, c2s_copy_session, c2s_copy_session, 50},
|
||||
{hook, c2s_handle_bind2_inline, c2s_handle_bind2_inline, 50},
|
||||
{hook, webadmin_page_host, webadmin_page, 50},
|
||||
{hook, webadmin_menu_hostuser, webadmin_menu_hostuser, 50},
|
||||
{hook, webadmin_page_hostuser, webadmin_page_hostuser, 50},
|
||||
{hook, webadmin_user, webadmin_user, 50},
|
||||
{hook, webadmin_user_parse_query, webadmin_user_parse_query, 50},
|
||||
{iq_handler, ejabberd_sm, ?NS_FLEX_OFFLINE, handle_offline_query}]}.
|
||||
@ -730,12 +735,39 @@ discard_warn_sender(Packet, Reason) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
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.
|
||||
%%%
|
||||
%%% Commands
|
||||
%%%
|
||||
|
||||
get_offline_messages(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
HdrsAll = case Mod:read_message_headers(LUser, LServer) of
|
||||
error -> [];
|
||||
L -> L
|
||||
end,
|
||||
format_user_queue(HdrsAll).
|
||||
|
||||
%%%
|
||||
%%% WebAdmin
|
||||
%%%
|
||||
|
||||
webadmin_menu_hostuser(Acc, _Host, _Username, _Lang) ->
|
||||
Acc ++ [{<<"queue">>, <<"Offline Queue">>}].
|
||||
|
||||
webadmin_page_hostuser(_, Host, U,
|
||||
#request{us = _US, path = [<<"queue">> | RPath],
|
||||
lang = Lang} = R) ->
|
||||
US = {U, Host},
|
||||
PageTitle = str:translate_and_format(Lang, ?T("~ts's Offline Messages Queue"), [us_to_list(US)]),
|
||||
Head = ?H1GL(PageTitle, <<"modules/#mod_offline">>, <<"mod_offline">>),
|
||||
Res = make_command(get_offline_messages, R, [{<<"user">>, U},
|
||||
{<<"host">>, Host}],
|
||||
[{table_options, {10, RPath}},
|
||||
{result_links, [{packet, paragraph, 1, <<"">>}]}]),
|
||||
{stop, Head ++ [Res]};
|
||||
webadmin_page_hostuser(Acc, _, _, _) -> Acc.
|
||||
|
||||
get_offline_els(LUser, LServer) ->
|
||||
[Packet || {_Seq, Packet} <- read_messages(LUser, LServer)].
|
||||
@ -939,8 +971,7 @@ count_mam_messages(LUser, LServer, ReadMsgs) ->
|
||||
|
||||
format_user_queue(Hdrs) ->
|
||||
lists:map(
|
||||
fun({Seq, From, To, TS, El}) ->
|
||||
ID = integer_to_binary(Seq),
|
||||
fun({_Seq, From, To, TS, El}) ->
|
||||
FPacket = ejabberd_web_admin:pretty_print_xml(El),
|
||||
SFrom = jid:encode(From),
|
||||
STo = jid:encode(To),
|
||||
@ -956,14 +987,7 @@ format_user_queue(Hdrs) ->
|
||||
{_, _, _} = Now ->
|
||||
format_time(Now)
|
||||
end,
|
||||
?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)])])
|
||||
{Time, SFrom, STo, FPacket}
|
||||
end, Hdrs).
|
||||
|
||||
format_time(Now) ->
|
||||
@ -971,111 +995,18 @@ format_time(Now) ->
|
||||
str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
|
||||
[Year, Month, Day, Hour, Minute, Second]).
|
||||
|
||||
user_queue(User, Server, Query, Lang) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
user_queue_parse_query(LUser, LServer, Query),
|
||||
HdrsAll = case Mod:read_message_headers(LUser, LServer) of
|
||||
error -> [];
|
||||
L -> L
|
||||
end,
|
||||
Hdrs = get_messages_subset(User, Server, HdrsAll),
|
||||
FMsgs = format_user_queue(Hdrs),
|
||||
PageTitle = str:translate_and_format(Lang, ?T("~ts's Offline Messages Queue"), [us_to_list(US)]),
|
||||
(?H1GL(PageTitle, <<"modules/#mod_offline">>, <<"mod_offline">>))
|
||||
++ [?XREST(?T("Submitted"))] ++
|
||||
[?XAE(<<"form">>,
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
||||
[?XE(<<"table">>,
|
||||
[?XE(<<"thead">>,
|
||||
[?XE(<<"tr">>,
|
||||
[?X(<<"td">>), ?XCT(<<"td">>, ?T("Time")),
|
||||
?XCT(<<"td">>, ?T("From")),
|
||||
?XCT(<<"td">>, ?T("To")),
|
||||
?XCT(<<"td">>, ?T("Packet"))])]),
|
||||
?XE(<<"tbody">>,
|
||||
if FMsgs == [] ->
|
||||
[?XE(<<"tr">>,
|
||||
[?XAC(<<"td">>, [{<<"colspan">>, <<"4">>}],
|
||||
<<" ">>)])];
|
||||
true -> FMsgs
|
||||
end)]),
|
||||
?BR,
|
||||
?INPUTTD(<<"submit">>, <<"delete">>,
|
||||
?T("Delete Selected"))])].
|
||||
|
||||
user_queue_parse_query(LUser, LServer, Query) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case lists:keysearch(<<"delete">>, 1, Query) of
|
||||
{value, _} ->
|
||||
case user_queue_parse_query(LUser, LServer, Query, Mod, false) of
|
||||
true ->
|
||||
flush_cache(Mod, LUser, LServer);
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
user_queue_parse_query(LUser, LServer, Query, Mod, Acc) ->
|
||||
case lists:keytake(<<"selected">>, 1, Query) of
|
||||
{value, {_, Seq}, Query2} ->
|
||||
NewAcc = case catch binary_to_integer(Seq) of
|
||||
I when is_integer(I), I>=0 ->
|
||||
Mod:remove_message(LUser, LServer, I),
|
||||
true;
|
||||
_ ->
|
||||
Acc
|
||||
end,
|
||||
user_queue_parse_query(LUser, LServer, Query2, Mod, NewAcc);
|
||||
false ->
|
||||
Acc
|
||||
end.
|
||||
|
||||
us_to_list({User, Server}) ->
|
||||
jid:encode({User, Server, <<"">>}).
|
||||
|
||||
get_queue_length(LUser, LServer) ->
|
||||
count_offline_messages(LUser, LServer).
|
||||
|
||||
get_messages_subset(User, Host, MsgsAll) ->
|
||||
MaxOfflineMsgs = case get_max_user_messages(User, Host) of
|
||||
Number when is_integer(Number) -> Number;
|
||||
_ -> 100
|
||||
end,
|
||||
Length = length(MsgsAll),
|
||||
get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll).
|
||||
webadmin_user(Acc, User, Server, R) ->
|
||||
Acc ++ [make_command(get_offline_count, R, [{<<"user">>, User}, {<<"host">>, Server}], [])].
|
||||
|
||||
get_messages_subset2(Max, Length, MsgsAll) when Length =< Max * 2 ->
|
||||
MsgsAll;
|
||||
get_messages_subset2(Max, Length, MsgsAll) ->
|
||||
FirstN = Max,
|
||||
{MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
|
||||
MsgsLastN = lists:nthtail(Length - FirstN - FirstN,
|
||||
Msgs2),
|
||||
NoJID = jid:make(<<"...">>, <<"...">>),
|
||||
Seq = <<"0">>,
|
||||
IntermediateMsg = #xmlel{name = <<"...">>, attrs = [],
|
||||
children = []},
|
||||
MsgsFirstN ++ [{Seq, NoJID, NoJID, IntermediateMsg}] ++ MsgsLastN.
|
||||
|
||||
webadmin_user(Acc, User, Server, Lang) ->
|
||||
QueueLen = count_offline_messages(jid:nodeprep(User),
|
||||
jid:nameprep(Server)),
|
||||
FQueueLen = ?C(integer_to_binary(QueueLen)),
|
||||
FQueueView = ?AC(<<"queue/">>,
|
||||
?T("View Queue")),
|
||||
Acc ++
|
||||
[?XCT(<<"h3">>, ?T("Offline Messages:")),
|
||||
FQueueLen,
|
||||
?C(<<" | ">>),
|
||||
FQueueView,
|
||||
?C(<<" | ">>),
|
||||
?INPUTTD(<<"submit">>, <<"removealloffline">>,
|
||||
?T("Remove All Offline Messages"))].
|
||||
%%%
|
||||
%%%
|
||||
%%%
|
||||
|
||||
-spec delete_all_msgs(binary(), binary()) -> {atomic, any()}.
|
||||
delete_all_msgs(User, Server) ->
|
||||
|
@ -46,13 +46,16 @@
|
||||
import_start/2, import_stop/2, is_subscribed/2,
|
||||
c2s_self_presence/1, in_subscription/2,
|
||||
out_subscription/1, set_items/3, remove_user/2,
|
||||
get_jid_info/4, encode_item/1, webadmin_page/3,
|
||||
webadmin_user/4, get_versioning_feature/2,
|
||||
get_jid_info/4, encode_item/1, get_versioning_feature/2,
|
||||
roster_version/2, mod_doc/0,
|
||||
mod_opt_type/1, mod_options/1, set_roster/1, del_roster/3,
|
||||
process_rosteritems/5,
|
||||
depends/2, set_item_and_notify_clients/3]).
|
||||
|
||||
-export([webadmin_page_hostuser/4, webadmin_menu_hostuser/4, webadmin_user/4]).
|
||||
|
||||
-import(ejabberd_web_admin, [make_command/4, make_command_raw_value/3, make_table/4]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
@ -98,7 +101,8 @@ start(Host, Opts) ->
|
||||
{hook, remove_user, remove_user, 50},
|
||||
{hook, c2s_self_presence, c2s_self_presence, 50},
|
||||
{hook, c2s_post_auth_features, get_versioning_feature, 50},
|
||||
{hook, webadmin_page_host, webadmin_page, 50},
|
||||
{hook, webadmin_menu_hostuser, webadmin_menu_hostuser, 50},
|
||||
{hook, webadmin_page_hostuser, webadmin_page_hostuser, 50},
|
||||
{hook, webadmin_user, webadmin_user, 50},
|
||||
{iq_handler, ejabberd_sm, ?NS_ROSTER, process_iq}]}.
|
||||
|
||||
@ -1016,205 +1020,84 @@ process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) ->
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
webadmin_page(_, Host,
|
||||
#request{us = _US, path = [<<"user">>, U, <<"roster">>],
|
||||
q = Query, lang = Lang} =
|
||||
_Request) ->
|
||||
Res = user_roster(U, Host, Query, Lang), {stop, Res};
|
||||
webadmin_page(Acc, _, _) -> Acc.
|
||||
%%% @format-begin
|
||||
|
||||
user_roster(User, Server, Query, Lang) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
Items1 = get_roster(LUser, LServer),
|
||||
Res = user_roster_parse_query(User, Server, Items1,
|
||||
Query),
|
||||
Items = get_roster(LUser, LServer),
|
||||
SItems = lists:sort(Items),
|
||||
FItems = case SItems of
|
||||
[] -> [?CT(?T("None"))];
|
||||
webadmin_menu_hostuser(Acc, _Host, _Username, _Lang) ->
|
||||
Acc ++ [{<<"roster">>, <<"Roster">>}].
|
||||
|
||||
webadmin_page_hostuser(_, Host, Username, #request{path = [<<"roster">> | RPath]} = R) ->
|
||||
Head = ?H1GL(<<"Roster">>, <<"modules/#mod_roster">>, <<"mod_roster">>),
|
||||
%% Execute twice: first to perform the action, the second to get new roster
|
||||
_ = make_webadmin_roster_table(Host, Username, R, RPath),
|
||||
RV2 = make_webadmin_roster_table(Host, Username, R, RPath),
|
||||
Set = [make_command(add_rosteritem,
|
||||
R,
|
||||
[{<<"localuser">>, Username}, {<<"localhost">>, Host}],
|
||||
[]),
|
||||
make_command(push_roster, R, [{<<"user">>, Username}, {<<"host">>, Host}], [])],
|
||||
Get = [make_command(get_roster, R, [], [{only, presentation}]),
|
||||
make_command(delete_rosteritem, R, [], [{only, presentation}]),
|
||||
RV2],
|
||||
{stop, Head ++ Get ++ Set};
|
||||
webadmin_page_hostuser(Acc, _, _, _) ->
|
||||
Acc.
|
||||
|
||||
make_webadmin_roster_table(Host, Username, R, RPath) ->
|
||||
Contacts =
|
||||
case make_command_raw_value(get_roster, R, [{<<"user">>, Username}, {<<"host">>, Host}])
|
||||
of
|
||||
Cs when is_list(Cs) ->
|
||||
Cs;
|
||||
_ ->
|
||||
[?XE(<<"table">>,
|
||||
[?XE(<<"thead">>,
|
||||
[?XE(<<"tr">>,
|
||||
[?XCT(<<"td">>, ?T("Jabber ID")),
|
||||
?XCT(<<"td">>, ?T("Nickname")),
|
||||
?XCT(<<"td">>, ?T("Subscription")),
|
||||
?XCT(<<"td">>, ?T("Pending")),
|
||||
?XCT(<<"td">>, ?T("Groups"))])]),
|
||||
?XE(<<"tbody">>,
|
||||
(lists:map(fun (R) ->
|
||||
Groups = lists:flatmap(fun
|
||||
(Group) ->
|
||||
[?C(Group),
|
||||
?BR]
|
||||
[]
|
||||
end,
|
||||
R#roster.groups),
|
||||
Pending =
|
||||
ask_to_pending(R#roster.ask),
|
||||
TDJID =
|
||||
build_contact_jid_td(R#roster.jid),
|
||||
?XE(<<"tr">>,
|
||||
[TDJID,
|
||||
?XAC(<<"td">>,
|
||||
[{<<"class">>,
|
||||
<<"valign">>}],
|
||||
(R#roster.name)),
|
||||
?XAC(<<"td">>,
|
||||
[{<<"class">>,
|
||||
<<"valign">>}],
|
||||
(iolist_to_binary(atom_to_list(R#roster.subscription)))),
|
||||
?XAC(<<"td">>,
|
||||
[{<<"class">>,
|
||||
<<"valign">>}],
|
||||
(iolist_to_binary(atom_to_list(Pending)))),
|
||||
?XAE(<<"td">>,
|
||||
[{<<"class">>,
|
||||
<<"valign">>}],
|
||||
Groups),
|
||||
if Pending == in ->
|
||||
?XAE(<<"td">>,
|
||||
[{<<"class">>,
|
||||
<<"valign">>}],
|
||||
[?INPUTT(<<"submit">>,
|
||||
<<"validate",
|
||||
(ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>,
|
||||
?T("Validate"))]);
|
||||
true -> ?X(<<"td">>)
|
||||
Level = 5 + length(RPath),
|
||||
Columns =
|
||||
[<<"jid">>, <<"nick">>, <<"subscription">>, <<"pending">>, <<"groups">>, <<"">>],
|
||||
Rows =
|
||||
lists:map(fun({Jid, Nick, Subscriptions, Pending, Groups}) ->
|
||||
{JidSplit, ProblematicBin} =
|
||||
try jid:decode(Jid) of
|
||||
#jid{} = J ->
|
||||
{jid:split(J), <<"">>}
|
||||
catch
|
||||
_:{bad_jid, _} ->
|
||||
?INFO_MSG("Error parsing contact of ~s@~s that is invalid JID: ~s",
|
||||
[Username, Host, Jid]),
|
||||
{{<<"000--error-parsing-jid">>, <<"localhost">>, <<"">>},
|
||||
<<", Error parsing JID: ", Jid/binary>>}
|
||||
end,
|
||||
?XAE(<<"td">>,
|
||||
[{<<"class">>,
|
||||
<<"valign">>}],
|
||||
[?INPUTTD(<<"submit">>,
|
||||
<<"remove",
|
||||
(ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>,
|
||||
?T("Remove"))])])
|
||||
{make_command(echo,
|
||||
R,
|
||||
[{<<"sentence">>, jid:encode(JidSplit)}],
|
||||
[{only, raw_and_value},
|
||||
{result_links, [{sentence, user, Level, <<"">>}]}]),
|
||||
?C(<<Nick/binary, ProblematicBin/binary>>),
|
||||
?C(Subscriptions),
|
||||
?C(Pending),
|
||||
?C(Groups),
|
||||
make_command(delete_rosteritem,
|
||||
R,
|
||||
[{<<"localuser">>, Username},
|
||||
{<<"localhost">>, Host},
|
||||
{<<"user">>, element(1, JidSplit)},
|
||||
{<<"host">>, element(2, JidSplit)}],
|
||||
[{only, button},
|
||||
{style, danger},
|
||||
{input_name_append,
|
||||
[Username,
|
||||
Host,
|
||||
element(1, JidSplit),
|
||||
element(2, JidSplit)]}])}
|
||||
end,
|
||||
SItems)))])]
|
||||
end,
|
||||
PageTitle = str:translate_and_format(Lang, ?T("Roster of ~ts"), [us_to_list(US)]),
|
||||
(?H1GL(PageTitle, <<"modules/#mod_roster">>, <<"mod_roster">>))
|
||||
++
|
||||
case Res of
|
||||
ok -> [?XREST(?T("Submitted"))];
|
||||
error -> [?XREST(?T("Bad format"))];
|
||||
nothing -> []
|
||||
end
|
||||
++
|
||||
[?XAE(<<"form">>,
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
||||
( [?P, ?INPUT(<<"text">>, <<"newjid">>, <<"">>),
|
||||
?C(<<" ">>),
|
||||
?INPUTT(<<"submit">>, <<"addjid">>,
|
||||
?T("Add Jabber ID"))]
|
||||
++ FItems))].
|
||||
lists:keysort(1, Contacts)),
|
||||
Table = make_table(20, RPath, Columns, Rows),
|
||||
?XE(<<"blockquote">>, [Table]).
|
||||
|
||||
build_contact_jid_td(RosterJID) ->
|
||||
ContactJID = jid:make(RosterJID),
|
||||
JIDURI = case {ContactJID#jid.luser,
|
||||
ContactJID#jid.lserver}
|
||||
of
|
||||
{<<"">>, _} -> <<"">>;
|
||||
{CUser, CServer} ->
|
||||
case lists:member(CServer, ejabberd_option:hosts()) of
|
||||
false -> <<"">>;
|
||||
true ->
|
||||
<<"../../../../../server/", CServer/binary, "/user/",
|
||||
CUser/binary, "/">>
|
||||
end
|
||||
end,
|
||||
case JIDURI of
|
||||
<<>> ->
|
||||
?XAC(<<"td">>, [{<<"class">>, <<"valign">>}],
|
||||
(jid:encode(RosterJID)));
|
||||
URI when is_binary(URI) ->
|
||||
?XAE(<<"td">>, [{<<"class">>, <<"valign">>}],
|
||||
[?AC(JIDURI, (jid:encode(RosterJID)))])
|
||||
end.
|
||||
|
||||
user_roster_parse_query(User, Server, Items, Query) ->
|
||||
case lists:keysearch(<<"addjid">>, 1, Query) of
|
||||
{value, _} ->
|
||||
case lists:keysearch(<<"newjid">>, 1, Query) of
|
||||
{value, {_, SJID}} ->
|
||||
try jid:decode(SJID) of
|
||||
JID ->
|
||||
user_roster_subscribe_jid(User, Server, JID), ok
|
||||
catch _:{bad_jid, _} ->
|
||||
error
|
||||
end;
|
||||
false -> error
|
||||
end;
|
||||
false ->
|
||||
case catch user_roster_item_parse_query(User, Server,
|
||||
Items, Query)
|
||||
of
|
||||
submitted -> ok;
|
||||
{'EXIT', _Reason} -> error;
|
||||
_ -> nothing
|
||||
end
|
||||
end.
|
||||
|
||||
user_roster_subscribe_jid(User, Server, JID) ->
|
||||
UJID = jid:make(User, Server),
|
||||
Presence = #presence{from = UJID, to = JID, type = subscribe},
|
||||
out_subscription(Presence),
|
||||
ejabberd_router:route(Presence).
|
||||
|
||||
user_roster_item_parse_query(User, Server, Items,
|
||||
Query) ->
|
||||
lists:foreach(fun (R) ->
|
||||
JID = R#roster.jid,
|
||||
case lists:keysearch(<<"validate",
|
||||
(ejabberd_web_admin:term_to_id(JID))/binary>>,
|
||||
1, Query)
|
||||
of
|
||||
{value, _} ->
|
||||
JID1 = jid:make(JID),
|
||||
UJID = jid:make(User, Server),
|
||||
Pres = #presence{from = UJID, to = JID1,
|
||||
type = subscribed},
|
||||
out_subscription(Pres),
|
||||
ejabberd_router:route(Pres),
|
||||
throw(submitted);
|
||||
false ->
|
||||
case lists:keysearch(<<"remove",
|
||||
(ejabberd_web_admin:term_to_id(JID))/binary>>,
|
||||
1, Query)
|
||||
of
|
||||
{value, _} ->
|
||||
UJID = jid:make(User, Server),
|
||||
RosterItem = #roster_item{
|
||||
jid = jid:make(JID),
|
||||
subscription = remove},
|
||||
process_iq_set(
|
||||
#iq{type = set,
|
||||
from = UJID,
|
||||
to = UJID,
|
||||
id = p1_rand:get_string(),
|
||||
sub_els = [#roster_query{
|
||||
items = [RosterItem]}]}),
|
||||
throw(submitted);
|
||||
false -> ok
|
||||
end
|
||||
end
|
||||
end,
|
||||
Items),
|
||||
nothing.
|
||||
|
||||
us_to_list({User, Server}) ->
|
||||
jid:encode({User, Server, <<"">>}).
|
||||
|
||||
webadmin_user(Acc, User, Server, Lang) ->
|
||||
QueueLen = length(get_roster(jid:nodeprep(User), jid:nameprep(Server))),
|
||||
FQueueLen = ?C(integer_to_binary(QueueLen)),
|
||||
FQueueView = ?AC(<<"roster/">>, ?T("View Roster")),
|
||||
Acc ++
|
||||
[?XCT(<<"h3">>, ?T("Roster:")),
|
||||
FQueueLen,
|
||||
?C(<<" | ">>),
|
||||
FQueueView].
|
||||
webadmin_user(Acc, User, Server, R) ->
|
||||
Acc
|
||||
++ [make_command(get_roster_count, R, [{<<"user">>, User}, {<<"host">>, Server}], [])].
|
||||
%%% @format-end
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
-spec has_duplicated_groups([binary()]) -> boolean().
|
||||
|
@ -41,6 +41,8 @@
|
||||
is_user_in_group/3, add_user_to_group/3, opts_to_binary/1,
|
||||
remove_user_from_group/3, mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]).
|
||||
|
||||
-import(ejabberd_web_admin, [make_command/4, make_command_raw_value/3, make_table/2, make_table/4]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
@ -861,286 +863,402 @@ unset_presence(User, Server, Resource, Status) ->
|
||||
end.
|
||||
|
||||
%%---------------------
|
||||
%% Web Admin
|
||||
%% Web Admin: Page Frontend
|
||||
%%---------------------
|
||||
|
||||
%% @format-begin
|
||||
|
||||
webadmin_menu(Acc, _Host, Lang) ->
|
||||
[{<<"shared-roster">>, translate:translate(Lang, ?T("Shared Roster Groups"))}
|
||||
| Acc].
|
||||
[{<<"shared-roster">>, translate:translate(Lang, ?T("Shared Roster Groups"))} | 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 = list_groups(Host),
|
||||
FGroups = (?XAE(<<"table">>, [],
|
||||
[?XE(<<"tbody">>,
|
||||
[?XE(<<"tr">>,
|
||||
[?X(<<"td">>),
|
||||
?XE(<<"td">>, [?CT(?T("Name:"))])
|
||||
])]++
|
||||
(lists:map(fun (Group) ->
|
||||
?XE(<<"tr">>,
|
||||
[?XE(<<"td">>,
|
||||
[?INPUT(<<"checkbox">>,
|
||||
<<"selected">>,
|
||||
Group)]),
|
||||
?XE(<<"td">>,
|
||||
[?AC(<<Group/binary, "/">>,
|
||||
Group)])])
|
||||
end,
|
||||
lists:sort(SRGroups))
|
||||
++
|
||||
[?XE(<<"tr">>,
|
||||
[?X(<<"td">>),
|
||||
?XE(<<"td">>,
|
||||
[?INPUT(<<"text">>, <<"namenew">>,
|
||||
<<"">>),
|
||||
?C(<<" ">>),
|
||||
?INPUTT(<<"submit">>, <<"addnew">>,
|
||||
?T("Add New"))])])]))])),
|
||||
(?H1GL((translate:translate(Lang, ?T("Shared Roster Groups"))),
|
||||
<<"modules/#mod_shared_roster">>, <<"mod_shared_roster">>))
|
||||
++
|
||||
case Res of
|
||||
ok -> [?XREST(?T("Submitted"))];
|
||||
error -> [?XREST(?T("Bad format"))];
|
||||
nothing -> []
|
||||
end
|
||||
++
|
||||
[?XAE(<<"form">>,
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
||||
[FGroups, ?BR,
|
||||
?INPUTTD(<<"submit">>, <<"delete">>,
|
||||
?T("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 /= <<"">> ->
|
||||
create_group(Host, Group),
|
||||
ok;
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
list_sr_groups_parse_delete(Host, Query) ->
|
||||
SRGroups = list_groups(Host),
|
||||
lists:foreach(fun (Group) ->
|
||||
case lists:member({<<"selected">>, Group}, Query) of
|
||||
true -> 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 = get_group_opts(Host, Group),
|
||||
Label = get_opt(GroupOpts, label, <<"">>), %%++
|
||||
Description = get_opt(GroupOpts, description, <<"">>),
|
||||
AllUsers = get_opt(GroupOpts, all_users, false),
|
||||
OnlineUsers = get_opt(GroupOpts, online_users, false),
|
||||
DisplayedGroups = get_opt(GroupOpts, displayed_groups,
|
||||
[]),
|
||||
Members = get_group_explicit_users(Host,
|
||||
Group),
|
||||
FMembers = iolist_to_binary(
|
||||
[if AllUsers -> <<"@all@\n">>;
|
||||
true -> <<"">>
|
||||
end,
|
||||
if OnlineUsers -> <<"@online@\n">>;
|
||||
true -> <<"">>
|
||||
end,
|
||||
[[us_to_list(Member), $\n] || Member <- Members]]),
|
||||
FDisplayedGroups = [<<DG/binary, $\n>> || DG <- DisplayedGroups],
|
||||
DescNL = length(ejabberd_regexp:split(Description,
|
||||
<<"\n">>)),
|
||||
FGroup = (?XAE(<<"table">>,
|
||||
[{<<"class">>, <<"withtextareas">>}],
|
||||
[?XE(<<"tbody">>,
|
||||
[?XE(<<"tr">>,
|
||||
[?XCT(<<"td">>, ?T("Name:")),
|
||||
?XE(<<"td">>, [?C(Group)]),
|
||||
?XE(<<"td">>, [?C(<<"">>)])]),
|
||||
?XE(<<"tr">>,
|
||||
[?XCT(<<"td">>, ?T("Label:")),
|
||||
?XE(<<"td">>,
|
||||
[?INPUT(<<"text">>, <<"label">>, Label)]),
|
||||
?XE(<<"td">>, [?CT(?T("Name in the rosters where this group will be displayed"))])]),
|
||||
?XE(<<"tr">>,
|
||||
[?XCT(<<"td">>, ?T("Description:")),
|
||||
?XE(<<"td">>,
|
||||
[?TEXTAREA(<<"description">>,
|
||||
integer_to_binary(lists:max([3,
|
||||
DescNL])),
|
||||
<<"20">>, Description)]),
|
||||
?XE(<<"td">>, [?CT(?T("Only admins can see this"))])
|
||||
]),
|
||||
?XE(<<"tr">>,
|
||||
[?XCT(<<"td">>, ?T("Members:")),
|
||||
?XE(<<"td">>,
|
||||
[?TEXTAREA(<<"members">>,
|
||||
integer_to_binary(lists:max([3,
|
||||
length(Members)+3])),
|
||||
<<"20">>, FMembers)]),
|
||||
?XE(<<"td">>, [?C(<<"JIDs, @all@, @online@">>)])
|
||||
]),
|
||||
?XE(<<"tr">>,
|
||||
[?XCT(<<"td">>, ?T("Displayed:")),
|
||||
?XE(<<"td">>,
|
||||
[?TEXTAREA(<<"dispgroups">>,
|
||||
integer_to_binary(lists:max([3, length(FDisplayedGroups)])),
|
||||
<<"20">>,
|
||||
list_to_binary(FDisplayedGroups))]),
|
||||
?XE(<<"td">>, [?CT(?T("Groups that will be displayed to the members"))])
|
||||
])])])),
|
||||
(?H1GL((translate:translate(Lang, ?T("Shared Roster Groups"))),
|
||||
<<"modules/#mod_shared_roster">>, <<"mod_shared_roster">>))
|
||||
++
|
||||
[?XC(<<"h2">>, translate:translate(Lang, ?T("Group")))] ++
|
||||
case Res of
|
||||
ok -> [?XREST(?T("Submitted"))];
|
||||
{error_elements, NonAddedList1, NG1} ->
|
||||
make_error_el(Lang,
|
||||
?T("Members not added (inexistent vhost!): "),
|
||||
[jid:encode({U,S,<<>>}) || {U,S} <- NonAddedList1])
|
||||
++ make_error_el(Lang, ?T("'Displayed groups' not added (they do not exist!): "), NG1);
|
||||
error -> [?XREST(?T("Bad format"))];
|
||||
nothing -> []
|
||||
end
|
||||
++
|
||||
[?XAE(<<"form">>,
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
||||
[FGroup, ?BR,
|
||||
?INPUTT(<<"submit">>, <<"submit">>, ?T("Submit"))])].
|
||||
|
||||
make_error_el(_, _, []) ->
|
||||
[];
|
||||
make_error_el(Lang, Message, BinList) ->
|
||||
NG2 = str:join(BinList, <<", ">>),
|
||||
NG3 = translate:translate(Lang, Message),
|
||||
NG4 = str:concat(NG3, NG2),
|
||||
[?XRES(NG4)].
|
||||
|
||||
shared_roster_group_parse_query(Host, Group, Query) ->
|
||||
case lists:keysearch(<<"submit">>, 1, Query) of
|
||||
{value, _} ->
|
||||
{value, {_, Label}} = lists:keysearch(<<"label">>, 1,
|
||||
Query), %++
|
||||
{value, {_, Description}} =
|
||||
lists:keysearch(<<"description">>, 1, Query),
|
||||
{value, {_, SMembers}} = lists:keysearch(<<"members">>,
|
||||
1, Query),
|
||||
{value, {_, SDispGroups}} =
|
||||
lists:keysearch(<<"dispgroups">>, 1, Query),
|
||||
LabelOpt = if Label == <<"">> -> [];
|
||||
true -> [{label, Label}] %++
|
||||
end,
|
||||
DescriptionOpt = if Description == <<"">> -> [];
|
||||
true -> [{description, Description}]
|
||||
end,
|
||||
DispGroups1 = str:tokens(SDispGroups, <<"\r\n">>),
|
||||
{DispGroups, WrongDispGroups} = filter_groups_existence(Host, DispGroups1),
|
||||
DispGroupsOpt = if DispGroups == [] -> [];
|
||||
true -> [{displayed_groups, DispGroups}]
|
||||
end,
|
||||
OldMembers = get_group_explicit_users(Host,
|
||||
Group),
|
||||
SJIDs = str:tokens(SMembers, <<", \r\n">>),
|
||||
NewMembers = lists:foldl(fun (_SJID, error) -> error;
|
||||
(SJID, USs) ->
|
||||
case SJID of
|
||||
<<"@all@">> -> USs;
|
||||
<<"@online@">> -> USs;
|
||||
_ ->
|
||||
try jid:decode(SJID) of
|
||||
JID ->
|
||||
[{JID#jid.luser,
|
||||
JID#jid.lserver}
|
||||
| USs]
|
||||
catch _:{bad_jid, _} ->
|
||||
error
|
||||
end
|
||||
end
|
||||
end,
|
||||
[], SJIDs),
|
||||
AllUsersOpt = case lists:member(<<"@all@">>, SJIDs) of
|
||||
true -> [{all_users, true}];
|
||||
false -> []
|
||||
end,
|
||||
OnlineUsersOpt = case lists:member(<<"@online@">>,
|
||||
SJIDs)
|
||||
of
|
||||
true -> [{online_users, true}];
|
||||
false -> []
|
||||
end,
|
||||
CurrentDisplayedGroups = get_displayed_groups(Group, Host),
|
||||
AddedDisplayedGroups = DispGroups -- CurrentDisplayedGroups,
|
||||
RemovedDisplayedGroups = CurrentDisplayedGroups -- DispGroups,
|
||||
displayed_groups_update(OldMembers, RemovedDisplayedGroups, remove),
|
||||
displayed_groups_update(OldMembers, AddedDisplayedGroups, both),
|
||||
set_group_opts(Host, Group,
|
||||
LabelOpt ++
|
||||
DispGroupsOpt ++
|
||||
DescriptionOpt ++
|
||||
AllUsersOpt ++ OnlineUsersOpt),
|
||||
if NewMembers == error -> error;
|
||||
webadmin_page(_,
|
||||
Host,
|
||||
#request{us = _US,
|
||||
path = [<<"shared-roster">> | RPath],
|
||||
lang = Lang} =
|
||||
R) ->
|
||||
PageTitle = translate:translate(Lang, ?T("Shared Roster Groups")),
|
||||
Head = ?H1GL(PageTitle, <<"modules/#mod_shared_roster">>, <<"mod_shared_roster">>),
|
||||
Level = length(RPath),
|
||||
Res = case check_group_exists(Host, RPath) of
|
||||
true ->
|
||||
AddedMembers = NewMembers -- OldMembers,
|
||||
RemovedMembers = OldMembers -- NewMembers,
|
||||
lists:foreach(
|
||||
fun(US) ->
|
||||
remove_user_from_group(Host,
|
||||
US,
|
||||
Group)
|
||||
webadmin_page_backend(Host, RPath, R, Lang, Level);
|
||||
false ->
|
||||
[?XREST(<<"Group does not exist.">>)]
|
||||
end,
|
||||
RemovedMembers),
|
||||
NonAddedMembers = lists:filter(
|
||||
fun(US) ->
|
||||
error == add_user_to_group(Host, US,
|
||||
Group)
|
||||
end,
|
||||
AddedMembers),
|
||||
case (NonAddedMembers /= []) or (WrongDispGroups /= []) of
|
||||
true -> {error_elements, NonAddedMembers, WrongDispGroups};
|
||||
false -> ok
|
||||
end
|
||||
{stop, Head ++ Res};
|
||||
webadmin_page(Acc, _, _) ->
|
||||
Acc.
|
||||
|
||||
check_group_exists(Host, [<<"group">>, Id | _]) ->
|
||||
case get_group_opts(Host, Id) of
|
||||
error ->
|
||||
false;
|
||||
_ ->
|
||||
true
|
||||
end;
|
||||
_ -> nothing
|
||||
end.
|
||||
check_group_exists(_, _) ->
|
||||
true.
|
||||
|
||||
get_opt(Opts, Opt, Default) ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
{value, {_, Val}} -> Val;
|
||||
false -> Default
|
||||
end.
|
||||
%%---------------------
|
||||
%% Web Admin: Page Backend
|
||||
%%---------------------
|
||||
|
||||
us_to_list({User, Server}) ->
|
||||
jid:encode({User, Server, <<"">>}).
|
||||
webadmin_page_backend(Host, [<<"group">>, Id, <<"info">> | RPath], R, _Lang, Level) ->
|
||||
Breadcrumb =
|
||||
make_breadcrumb({group_section,
|
||||
Level,
|
||||
<<"Groups of ", Host/binary>>,
|
||||
Id,
|
||||
<<"Information">>,
|
||||
RPath}),
|
||||
SetLabel =
|
||||
make_command(srg_set_info,
|
||||
R,
|
||||
[{<<"host">>, Host}, {<<"group">>, Id}, {<<"key">>, <<"label">>}],
|
||||
[{only, without_presentation}, {input_name_append, [Id, Host, <<"label">>]}]),
|
||||
SetDescription =
|
||||
make_command(srg_set_info,
|
||||
R,
|
||||
[{<<"host">>, Host}, {<<"group">>, Id}, {<<"key">>, <<"description">>}],
|
||||
[{only, without_presentation},
|
||||
{input_name_append, [Id, Host, <<"description">>]}]),
|
||||
SetAll =
|
||||
make_command(srg_set_info,
|
||||
R,
|
||||
[{<<"host">>, Host},
|
||||
{<<"group">>, Id},
|
||||
{<<"key">>, <<"all_users">>},
|
||||
{<<"value">>, <<"true">>}],
|
||||
[{only, button},
|
||||
{input_name_append, [Id, Host, <<"all_users">>, <<"true">>]}]),
|
||||
UnsetAll =
|
||||
make_command(srg_set_info,
|
||||
R,
|
||||
[{<<"host">>, Host},
|
||||
{<<"group">>, Id},
|
||||
{<<"key">>, <<"all_users">>},
|
||||
{<<"value">>, <<"false">>}],
|
||||
[{only, button},
|
||||
{input_name_append, [Id, Host, <<"all_users">>, <<"false">>]}]),
|
||||
SetOnline =
|
||||
make_command(srg_set_info,
|
||||
R,
|
||||
[{<<"host">>, Host},
|
||||
{<<"group">>, Id},
|
||||
{<<"key">>, <<"online_users">>},
|
||||
{<<"value">>, <<"true">>}],
|
||||
[{only, button},
|
||||
{input_name_append, [Id, Host, <<"online_users">>, <<"true">>]}]),
|
||||
UnsetOnline =
|
||||
make_command(srg_set_info,
|
||||
R,
|
||||
[{<<"host">>, Host},
|
||||
{<<"group">>, Id},
|
||||
{<<"key">>, <<"online_users">>},
|
||||
{<<"value">>, <<"false">>}],
|
||||
[{only, button},
|
||||
{input_name_append, [Id, Host, <<"online_users">>, <<"false">>]}]),
|
||||
GetInfo =
|
||||
make_command_raw_value(srg_get_info, R, [{<<"group">>, Id}, {<<"host">>, Host}]),
|
||||
AllElement =
|
||||
case proplists:get_value(<<"all_users">>, GetInfo, not_found) of
|
||||
"true" ->
|
||||
{?C("Unset @all@: "), UnsetAll};
|
||||
_ ->
|
||||
{?C("Set @all@: "), SetAll}
|
||||
end,
|
||||
OnlineElement =
|
||||
case proplists:get_value(<<"online_users">>, GetInfo, not_found) of
|
||||
"true" ->
|
||||
{?C("Unset @online@: "), UnsetOnline};
|
||||
_ ->
|
||||
{?C("Set @online@: "), SetOnline}
|
||||
end,
|
||||
Types =
|
||||
[{?C("Set label: "), SetLabel},
|
||||
{?C("Set description: "), SetDescription},
|
||||
AllElement,
|
||||
OnlineElement],
|
||||
Get = [?BR,
|
||||
make_command(srg_get_info, R, [{<<"host">>, Host}, {<<"group">>, Id}], []),
|
||||
make_command(srg_set_info, R, [], [{only, presentation}]),
|
||||
make_table(20, [], [{<<"">>, right}, <<"">>], Types)],
|
||||
Breadcrumb ++ Get;
|
||||
webadmin_page_backend(Host,
|
||||
[<<"group">>, Id, <<"displayed">> | RPath],
|
||||
R,
|
||||
_Lang,
|
||||
Level) ->
|
||||
Breadcrumb =
|
||||
make_breadcrumb({group_section,
|
||||
Level,
|
||||
<<"Groups of ", Host/binary>>,
|
||||
Id,
|
||||
<<"Displayed Groups">>,
|
||||
RPath}),
|
||||
AddDisplayed =
|
||||
make_command(srg_add_displayed, R, [{<<"host">>, Host}, {<<"group">>, Id}], []),
|
||||
_ = make_webadmin_displayed_table(Host, Id, R),
|
||||
DisplayedTable = make_webadmin_displayed_table(Host, Id, R),
|
||||
Get = [?BR,
|
||||
make_command(srg_get_displayed, R, [], [{only, presentation}]),
|
||||
make_command(srg_del_displayed, R, [], [{only, presentation}]),
|
||||
?XE(<<"blockquote">>, [DisplayedTable]),
|
||||
AddDisplayed],
|
||||
Breadcrumb ++ Get;
|
||||
webadmin_page_backend(Host, [<<"group">>, Id, <<"members">> | RPath], R, _Lang, Level) ->
|
||||
Breadcrumb =
|
||||
make_breadcrumb({group_section,
|
||||
Level,
|
||||
<<"Groups of ", Host/binary>>,
|
||||
Id,
|
||||
<<"Members">>,
|
||||
RPath}),
|
||||
UserAdd = make_command(srg_user_add, R, [{<<"grouphost">>, Host}, {<<"group">>, Id}], []),
|
||||
_ = make_webadmin_members_table(Host, Id, R),
|
||||
MembersTable = make_webadmin_members_table(Host, Id, R),
|
||||
Get = [make_command(srg_get_members, R, [], [{only, presentation}]),
|
||||
make_command(srg_user_del, R, [], [{only, presentation}]),
|
||||
?XE(<<"blockquote">>, [MembersTable]),
|
||||
UserAdd],
|
||||
Breadcrumb ++ Get;
|
||||
webadmin_page_backend(Host, [<<"group">>, Id, <<"delete">> | RPath], R, _Lang, Level) ->
|
||||
Breadcrumb =
|
||||
make_breadcrumb({group_section,
|
||||
Level,
|
||||
<<"Groups of ", Host/binary>>,
|
||||
Id,
|
||||
<<"Delete">>,
|
||||
RPath}),
|
||||
Get = [make_command(srg_delete,
|
||||
R,
|
||||
[{<<"host">>, Host}, {<<"group">>, Id}],
|
||||
[{style, danger}])],
|
||||
Breadcrumb ++ Get;
|
||||
webadmin_page_backend(Host, [<<"group">>, Id | _RPath], _R, _Lang, Level) ->
|
||||
Breadcrumb = make_breadcrumb({group, Level, <<"Groups of ", Host/binary>>, Id}),
|
||||
MenuItems =
|
||||
[{<<"info/">>, <<"Information">>},
|
||||
{<<"members/">>, <<"Members">>},
|
||||
{<<"displayed/">>, <<"Displayed Groups">>},
|
||||
{<<"delete/">>, <<"Delete">>}],
|
||||
Get = [?XE(<<"ul">>, [?LI([?AC(MIU, MIN)]) || {MIU, MIN} <- MenuItems])],
|
||||
Breadcrumb ++ Get;
|
||||
webadmin_page_backend(Host, RPath, R, _Lang, Level) ->
|
||||
Breadcrumb = make_breadcrumb({groups, <<"Groups of ", Host/binary>>}),
|
||||
_ = make_webadmin_srg_table(Host, R, 3 + Level, RPath),
|
||||
Set = [make_command(srg_add, R, [{<<"host">>, Host}], []),
|
||||
make_command(srg_create, R, [{<<"host">>, Host}], [])],
|
||||
RV2 = make_webadmin_srg_table(Host, R, 3 + Level, RPath),
|
||||
Get = [make_command(srg_list, R, [{<<"host">>, Host}], [{only, presentation}]),
|
||||
make_command(srg_get_info, R, [{<<"host">>, Host}], [{only, presentation}]),
|
||||
make_command(srg_delete, R, [{<<"host">>, Host}], [{only, presentation}]),
|
||||
?XE(<<"blockquote">>, [RV2])],
|
||||
Breadcrumb ++ Get ++ Set.
|
||||
|
||||
%%---------------------
|
||||
%% Web Admin: Table Generation
|
||||
%%---------------------
|
||||
|
||||
make_webadmin_srg_table(Host, R, Level, RPath) ->
|
||||
Groups =
|
||||
case make_command_raw_value(srg_list, R, [{<<"host">>, Host}]) of
|
||||
Gs when is_list(Gs) ->
|
||||
Gs;
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
Columns =
|
||||
[<<"id">>,
|
||||
<<"label">>,
|
||||
<<"description">>,
|
||||
<<"all">>,
|
||||
<<"online">>,
|
||||
{<<"members">>, right},
|
||||
{<<"displayed">>, right},
|
||||
<<"">>],
|
||||
Rows =
|
||||
[{make_command(echo3,
|
||||
R,
|
||||
[{<<"first">>, Id}, {<<"second">>, Host}, {<<"sentence">>, Id}],
|
||||
[{only, value}, {result_links, [{sentence, shared_roster, Level, <<"">>}]}]),
|
||||
make_command(echo3,
|
||||
R,
|
||||
[{<<"first">>, Id},
|
||||
{<<"second">>, Host},
|
||||
{<<"sentence">>,
|
||||
iolist_to_binary(proplists:get_value(<<"label">>,
|
||||
make_command_raw_value(srg_get_info,
|
||||
R,
|
||||
[{<<"group">>,
|
||||
Id},
|
||||
{<<"host">>,
|
||||
Host}]),
|
||||
""))}],
|
||||
[{only, value},
|
||||
{result_links, [{sentence, shared_roster, Level, <<"info">>}]}]),
|
||||
make_command(echo3,
|
||||
R,
|
||||
[{<<"first">>, Id},
|
||||
{<<"second">>, Host},
|
||||
{<<"sentence">>,
|
||||
iolist_to_binary(proplists:get_value(<<"description">>,
|
||||
make_command_raw_value(srg_get_info,
|
||||
R,
|
||||
[{<<"group">>,
|
||||
Id},
|
||||
{<<"host">>,
|
||||
Host}]),
|
||||
""))}],
|
||||
[{only, value},
|
||||
{result_links, [{sentence, shared_roster, Level, <<"info">>}]}]),
|
||||
make_command(echo3,
|
||||
R,
|
||||
[{<<"first">>, Id},
|
||||
{<<"second">>, Host},
|
||||
{<<"sentence">>,
|
||||
iolist_to_binary(proplists:get_value(<<"all_users">>,
|
||||
make_command_raw_value(srg_get_info,
|
||||
R,
|
||||
[{<<"group">>,
|
||||
Id},
|
||||
{<<"host">>,
|
||||
Host}]),
|
||||
""))}],
|
||||
[{only, value},
|
||||
{result_links, [{sentence, shared_roster, Level, <<"info">>}]}]),
|
||||
make_command(echo3,
|
||||
R,
|
||||
[{<<"first">>, Id},
|
||||
{<<"second">>, Host},
|
||||
{<<"sentence">>,
|
||||
iolist_to_binary(proplists:get_value(<<"online_users">>,
|
||||
make_command_raw_value(srg_get_info,
|
||||
R,
|
||||
[{<<"group">>,
|
||||
Id},
|
||||
{<<"host">>,
|
||||
Host}]),
|
||||
""))}],
|
||||
[{only, value},
|
||||
{result_links, [{sentence, shared_roster, Level, <<"info">>}]}]),
|
||||
make_command(echo3,
|
||||
R,
|
||||
[{<<"first">>, Id},
|
||||
{<<"second">>, Host},
|
||||
{<<"sentence">>,
|
||||
integer_to_binary(length(make_command_raw_value(srg_get_members,
|
||||
R,
|
||||
[{<<"group">>, Id},
|
||||
{<<"host">>, Host}])))}],
|
||||
[{only, value},
|
||||
{result_links, [{sentence, shared_roster, Level, <<"members">>}]}]),
|
||||
make_command(echo3,
|
||||
R,
|
||||
[{<<"first">>, Id},
|
||||
{<<"second">>, Host},
|
||||
{<<"sentence">>,
|
||||
integer_to_binary(length(make_command_raw_value(srg_get_displayed,
|
||||
R,
|
||||
[{<<"group">>, Id},
|
||||
{<<"host">>, Host}])))}],
|
||||
[{only, value},
|
||||
{result_links, [{sentence, shared_roster, Level, <<"displayed">>}]}]),
|
||||
make_command(srg_delete,
|
||||
R,
|
||||
[{<<"group">>, Id}, {<<"host">>, Host}],
|
||||
[{only, button}, {style, danger}, {input_name_append, [Id, Host]}])}
|
||||
|| Id <- Groups],
|
||||
make_table(20, RPath, Columns, Rows).
|
||||
|
||||
make_webadmin_members_table(Host, Id, R) ->
|
||||
Members =
|
||||
case make_command_raw_value(srg_get_members, R, [{<<"host">>, Host}, {<<"group">>, Id}])
|
||||
of
|
||||
Ms when is_list(Ms) ->
|
||||
Ms;
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
make_table([<<"member">>, <<"">>],
|
||||
[{make_command(echo,
|
||||
R,
|
||||
[{<<"sentence">>, Jid}],
|
||||
[{only, value}, {result_links, [{sentence, user, 6, <<"">>}]}]),
|
||||
make_command(srg_user_del,
|
||||
R,
|
||||
[{<<"user">>,
|
||||
element(1,
|
||||
jid:split(
|
||||
jid:decode(Jid)))},
|
||||
{<<"host">>,
|
||||
element(2,
|
||||
jid:split(
|
||||
jid:decode(Jid)))},
|
||||
{<<"group">>, Id},
|
||||
{<<"grouphost">>, Host}],
|
||||
[{only, button},
|
||||
{style, danger},
|
||||
{input_name_append,
|
||||
[element(1,
|
||||
jid:split(
|
||||
jid:decode(Jid))),
|
||||
element(2,
|
||||
jid:split(
|
||||
jid:decode(Jid))),
|
||||
Id,
|
||||
Host]}])}
|
||||
|| Jid <- Members]).
|
||||
|
||||
make_webadmin_displayed_table(Host, Id, R) ->
|
||||
Displayed =
|
||||
case make_command_raw_value(srg_get_displayed, R, [{<<"host">>, Host}, {<<"group">>, Id}])
|
||||
of
|
||||
Ms when is_list(Ms) ->
|
||||
Ms;
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
make_table([<<"group">>, <<"">>],
|
||||
[{make_command(echo3,
|
||||
R,
|
||||
[{<<"first">>, ThisId},
|
||||
{<<"second">>, Host},
|
||||
{<<"sentence">>, ThisId}],
|
||||
[{only, value},
|
||||
{result_links, [{sentence, shared_roster, 6, <<"">>}]}]),
|
||||
make_command(srg_del_displayed,
|
||||
R,
|
||||
[{<<"group">>, Id}, {<<"host">>, Host}, {<<"del">>, ThisId}],
|
||||
[{only, button},
|
||||
{style, danger},
|
||||
{input_name_append, [Id, Host, ThisId]}])}
|
||||
|| ThisId <- Displayed]).
|
||||
|
||||
make_breadcrumb({groups, Service}) ->
|
||||
make_breadcrumb([Service]);
|
||||
make_breadcrumb({group, Level, Service, Name}) ->
|
||||
make_breadcrumb([{Level, Service}, separator, Name]);
|
||||
make_breadcrumb({group_section, Level, Service, Name, Section, RPath}) ->
|
||||
make_breadcrumb([{Level, Service}, separator, {Level - 2, Name}, separator, Section
|
||||
| RPath]);
|
||||
make_breadcrumb(Elements) ->
|
||||
lists:map(fun ({xmlel, _, _, _} = Xmlel) ->
|
||||
Xmlel;
|
||||
(<<"sort">>) ->
|
||||
?C(<<" +">>);
|
||||
(<<"page">>) ->
|
||||
?C(<<" #">>);
|
||||
(separator) ->
|
||||
?C(<<" > ">>);
|
||||
(Bin) when is_binary(Bin) ->
|
||||
?C(Bin);
|
||||
({Level, Bin}) when is_integer(Level) and is_binary(Bin) ->
|
||||
?AC(binary:copy(<<"../">>, Level), Bin)
|
||||
end,
|
||||
Elements).
|
||||
%% @format-end
|
||||
|
||||
split_grouphost(Host, Group) ->
|
||||
case str:tokens(Group, <<"@">>) of
|
||||
@ -1148,17 +1266,6 @@ split_grouphost(Host, Group) ->
|
||||
[_] -> {Host, Group}
|
||||
end.
|
||||
|
||||
filter_groups_existence(Host, Groups) ->
|
||||
lists:partition(
|
||||
fun(Group) -> error /= get_group_opts(Host, Group) end,
|
||||
Groups).
|
||||
|
||||
displayed_groups_update(Members, DisplayedGroups, Subscription) ->
|
||||
lists:foreach(
|
||||
fun({U, S}) ->
|
||||
push_displayed_to_user(U, S, S, Subscription, DisplayedGroups)
|
||||
end, Members).
|
||||
|
||||
opts_to_binary(Opts) ->
|
||||
lists:map(
|
||||
fun({label, Label}) ->
|
||||
|
Loading…
Reference in New Issue
Block a user