From 677d8b1a2972c3c4134aaf07780f2b16846e14be Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Feb 2011 22:33:23 +0100 Subject: [PATCH] Add support for '@online@' Shared Roster Group (thanks to Martin Langhoff)(EJAB-1391) New version of the @online@ patch originally by Collabora. Notes: - the presence push is mediated via the group rather than per user - this may reduce memory footprint... _if_ ejabberd has some smart optimisation in that codepath - it assumes that any group with membership @online@ _displays_ online as well -- this is a simplification and breaks the decoupling that ejabberd has in this regard. --- doc/guide.tex | 10 ++-- src/mod_shared_roster.erl | 113 +++++++++++++++++++++++++++++++++++--- 2 files changed, 111 insertions(+), 12 deletions(-) diff --git a/doc/guide.tex b/doc/guide.tex index e0bb42e4c..2bacd57c9 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -4055,11 +4055,13 @@ has a unique identification and the following parameters: \item[Name] The name of the group, which will be displayed in the roster. \item[Description] The description of the group. This parameter does not affect anything. -\item[Members] A list of full JIDs of group members, entered one per line in +\item[Members] A list of JIDs of group members, entered one per line in the Web Admin. - To put as members all the registered users in the virtual hosts, - you can use the special directive: @all@. - Note that this directive is designed for a small server with just a few hundred users. + The special member directive \term{@all@} + represents all the registered users in the virtual host; + which is only recommended for a small server with just a few hundred users. + The special member directive \term{@online@} + represents the online users in the virtual host. \item[Displayed groups] A list of groups that will be in the rosters of this group's members. \end{description} diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 13348cffb..14879cce0 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -37,6 +37,8 @@ process_item/2, in_subscription/6, out_subscription/4, + user_available/1, + unset_presence/4, register_user/2, remove_user/2, list_groups/1, @@ -85,6 +87,10 @@ start(Host, _Opts) -> ?MODULE, get_jid_info, 70), ejabberd_hooks:add(roster_process_item, Host, ?MODULE, process_item, 50), + ejabberd_hooks:add(user_available_hook, Host, + ?MODULE, user_available, 50), + ejabberd_hooks:add(unset_presence_hook, Host, + ?MODULE, unset_presence, 50), ejabberd_hooks:add(register_user, Host, ?MODULE, register_user, 50), ejabberd_hooks:add(remove_user, Host, @@ -109,6 +115,10 @@ stop(Host) -> ?MODULE, get_jid_info, 70), ejabberd_hooks:delete(roster_process_item, Host, ?MODULE, process_item, 50), + ejabberd_hooks:delete(user_available_hook, Host, + ?MODULE, user_available, 50), + ejabberd_hooks:delete(unset_presence_hook, Host, + ?MODULE, unset_presence, 50), ejabberd_hooks:delete(register_user, Host, ?MODULE, register_user, 50), ejabberd_hooks:delete(remove_user, Host, @@ -470,21 +480,38 @@ get_group_opt(Host, Group, Opt, Default) -> Default end. +get_online_users(Host) -> + lists:usort([{U, S} || {U, S, _} <- ejabberd_sm:get_vh_session_list(Host)]). + get_group_users(Host, Group) -> case get_group_opt(Host, Group, all_users, false) of true -> ejabberd_auth:get_vh_registered_users(Host); false -> [] - end ++ get_group_explicit_users(Host, Group). + end ++ + case get_group_opt(Host, Group, online_users, false) of + true -> + get_online_users(Host); + false -> + [] + end ++ + get_group_explicit_users(Host, Group). -get_group_users(_User, Host, Group, GroupOpts) -> +get_group_users(Host, Group, GroupOpts) -> case proplists:get_value(all_users, GroupOpts, false) of true -> ejabberd_auth:get_vh_registered_users(Host); false -> [] - end ++ get_group_explicit_users(Host, Group). + end ++ + case proplists:get_value(online_users, GroupOpts, false) of + true -> + get_online_users(Host); + false -> + [] + end ++ + get_group_explicit_users(Host, Group). %% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}] get_group_explicit_users(Host, Group) -> @@ -502,11 +529,20 @@ get_group_explicit_users(Host, Group) -> get_group_name(Host, Group) -> get_group_opt(Host, Group, name, Group). -%% Get list of names of groups that have @all@ in the memberlist +%% Get list of names of groups that have @all@/@online@/etc in the memberlist get_special_users_groups(Host) -> lists:filter( fun(Group) -> get_group_opt(Host, Group, all_users, false) + orelse get_group_opt(Host, Group, online_users, false) + end, + list_groups(Host)). + +%% Get list of names of groups that have @online@ in the memberlist +get_special_users_groups_online(Host) -> + lists:filter( + fun(Group) -> + get_group_opt(Host, Group, online_users, false) end, list_groups(Host)). @@ -661,7 +697,7 @@ push_user_to_members(User, Server, Subscription) -> lists:foreach( fun({U, S}) -> push_roster_item(U, S, LUser, LServer, GroupName, Subscription) - end, get_group_users(LUser, LServer, Group, GroupOpts)) + end, get_group_users(LServer, Group, GroupOpts)) end, lists:usort(SpecialGroups++UserGroups)). push_user_to_displayed(LUser, LServer, Group, Subscription) -> @@ -673,7 +709,8 @@ push_user_to_displayed(LUser, LServer, Group, Subscription) -> push_user_to_group(LUser, LServer, Group, GroupName, Subscription) -> lists:foreach( - fun({U, S}) -> + fun({U, S}) when (U == LUser) and (S == LServer) -> ok; + ({U, S}) -> push_roster_item(U, S, LUser, LServer, GroupName, Subscription) end, get_group_users(LServer, Group)). @@ -757,6 +794,51 @@ ask_to_pending(subscribe) -> out; ask_to_pending(unsubscribe) -> none; ask_to_pending(Ask) -> Ask. +user_available(New) -> + LUser = New#jid.luser, + LServer = New#jid.lserver, + Resources = ejabberd_sm:get_user_resources(LUser, LServer), + ?DEBUG("user_available for ~p @ ~p (~p resources)", + [LUser, LServer, length(Resources)]), + case length(Resources) of + %% first session for this user + 1 -> + %% This is a simplification - we ignore he 'display' + %% property - @online@ is always reflective. + OnlineGroups = get_special_users_groups_online(LServer), + lists:foreach( + fun(OG) -> + ?DEBUG("user_available: pushing ~p @ ~p grp ~p", + [LUser, LServer, OG ]), + push_user_to_displayed(LUser, LServer, OG, both) + end, OnlineGroups); + _ -> + ok + end. + +unset_presence(LUser, LServer, Resource, Status) -> + Resources = ejabberd_sm:get_user_resources(LUser, LServer), + ?DEBUG("unset_presence for ~p @ ~p / ~p -> ~p (~p resources)", + [LUser, LServer, Resource, Status, length(Resources)]), + %% if user has no resources left... + case length(Resources) of + 0 -> + %% This is a simplification - we ignore he 'display' + %% property - @online@ is always reflective. + OnlineGroups = get_special_users_groups_online(LServer), + %% for each of these groups... + lists:foreach( + fun(OG) -> + %% Push removal of the old user to members of groups + %% where the group that this uwas members was displayed + push_user_to_displayed(LUser, LServer, OG, remove), + %% Push removal of members of groups that where + %% displayed to the group which thiuser has left + push_displayed_to_user(LUser, LServer, OG, LServer,remove) + end, OnlineGroups); + _ -> + ok + end. %%--------------------- %% Web Admin @@ -860,6 +942,7 @@ shared_roster_group(Host, Group, Query, Lang) -> Name = get_opt(GroupOpts, name, ""), Description = get_opt(GroupOpts, description, ""), AllUsers = get_opt(GroupOpts, all_users, false), + OnlineUsers = get_opt(GroupOpts, online_users, false), %%Disabled = false, DisplayedGroups = get_opt(GroupOpts, displayed_groups, []), Members = mod_shared_roster:get_group_explicit_users(Host, Group), @@ -869,7 +952,14 @@ shared_roster_group(Host, Group, Query, Lang) -> "@all@\n"; true -> [] - end ++ [[us_to_list(Member), $\n] || Member <- Members], + end ++ + if + OnlineUsers -> + "@online@\n"; + true -> + [] + end ++ + [[us_to_list(Member), $\n] || Member <- Members], FDisplayedGroups = [[DG, $\n] || DG <- DisplayedGroups], DescNL = length(element(2, regexp:split(Description, "\n"))), FGroup = @@ -953,6 +1043,8 @@ shared_roster_group_parse_query(Host, Group, Query) -> case SJID of "@all@" -> USs; + "@online@" -> + USs; _ -> case jlib:string_to_jid(SJID) of JID when is_record(JID, jid) -> @@ -967,10 +1059,15 @@ shared_roster_group_parse_query(Host, Group, Query) -> true -> [{all_users, true}]; false -> [] end, + OnlineUsersOpt = + case lists:member("@online@", SJIDs) of + true -> [{online_users, true}]; + false -> [] + end, mod_shared_roster:set_group_opts( Host, Group, - NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt), + NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt ++ OnlineUsersOpt), if NewMembers == error -> error;