Hi list, hi Badlop! In the process of building a new version of OLPC's School Server, I am trying to solve the last patch we have that isn't in your tree. (Thanks for all your help getting earlier patches merged! Also, happy that you liked them :-) )
The patch implements @online@ -- and in the past it has been discussed and rejected or delayed because it didn't implement @recent@ or @nearby@ correctly. This was discussed back in https://support.process-one.net/browse/EJAB-456 http://lists.jabber.ru/pipermail/ejabberd/2009-April/004901.html In this updated version of the patch I have *dropped* @recent@ and @nearby@ components, so we only have the part of the patch that... _works_ and is actually used. I am testing this patch at the moment, but I generally expect to confirm it works as expected in a few days. Background: OLPC makes extensive use of @online@ for small and autoconfigured school servers. Anything larger than ~100 users, it only makes sense to actually organize users in groups (as per their classrooms) -- we do this via Moodle, which then manages SRGs calling ejabberdctl calls. In practice, we have found that those two modes (@online@ and managed SRGs) fully cover our use cases. We don't use, and we don't think we need @recent@ or @nearby@. Those parts of the earlier patches have unfortunately never worked. Review? Merge? Scream? cheers, m -- martin.langh...@gmail.com mar...@laptop.org -- School Server Architect - ask interesting questions - don't get distracted with shiny stuff - working code first - http://wiki.laptop.org/go/User:Martinlanghoff
From f98185d4da9ed34df7e5e3bf7f0b8e4b1b169e6c Mon Sep 17 00:00:00 2001 From: Martin Langhoff <mar...@laptop.org> Date: Mon, 24 Jan 2011 17:55:22 -0500 Subject: [PATCH] New version of the @online@ patch originally by Collabora. 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 | 154 ++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 137 insertions(+), 17 deletions(-) diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 64a8291..2f23201 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, @@ -132,7 +142,7 @@ get_user_roster(Items, US) -> GroupName, 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 @@ -310,7 +320,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)}. @@ -329,7 +339,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} -> @@ -371,7 +381,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 -> @@ -470,21 +480,41 @@ get_group_opt(Host, Group, Opt, Default) -> Default end. -get_group_users(Host, Group) -> +-record(last_activity, {us, timestamp, status}). +-record(session, {sid, usr, us, priority, info}). + +get_online_users(Host) -> + lists:usort([{U, S} || {U, S, _} <- ejabberd_sm:get_vh_session_list(Host)]). + +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, online_users, false) of + true -> + get_online_users(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(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 +532,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) + 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)). @@ -565,7 +604,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. @@ -632,7 +671,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) @@ -675,7 +714,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) -> @@ -757,6 +796,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 @@ -860,6 +965,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 +975,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 +1066,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 +1082,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; -- 1.7.3.4
_______________________________________________ Server-devel mailing list Server-devel@lists.laptop.org http://lists.laptop.org/listinfo/server-devel