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.
This commit is contained in:
Badlop 2011-02-21 22:33:23 +01:00
parent 850218c2df
commit 677d8b1a29
2 changed files with 111 additions and 12 deletions

View File

@ -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}

View File

@ -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;