diff --git a/src/mod_pubsub/Makefile.win32 b/src/mod_pubsub/Makefile.win32 index f981329c9..d3cdcc499 100644 --- a/src/mod_pubsub/Makefile.win32 +++ b/src/mod_pubsub/Makefile.win32 @@ -4,7 +4,7 @@ include ..\Makefile.inc EFLAGS = -I .. -pz .. OUTDIR = .. -BEAMS = ..\gen_pubsub_node.beam ..\gen_pubsub_nodetree.beam ..\mod_pubsub.beam ..\nodetree_default.beam ..\nodetree_virtual.beam ..\node_buddy.beam ..\node_club.beam ..\node_default.beam ..\node_dispatch.beam ..\node_pep.beam ..\node_private.beam ..\node_public.beam +BEAMS = ..\gen_pubsub_node.beam ..\gen_pubsub_nodetree.beam ..\mod_pubsub.beam ..\nodetree_tree.beam ..\nodetree_virtual.beam ..\node_buddy.beam ..\node_club.beam ..\node_hometree.beam ..\node_dispatch.beam ..\node_pep.beam ..\node_private.beam ..\node_public.beam ALL : $(BEAMS) @@ -20,8 +20,8 @@ $(OUTDIR)\gen_pubsub_nodetree.beam : gen_pubsub_nodetree.erl $(OUTDIR)\mod_pubsub.beam : mod_pubsub.erl erlc -W $(EFLAGS) -o $(OUTDIR) mod_pubsub.erl -$(OUTDIR)\nodetree_default.beam : nodetree_default.erl - erlc -W $(EFLAGS) -o $(OUTDIR) nodetree_default.erl +$(OUTDIR)\nodetree_tree.beam : nodetree_tree.erl + erlc -W $(EFLAGS) -o $(OUTDIR) nodetree_tree.erl $(OUTDIR)\nodetree_virtual.beam : nodetree_virtual.erl erlc -W $(EFLAGS) -o $(OUTDIR) nodetree_virtual.erl @@ -32,8 +32,8 @@ $(OUTDIR)\node_buddy.beam : node_buddy.erl $(OUTDIR)\node_club.beam : node_club.erl erlc -W $(EFLAGS) -o $(OUTDIR) node_club.erl -$(OUTDIR)\node_default.beam : node_default.erl - erlc -W $(EFLAGS) -o $(OUTDIR) node_default.erl +$(OUTDIR)\node_hometree.beam : node_hometree.erl + erlc -W $(EFLAGS) -o $(OUTDIR) node_hometree.erl $(OUTDIR)\node_dispatch.beam : node_dispatch.erl erlc -W $(EFLAGS) -o $(OUTDIR) node_dispatch.erl diff --git a/src/mod_pubsub/gen_pubsub_nodetree.erl b/src/mod_pubsub/gen_pubsub_nodetree.erl index f012b3d0b..f55694500 100644 --- a/src/mod_pubsub/gen_pubsub_nodetree.erl +++ b/src/mod_pubsub/gen_pubsub_nodetree.erl @@ -47,6 +47,8 @@ behaviour_info(callbacks) -> {get_node, 1}, {get_nodes, 2}, {get_nodes, 1}, + {get_parentnodes, 3}, + {get_parentnodes_tree, 3}, {get_subnodes, 3}, {get_subnodes_tree, 3}, {create_node, 5}, diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index af3f76f62..b36d1a3ff 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -80,7 +80,7 @@ get_items/2, get_item/3, get_cached_item/2, - broadcast_stanza/7, + broadcast_stanza/8, get_configure/5, set_configure/5, tree_action/3, @@ -304,7 +304,7 @@ update_node_database(Host, ServerHost) -> mnesia:delete({pubsub_node, NodeId}), {[#pubsub_node{nodeid = NodeId, id = NodeIdx, - parent = element(2, ParentId), + parents = [element(2, ParentId)], owners = Owners, options = Options} | RecList], NodeIdx + 1} @@ -334,12 +334,12 @@ update_node_database(Host, ServerHost) -> #pubsub_node{ nodeid = NodeId, id = 0, - parent = Parent, + parents = [Parent], type = Type, owners = Owners, options = Options} end, - mnesia:transform_table(pubsub_node, F, [nodeid, id, parent, type, owners, options]), + mnesia:transform_table(pubsub_node, F, [nodeid, id, parents, type, owners, options]), FNew = fun() -> lists:foldl(fun(#pubsub_node{nodeid = NodeId} = PubsubNode, NodeIdx) -> mnesia:write(PubsubNode#pubsub_node{id = NodeIdx}), @@ -371,6 +371,17 @@ update_node_database(Host, ServerHost) -> ?ERROR_MSG("Problem updating Pubsub node tables:~n~p", [Reason]) end; + [nodeid, id, parent, type, owners, options] -> + F = fun({pubsub_node, NodeId, Id, Parent, Type, Owners, Options}) -> + #pubsub_node{ + nodeid = NodeId, + id = Id, + parents = [Parent], + type = Type, + owners = Owners, + options = Options} + end, + mnesia:transform_table(pubsub_node, F, [nodeid, id, parents, type, owners, options]); _ -> ok end. @@ -1443,6 +1454,12 @@ replace_subscription_helper(_, OldSub, Acc) -> -define(STRINGXFIELD(Label, Var, Val), ?XFIELD("text-single", Label, Var, Val)). +-define(STRINGMXFIELD(Label, Var, Vals), + {xmlelement, "field", [{"type", "text-multi"}, + {"label", translate:translate(Lang, Label)}, + {"var", Var}], + [{xmlelement, "value", [], [{xmlcdata, V}]} || V <- Vals]}). + -define(XFIELDOPT(Type, Label, Var, Val, Opts), {xmlelement, "field", [{"type", Type}, {"label", translate:translate(Lang, Label)}, @@ -1601,10 +1618,14 @@ delete_node(_Host, [], _Owner) -> {error, ?ERR_NOT_ALLOWED}; delete_node(Host, Node, Owner) -> Action = fun(#pubsub_node{type = Type, id = NodeId}) -> + SubsByDepth = get_collection_subscriptions(Host, Node), case node_call(Type, get_affiliation, [NodeId, Owner]) of {result, owner} -> Removed = tree_call(Host, delete_node, [Host, Node]), - node_call(Type, delete_node, [Removed]); + case node_call(Type, delete_node, [Removed]) of + {result, Res} -> {result, {SubsByDepth, Res}}; + Error -> Error + end; _ -> %% Entity is not an owner {error, ?ERR_FORBIDDEN} @@ -1612,27 +1633,26 @@ delete_node(Host, Node, Owner) -> end, Reply = [], case transaction(Host, Node, Action, transaction) of - {result, {_, {Result, broadcast, Removed}}} -> - lists:foreach(fun({RNode, RSubscriptions}) -> + {result, {_, {SubsByDepth, {Result, broadcast, Removed}}}} -> + lists:foreach(fun({RNode, _RSubscriptions}) -> {RH, RN} = RNode#pubsub_node.nodeid, NodeId = RNode#pubsub_node.id, Type = RNode#pubsub_node.type, Options = RNode#pubsub_node.options, - broadcast_removed_node(RH, RN, NodeId, Type, Options, RSubscriptions), - unset_cached_item(RH, NodeId) + broadcast_removed_node(RH, RN, NodeId, Type, Options, SubsByDepth) end, Removed), case Result of default -> {result, Reply}; _ -> {result, Result} end; - {result, {_, {Result, _Removed}}} -> + {result, {_, {_, {Result, _Removed}}}} -> case Result of default -> {result, Reply}; _ -> {result, Result} end; - {result, {_, default}} -> + {result, {_, {_, default}}} -> {result, Reply}; - {result, {_, Result}} -> + {result, {_, {_, Result}}} -> {result, Result}; Error -> Error @@ -2605,19 +2625,28 @@ service_jid(Host) -> %% @spec (LJID, PresenceDelivery) -> boolean() %% LJID = jid() +%% NotifyType = items | nodes +%% Depth = integer() %% NodeOptions = [{atom(), term()}] %% SubOptions = [{atom(), term()}] %% @doc
Check if a notification must be delivered or not based on %% node and subscription options.
-is_to_deliver(LJID, NodeOptions, SubOptions) -> - sub_to_deliver(LJID, SubOptions) andalso node_to_deliver(LJID, NodeOptions). +is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) -> + sub_to_deliver(LJID, NotifyType, Depth, SubOptions) + andalso node_to_deliver(LJID, NodeOptions). -sub_to_deliver(_LJID, SubOptions) -> - lists:all(fun sub_option_can_deliver/1, SubOptions). +sub_to_deliver(_LJID, NotifyType, Depth, SubOptions) -> + lists:all(fun (Option) -> + sub_option_can_deliver(NotifyType, Depth, Option) + end, SubOptions). -sub_option_can_deliver({deliver, false}) -> false; -sub_option_can_deliver({expire, When}) -> now() < When; -sub_option_can_deliver(_) -> true. +sub_option_can_deliver(items, _, {subscription_type, nodes}) -> false; +sub_option_can_deliver(nodes, _, {subscription_type, items}) -> false; +sub_option_can_deliver(_, _, {subscription_depth, all}) -> true; +sub_option_can_deliver(_, Depth, {subscription_depth, D}) -> Depth < D; +sub_option_can_deliver(_, _, {deliver, false}) -> false; +sub_option_can_deliver(_, _, {expire, When}) -> now() < When; +sub_option_can_deliver(_, _, _) -> true. node_to_deliver(LJID, NodeOptions) -> PresenceDelivery = get_option(NodeOptions, presence_based_delivery), @@ -2652,11 +2681,8 @@ event_stanza(Els) -> broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, _From, Payload) -> %broadcast(Host, Node, NodeId, NodeOptions, none, true, "items", ItemEls) - case node_action(Host, Type, get_node_subscriptions, [NodeId]) of - {result, []} -> - {result, false}; - {result, Subs} -> - SubOptions = get_options_for_subs(Host, Node, NodeId, Subs), + case get_collection_subscriptions(Host, Node) of + SubsByDepth when is_list(SubsByDepth) -> Content = case get_option(NodeOptions, deliver_payloads) of true -> Payload; false -> [] @@ -2665,7 +2691,7 @@ broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, _ [{xmlelement, "items", nodeAttr(Node), [{xmlelement, "item", itemAttr(ItemId), Content}]}]), broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubOptions, Stanza), + NodeOptions, SubsByDepth, items, Stanza), case Removed of [] -> ok; @@ -2676,8 +2702,8 @@ broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, _ [{xmlelement, "items", nodeAttr(Node), [{xmlelement, "retract", itemAttr(RId), []} || RId <- Removed]}]), broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubOptions, - RetractStanza); + NodeOptions, SubsByDepth, + items, RetractStanza); _ -> ok end @@ -2695,16 +2721,13 @@ broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, ForceNot %broadcast(Host, Node, NodeId, NodeOptions, notify_retract, ForceNotify, "retract", RetractEls) case (get_option(NodeOptions, notify_retract) or ForceNotify) of true -> - case node_action(Host, Type, get_node_subscriptions, [NodeId]) of - {result, []} -> - {result, false}; - {result, Subs} -> - SubOptions = get_options_for_subs(Host, Node, NodeId, Subs), + case get_collection_subscriptions(Host, Node) of + SubsByDepth when is_list(SubsByDepth) -> Stanza = event_stanza( [{xmlelement, "items", nodeAttr(Node), [{xmlelement, "retract", itemAttr(ItemId), []} || ItemId <- ItemIds]}]), broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubOptions, Stanza), + NodeOptions, SubsByDepth, items, Stanza), {result, true}; _ -> {result, false} @@ -2717,16 +2740,13 @@ broadcast_purge_node(Host, Node, NodeId, Type, NodeOptions) -> %broadcast(Host, Node, NodeId, NodeOptions, notify_retract, false, "purge", []) case get_option(NodeOptions, notify_retract) of true -> - case node_action(Host, Type, get_node_subscriptions, [NodeId]) of - {result, []} -> - {result, false}; - {result, Subs} -> - SubOptions = get_options_for_subs(Host, Node, NodeId, Subs), + case get_collection_subscriptions(Host, Node) of + SubsByDepth when is_list(SubsByDepth) -> Stanza = event_stanza( [{xmlelement, "purge", nodeAttr(Node), []}]), broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubOptions, Stanza), + NodeOptions, SubsByDepth, nodes, Stanza), {result, true}; _ -> {result, false} @@ -2735,20 +2755,19 @@ broadcast_purge_node(Host, Node, NodeId, Type, NodeOptions) -> {result, false} end. -broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions, Subs) -> +broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) -> %broadcast(Host, Node, NodeId, NodeOptions, notify_delete, false, "delete", []) case get_option(NodeOptions, notify_delete) of true -> - case Subs of + case SubsByDepth of [] -> {result, false}; _ -> - SubOptions = get_options_for_subs(Host, Node, NodeId, Subs), Stanza = event_stanza( [{xmlelement, "delete", nodeAttr(Node), []}]), broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubOptions, Stanza), + NodeOptions, SubsByDepth, nodes, Stanza), {result, true} end; _ -> @@ -2759,11 +2778,8 @@ broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) -> %broadcast(Host, Node, NodeId, NodeOptions, notify_config, false, "items", ConfigEls) case get_option(NodeOptions, notify_config) of true -> - case node_action(Host, Type, get_node_subscriptions, [NodeId]) of - {result, []} -> - {result, false}; - {result, Subs} -> - SubOptions = get_options_for_subs(Host, Node, NodeId, Subs), + case get_collection_subscriptions(Host, Node) of + SubsByDepth when is_list(SubsByDepth) -> Content = case get_option(NodeOptions, deliver_payloads) of true -> [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}], @@ -2775,7 +2791,7 @@ broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) -> [{xmlelement, "items", nodeAttr(Node), [{xmlelement, "item", itemAttr("configuration"), Content}]}]), broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubOptions, Stanza), + NodeOptions, SubsByDepth, nodes, Stanza), {result, true}; _ -> {result, false} @@ -2784,6 +2800,28 @@ broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) -> {result, false} end. +get_collection_subscriptions(Host, Node) -> + case mnesia:transaction(fun tree_call/3, + [Host, get_parentnodes_tree, + [Host, Node, service_jid(Host)]]) of + {atomic, NodesByDepth} when is_list(NodesByDepth) -> + lists:map(fun ({Depth, Nodes}) -> + {Depth, [{N, get_node_subs(N)} || N <- Nodes]} + end, NodesByDepth); + Other -> + Other + end. + +get_node_subs(#pubsub_node{type = Type, + nodeid = {Host, Node}, + id = NodeID}) -> + case node_action(Host, Type, get_node_subscriptions, [NodeID]) of + {result, Subs} -> + get_options_for_subs(Host, Node, NodeID, Subs); + Other -> + Other + end. + % TODO: merge broadcast code that way %broadcast(Host, Node, NodeId, Type, NodeOptions, Feature, Force, ElName, SubEls) -> % case (get_option(NodeOptions, Feature) or Force) of @@ -2802,15 +2840,14 @@ broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) -> % {result, false} % end -broadcast_stanza(Host, Node, _NodeId, _Type, NodeOptions, Subs, Stanza) -> +broadcast_stanza(Host, Node, _NodeId, _Type, NodeOptions, SubsByDepth, NotifyType, Stanza) -> %AccessModel = get_option(NodeOptions, access_model), BroadcastAll = get_option(NodeOptions, broadcast_all_resources), %% XXX this is not standard, but usefull From = service_jid(Host), %% Handles explicit subscriptions - DeliverSubs = lists:filter(fun({LJID, _Node, SubOptions}) -> - is_to_deliver(LJID, NodeOptions, SubOptions) - end, Subs), - lists:foreach(fun({LJID, _Node, _SubOptions}) -> + FilteredSubsByDepth = depths_to_deliver(NotifyType, SubsByDepth), + NodesByJID = collate_subs_by_jid(FilteredSubsByDepth), + lists:foreach(fun ({LJID, Nodes}) -> LJIDs = case BroadcastAll of true -> {U, S, _} = LJID, @@ -2818,10 +2855,11 @@ broadcast_stanza(Host, Node, _NodeId, _Type, NodeOptions, Subs, Stanza) -> false -> [LJID] end, + SHIMStanza = add_headers(Stanza, collection_shim(Node, Nodes)), lists:foreach(fun(To) -> - ejabberd_router ! {route, From, jlib:make_jid(To), Stanza} + ejabberd_router ! {route, From, jlib:make_jid(To), SHIMStanza} end, LJIDs) - end, DeliverSubs), + end, NodesByJID), %% Handles implicit presence subscriptions case Host of {LUser, LServer, LResource} -> @@ -2869,6 +2907,37 @@ broadcast_stanza(Host, Node, _NodeId, _Type, NodeOptions, Subs, Stanza) -> ok end. +depths_to_deliver(NotifyType, SubsByDepth) -> + NodesToDeliver = + fun (Depth, Node, Subs, Acc) -> + lists:foldl(fun ({LJID, _Node, SubOptions} = S, Acc2) -> + case is_to_deliver(LJID, NotifyType, Depth, + Node#pubsub_node.options, + SubOptions) of + true -> [S | Acc2]; + false -> Acc2 + end + end, Acc, Subs) + end, + + DepthsToDeliver = + fun ({Depth, SubsByNode}, Acc) -> + lists:foldl(fun ({Node, Subs}, Acc2) -> + NodesToDeliver(Depth, Node, Subs, Acc2) + end, Acc, SubsByNode) + end, + + lists:foldl(DepthsToDeliver, [], SubsByDepth). + +collate_subs_by_jid(SubsByDepth) -> + lists:foldl(fun ({JID, Node, _Options}, Acc) -> + OldNodes = case lists:keysearch(JID, 1, Acc) of + {value, {JID, Nodes}} -> Nodes; + false -> [] + end, + lists:keystore(JID, 1, Acc, {JID, [Node | OldNodes]}) + end, [], SubsByDepth). + %% If we don't know the resource, just pick first if any %% If no resource available, check if caps anyway (remote online) user_resources(User, Server) -> @@ -3000,6 +3069,10 @@ max_items(Options) -> ?LISTMXFIELD(Label, "pubsub#" ++ atom_to_list(Var), get_option(Options, Var), Opts)). +-define(NLIST_CONFIG_FIELD(Label, Var), + ?STRINGMXFIELD(Label, "pubsub#" ++ atom_to_list(Var), + [node_to_string(N) || N <- get_option(Options, Var)])). + get_configure_xfields(_Type, Options, Lang, Groups) -> [?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NODE_CONFIG), ?BOOL_CONFIG_FIELD("Deliver payloads with event notifications", deliver_payloads), @@ -3020,7 +3093,8 @@ get_configure_xfields(_Type, Options, Lang, Groups) -> ?INTEGER_CONFIG_FIELD("Max payload size in bytes", max_payload_size), ?ALIST_CONFIG_FIELD("When to send the last published item", send_last_published_item, [never, on_sub, on_sub_and_presence]), - ?BOOL_CONFIG_FIELD("Only deliver notifications to available users", presence_based_delivery) + ?BOOL_CONFIG_FIELD("Only deliver notifications to available users", presence_based_delivery), + ?NLIST_CONFIG_FIELD("The collections with which a node is affiliated", collection) ]. %%There are several reasons why the node configuration request might fail:
@@ -3052,8 +3126,10 @@ set_configure(Host, Node, From, Els, Lang) -> end, case set_xoption(XData, OldOpts) of NewOpts when is_list(NewOpts) -> - tree_call(Host, set_node, [N#pubsub_node{options = NewOpts}]), - {result, ok}; + case tree_call(Host, set_node, [N#pubsub_node{options = NewOpts}]) of + ok -> {result, ok}; + Err -> Err + end; Err -> Err end @@ -3166,6 +3242,9 @@ set_xoption([{"pubsub#type", Value} | Opts], NewOpts) -> ?SET_STRING_XOPT(type, Value); set_xoption([{"pubsub#body_xslt", Value} | Opts], NewOpts) -> ?SET_STRING_XOPT(body_xslt, Value); +set_xoption([{"pubsub#collection", Value} | Opts], NewOpts) -> + NewValue = [string_to_node(V) || V <- Value], + ?SET_LIST_XOPT(collection, NewValue); set_xoption([_ | Opts], NewOpts) -> % skip unknown field set_xoption(Opts, NewOpts). @@ -3257,7 +3336,7 @@ features() -> "member-affiliation", % RECOMMENDED %TODO "meta-data", % RECOMMENDED % see plugin "modify-affiliations", % OPTIONAL - %TODO "multi-collection", % OPTIONAL + % see plugin "multi-collection", % OPTIONAL % see plugin "multi-subscribe", % OPTIONAL % see plugin "outcast-affiliation", % RECOMMENDED % see plugin "persistent-items", % RECOMMENDED @@ -3395,3 +3474,10 @@ itemsEls(Items) -> lists:map(fun(#pubsub_item{itemid = {ItemId, _}, payload = Payload}) -> {xmlelement, "item", itemAttr(ItemId), Payload} end, Items). + +add_headers({xmlelement, Name, Attrs, Els}, Headers) -> + {xmlelement, Name, Attrs, Els ++ Headers}. + +collection_shim(Node, Nodes) -> + [{xmlelement, "header", [{"name", "Collection"}], + [{xmlcdata, node_to_string(N)}]} || N <- Nodes -- [Node]]. diff --git a/src/mod_pubsub/node.template b/src/mod_pubsub/node.template index 336c547a9..534c18226 100644 --- a/src/mod_pubsub/node.template +++ b/src/mod_pubsub/node.template @@ -65,7 +65,8 @@ get_items/2, get_item/7, get_item/2, - set_item/1 + set_item/1, + get_item_name/3 ]). @@ -76,8 +77,7 @@ terminate(Host, ServerHost) -> node_hometree:terminate(Host, ServerHost). options() -> - [{node_type, __TO_BE_DEFINED__}, - {deliver_payloads, true}, + [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, true}, @@ -118,8 +118,8 @@ create_node(NodeId, Owner) -> delete_node(Removed) -> node_hometree:delete_node(Removed). -subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> - node_hometree:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> + node_hometree:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> node_hometree:unsubscribe_node(NodeId, Sender, Subscriber, SubID). @@ -158,7 +158,7 @@ get_subscriptions(NodeId, Owner) -> node_hometree:get_subscriptions(NodeId, Owner). set_subscriptions(NodeId, Owner, Subscriptions) -> - node_hometree:set_subscriptions(NodeId, Owner, Subscription). + node_hometree:set_subscriptions(NodeId, Owner, Subscriptions). get_states(NodeId) -> node_hometree:get_states(NodeId). @@ -172,7 +172,7 @@ set_state(State) -> get_items(NodeId, From) -> node_hometree:get_items(NodeId, From). -get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). get_item(NodeId, ItemId) -> @@ -183,3 +183,6 @@ get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, Su set_item(Item) -> node_hometree:set_item(Item). + +get_item_name(Host, Node, Id) -> + node_hometree:get_item_name(Host, Node, Id). diff --git a/src/mod_pubsub/node_buddy.erl b/src/mod_pubsub/node_buddy.erl index 0094674e7..2e8dd0249 100644 --- a/src/mod_pubsub/node_buddy.erl +++ b/src/mod_pubsub/node_buddy.erl @@ -78,8 +78,7 @@ terminate(Host, ServerHost) -> node_hometree:terminate(Host, ServerHost). options() -> - [{node_type, buddy}, - {deliver_payloads, true}, + [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, true}, diff --git a/src/mod_pubsub/node_club.erl b/src/mod_pubsub/node_club.erl index 6bbece021..91ff520c6 100644 --- a/src/mod_pubsub/node_club.erl +++ b/src/mod_pubsub/node_club.erl @@ -78,8 +78,7 @@ terminate(Host, ServerHost) -> node_hometree:terminate(Host, ServerHost). options() -> - [{node_type, club}, - {deliver_payloads, true}, + [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, true}, diff --git a/src/mod_pubsub/node_dag.erl b/src/mod_pubsub/node_dag.erl new file mode 100644 index 000000000..954a26a95 --- /dev/null +++ b/src/mod_pubsub/node_dag.erl @@ -0,0 +1,154 @@ +%%% ==================================================================== +%%% ``The contents of this file are subject to the Erlang Public License, +%%% Version 1.1, (the "License"); you may not use this file except in +%%% compliance with the License. You should have received a copy of the +%%% Erlang Public License along with this software. If not, it can be +%%% retrieved via the world wide web at http://www.erlang.org/. +%%% +%%% Software distributed under the License is distributed on an "AS IS" +%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%%% the License for the specific language governing rights and limitations +%%% under the License. +%%% +%%% @author Brian CullyGenerate pubsub service JID.
+service_jid(Host) -> + case Host of + {U,S,_} -> {jid, U, S, "", U, S, ""}; + _ -> {jid, "", Host, "", "", Host, ""} + end. diff --git a/src/mod_pubsub/nodetree_tree.erl b/src/mod_pubsub/nodetree_tree.erl index 8f49a06b0..767b0e9c8 100644 --- a/src/mod_pubsub/nodetree_tree.erl +++ b/src/mod_pubsub/nodetree_tree.erl @@ -36,6 +36,8 @@ -module(nodetree_tree). -author('christophe.romain@process-one.net'). +-include_lib("stdlib/include/qlc.hrl"). + -include("pubsub.hrl"). -include("jlib.hrl"). @@ -50,6 +52,8 @@ get_node/1, get_nodes/2, get_nodes/1, + get_parentnodes/3, + get_parentnodes_tree/3, get_subnodes/3, get_subnodes_tree/3, create_node/5, @@ -124,6 +128,32 @@ get_nodes(Host, _From) -> get_nodes(Host) -> mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}). +%% @spec (Host, Node, From) -> [{Depth, Record}] | {error, Reason} +%% Host = mod_pubsub:host() | mod_pubsub:jid() +%% Node = mod_pubsub:pubsubNode() +%% From = mod_pubsub:jid() +%% Depth = integer() +%% Record = pubsubNode() +%% @docDefault node tree does not handle parents, return empty list.
+get_parentnodes(_Host, _Node, _From) -> + []. + +%% @spec (Host, Node, From) -> [{Depth, Record}] | {error, Reason} +%% Host = mod_pubsub:host() | mod_pubsub:jid() +%% Node = mod_pubsub:pubsubNode() +%% From = mod_pubsub:jid() +%% Depth = integer() +%% Record = pubsubNode() +%% @docDefault node tree does not handle parents, return a list +%% containing just this node.
+get_parentnodes_tree(Host, Node, From) -> + case get_node(Host, Node, From) of + N when is_record(N, pubsub_node) -> + [{0, mnesia:read(pubsub_node, {Host, Node})}]; + Error -> + Error + end. + %% @spec (Host, Node, From) -> [pubsubNode()] | {error, Reason} %% Host = mod_pubsub:host() %% Node = mod_pubsub:pubsubNode() @@ -131,7 +161,11 @@ get_nodes(Host) -> get_subnodes(Host, Node, _From) -> get_subnodes(Host, Node). get_subnodes(Host, Node) -> - mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, parent = Node, _ = '_'}). + Q = qlc:q([N || #pubsub_node{nodeid = {NHost, _}, + parents = Parents} = N <- mnesia:table(pubsub_node), + Host == NHost, + lists:member(Node, Parents)]), + qlc:e(Q). %% @spec (Host, Index) -> [pubsubNodeIdx()] | {error, Reason} %% Host = mod_pubsub:host() @@ -180,7 +214,7 @@ create_node(Host, Node, Type, Owner, Options) -> NodeId = pubsub_index:new(node), mnesia:write(#pubsub_node{nodeid = {Host, Node}, id = NodeId, - parent = ParentNode, + parents = [ParentNode], type = Type, owners = [BJID], options = Options}), diff --git a/src/mod_pubsub/nodetree_virtual.erl b/src/mod_pubsub/nodetree_virtual.erl index 4b331aeb9..e003f9058 100644 --- a/src/mod_pubsub/nodetree_virtual.erl +++ b/src/mod_pubsub/nodetree_virtual.erl @@ -47,6 +47,8 @@ get_node/1, get_nodes/2, get_nodes/1, + get_parentnodes/3, + get_parentnodes_tree/3, get_subnodes/3, get_subnodes_tree/3, create_node/5, @@ -104,6 +106,22 @@ get_nodes(Host, _From) -> get_nodes(_Host) -> []. +%% @spec (Host, Node, From) -> [pubsubNode()] +%% Host = mod_pubsub:host() +%% Node = mod_pubsub:pubsubNode() +%% From = mod_pubsub:jid() +%% @docVirtual node tree does not handle parent/child. Child list is empty.
+get_parentnodes(_Host, _Node, _From) -> + []. + +%% @spec (Host, Node, From) -> [pubsubNode()] +%% Host = mod_pubsub:host() +%% Node = mod_pubsub:pubsubNode() +%% From = mod_pubsub:jid() +%% @docVirtual node tree does not handle parent/child. Child list is empty.
+get_parentnodes_tree(_Host, _Node, _From) -> + []. + %% @spec (Host, Node, From) -> [pubsubNode()] %% Host = mod_pubsub:host() %% Node = mod_pubsub:pubsubNode() diff --git a/src/mod_pubsub/pubsub.hrl b/src/mod_pubsub/pubsub.hrl index ee129aa9f..f406a05c3 100644 --- a/src/mod_pubsub/pubsub.hrl +++ b/src/mod_pubsub/pubsub.hrl @@ -91,7 +91,7 @@ %%%The parentid and type fields are indexed.
-record(pubsub_node, {nodeid, id, - parent, + parents = [], type = "flat", owners = [], options = []