[Server-devel] [PATCH] mod_shared_roster: simplified @online@ patch
martin.langhoff at gmail.com
martin.langhoff at gmail.com
Mon Feb 23 01:29:04 EST 2009
From: Martin Langhoff <martin at laptop.org>
New version of the @online@ patch originally by Collabora.
With this patch, @online@ works 100% - @recent@ and @nearby@ are
_broken_ however. The author of this patch doubts they ever
worked reliably.
Notes:
- fixed a typo in is_user_in_group
- simplified user_available and unset_presence hook handlers
- 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.
---
src/mod_shared_roster.erl | 248 +++++++++++++++++++++++++++++++++++++++++---
1 files changed, 231 insertions(+), 17 deletions(-)
diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl
index af123c2..d1b169d 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,
@@ -45,7 +47,7 @@
delete_group/2,
get_group_opts/2,
set_group_opts/3,
- get_group_users/2,
+ get_group_users/3,
get_group_explicit_users/2,
is_user_in_group/3,
add_user_to_group/3,
@@ -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,
@@ -131,7 +141,7 @@ get_user_roster(Items, US) ->
get_group_name(S, Group),
Acc2)
end
- end, Acc1, get_group_users(S, Group))
+ end, Acc1, get_group_users(U, S, Group))
end, dict:new(), DisplayedGroups),
%% If partially subscribed users are also in shared roster, show them as
@@ -267,7 +277,7 @@ get_subscription_lists({F, T}, User, Server) ->
lists:usort(
lists:flatmap(
fun(Group) ->
- get_group_users(LServer, Group)
+ get_group_users(LUser, LServer, Group)
end, DisplayedGroups)),
SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers],
{lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
@@ -286,7 +296,7 @@ get_jid_info({Subscription, Groups}, User, Server, JID) ->
fun(User1, Acc2) ->
dict:append(
User1, get_group_name(LServer, Group), Acc2)
- end, Acc1, get_group_users(LServer, Group))
+ end, Acc1, get_group_users(LUser, LServer, Group))
end, dict:new(), DisplayedGroups),
case dict:find(US1, SRUsers) of
{ok, GroupNames} ->
@@ -316,7 +326,7 @@ process_subscription(Direction, User, Server, JID, _Type, Acc) ->
lists:usort(
lists:flatmap(
fun(Group) ->
- get_group_users(LServer, Group)
+ get_group_users(LUser, LServer, Group)
end, DisplayedGroups)),
case lists:member(US1, SRUsers) of
true ->
@@ -414,21 +424,103 @@ get_group_opt(Host, Group, Opt, Default) ->
false
end.
-get_group_users(Host, Group) ->
+-record(last_activity, {us, timestamp, status}).
+-record(session, {sid, usr, us, priority, info}).
+
+get_recent_users(Host, Days) ->
+ LServer = jlib:nameprep(Host),
+ %% Convert older time
+ SecOlder = Days*24*60*60,
+
+ %% Get current time
+ {MegaSecs, Secs, _MicroSecs} = now(),
+ TimeStampOlder = MegaSecs * 1000000 + Secs - SecOlder,
+
+ %% Get the list of recently connected users
+ RecentUsers = mnesia:dirty_select(last_activity,
+ [{#last_activity{us={'$1',LServer},timestamp='$2',_='_'},
+ [{'>','$2',TimeStampOlder}],
+ ['$1'] }]),
+ %% Get the list of connected users
+ OnlineUsers = mnesia:dirty_select(session,
+ [{#session{us={'$1',LServer}, _='_'},
+ [],
+ ['$1']}]),
+
+ Users = lists:usort(RecentUsers++OnlineUsers),
+ lists:map(fun(User) -> {User, LServer} end, Users).
+
+get_online_users(Host) ->
+ lists:usort([{U, S} || {U, S, _} <- ejabberd_sm:get_vh_session_list(Host)]).
+
+get_nearby_users(User, Host) ->
+ Resources = ejabberd_sm:get_user_resources(User, Host),
+ Sessions = ejabberd_sm:get_vh_session_list(Host),
+ Nearby = lists:foldl(
+ fun(Resource, Acc1) ->
+ lists:foldl(
+ fun({U, S, R}, Acc2) ->
+ if
+ R == Resource -> {U, S} ++ Acc2;
+ true -> Acc2
+ end
+ end, [], Sessions) ++ Acc1
+ end, [], Resources),
+ lists:usort(Nearby).
+
+get_group_users(User, 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).
-
-get_group_users(_User, Host, Group, GroupOpts) ->
+ end ++
+ case get_group_opt(Host, Group, recent_users_days, 0) of
+ 0 ->
+ [];
+ Days when is_integer(Days) ->
+ get_recent_users(Host, Days)
+ end ++
+ case get_group_opt(Host, Group, online_users, false) of
+ true ->
+ get_online_users(Host);
+ false ->
+ []
+ end ++
+ case get_group_opt(Host, Group, nearby_users, false) of
+ true ->
+ get_nearby_users(User, Host);
+ false ->
+ []
+ end ++
+ get_group_explicit_users(Host, Group).
+
+get_group_users(User, 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(recent_users_days, GroupOpts, 0) of
+ 0 ->
+ [];
+ Days when is_integer(Days) ->
+ get_recent_users(Host, Days)
+ end ++
+ case proplists:get_value(online_users, GroupOpts, false) of
+ true ->
+ get_online_users(Host);
+ false ->
+ []
+ end ++
+ case proplists:get_value(nearby_users, GroupOpts, false) of
+ true ->
+ get_nearby_users(User, Host);
+ false ->
+ []
+ end ++
+ get_group_explicit_users(Host, Group).
%% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
get_group_explicit_users(Host, Group) ->
@@ -446,11 +538,22 @@ 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)
+ get_group_opt(Host, Group, all_users, false) orelse
+ get_group_opt(Host, Group, recent_users_days, 0) > 0 orelse
+ get_group_opt(Host, Group, online_users, false) orelse
+ get_group_opt(Host, Group, nearby_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)).
@@ -509,7 +612,7 @@ get_user_displayed_groups(US) ->
is_user_in_group({_U, S} = US, Group, Host) ->
case catch mnesia:dirty_match_object(
#sr_user{us=US, group_host={Group, Host}}) of
- [] -> lists:member(US, get_group_users(S, Group));
+ [] -> lists:member(US, get_group_users(_U, S, Group));
_ -> true
end.
@@ -551,7 +654,7 @@ push_members_to_user(LUser, LServer, Group, Host, Subscription) ->
GroupsOpts = groups_with_opts(LServer),
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
GroupName = proplists:get_value(name, GroupOpts, Group),
- Members = get_group_users(Host, Group),
+ Members = get_group_users(LUser, Host, Group),
lists:foreach(
fun({U, S}) ->
push_roster_item(LUser, LServer, U, S, GroupName, Subscription)
@@ -593,7 +696,7 @@ push_user_to_group(LUser, LServer, Group, GroupName, Subscription) ->
lists:foreach(
fun({U, S}) ->
push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
- end, get_group_users(LServer, Group)).
+ end, get_group_users(LUser, LServer, Group)).
%% Get list of groups to which this group is displayed
displayed_to_groups(GroupName, LServer) ->
@@ -675,6 +778,72 @@ ask_to_pending(subscribe) -> out;
ask_to_pending(unsubscribe) -> none;
ask_to_pending(Ask) -> Ask.
+%% get a roster item for a contact from a particular user's
+%% perspective, considering both normal and shared roster items
+%% FIXME: is there a more efficient way to do this?
+get_user_roster_item(FromUS, ToUS) ->
+ {FUser, FServer} = FromUS,
+ case catch ejabberd_hooks:run_fold(roster_get, FServer, [], [ToUS]) of
+ Items when is_list(Items) ->
+ case [I || I <- Items, I#roster.jid == {FUser, FServer, []}] of
+ [Item | _ ] ->
+ Item;
+ [] ->
+ false
+ end;
+ _ ->
+ error
+ end.
+
+user_available(New) ->
+ LUser = New#jid.luser,
+ LServer = New#jid.lserver,
+ Resources = ejabberd_sm:get_user_resources(LUser, LServer),
+ ?INFO_MSG("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) ->
+ ?INFO_MSG("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),
+ ?INFO_MSG("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 user was members was displayed
+ push_user_to_displayed(LUser, LServer, OG, remove),
+ %% Push removal of members of groups that where displayed to the group which this user has left
+ push_displayed_to_user(LUser, LServer, OG, LServer, remove)
+ end, OnlineGroups);
+ _ ->
+ ok
+ end.
%%---------------------
%% Web Admin
@@ -778,6 +947,9 @@ shared_roster_group(Host, Group, Query, Lang) ->
Name = get_opt(GroupOpts, name, ""),
Description = get_opt(GroupOpts, description, ""),
AllUsers = get_opt(GroupOpts, all_users, false),
+ RecentUsersDays = get_opt(GroupOpts, recent_users_days, 0),
+ OnlineUsers = get_opt(GroupOpts, online_users, false),
+ NearbyUsers = get_opt(GroupOpts, nearby_users, false),
%%Disabled = false,
DisplayedGroups = get_opt(GroupOpts, displayed_groups, []),
Members = mod_shared_roster:get_group_explicit_users(Host, Group),
@@ -787,7 +959,26 @@ shared_roster_group(Host, Group, Query, Lang) ->
"@all@\n";
true ->
[]
- end ++ [[us_to_list(Member), $\n] || Member <- Members],
+ end ++
+ if
+ RecentUsersDays > 0 ->
+ "@recent@\n";
+ true ->
+ []
+ end ++
+ if
+ OnlineUsers ->
+ "@online@\n";
+ true ->
+ []
+ end ++
+ if
+ NearbyUsers ->
+ "@nearby@\n";
+ true ->
+ []
+ end ++
+ [[us_to_list(Member), $\n] || Member <- Members],
FDisplayedGroups = [[DG, $\n] || DG <- DisplayedGroups],
FGroup =
?XAE("table", [],
@@ -870,6 +1061,12 @@ shared_roster_group_parse_query(Host, Group, Query) ->
case SJID of
"@all@" ->
USs;
+ "@recent@" ->
+ USs;
+ "@online@" ->
+ USs;
+ "@nearby@" ->
+ USs;
_ ->
case jlib:string_to_jid(SJID) of
JID when is_record(JID, jid) ->
@@ -884,10 +1081,27 @@ shared_roster_group_parse_query(Host, Group, Query) ->
true -> [{all_users, true}];
false -> []
end,
+ %% FIXME: this should be specified by the user
+ RecentUsersOpt =
+ case lists:member("@recent@", SJIDs) of
+ true -> [{recent_users_days, 7}];
+ false -> []
+ end,
+ OnlineUsersOpt =
+ case lists:member("@online@", SJIDs) of
+ true -> [{online_users, true}];
+ false -> []
+ end,
+ NearbyUsersOpt =
+ case lists:member("@nearby@", SJIDs) of
+ true -> [{nearby_users, true}];
+ false -> []
+ end,
mod_shared_roster:set_group_opts(
Host, Group,
- NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt),
+ NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt
+ ++ RecentUsersOpt ++ OnlineUsersOpt ++ NearbyUsersOpt),
if
NewMembers == error -> error;
--
1.5.6.6
More information about the Server-devel
mailing list