mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-22 17:28:25 +01:00
experimental patch including XEP-248 (thanks to Brian Cully)
SVN Revision: 2157
This commit is contained in:
parent
6f080f7fed
commit
50b73664e2
@ -4,7 +4,7 @@ include ..\Makefile.inc
|
|||||||
EFLAGS = -I .. -pz ..
|
EFLAGS = -I .. -pz ..
|
||||||
|
|
||||||
OUTDIR = ..
|
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)
|
ALL : $(BEAMS)
|
||||||
|
|
||||||
@ -20,8 +20,8 @@ $(OUTDIR)\gen_pubsub_nodetree.beam : gen_pubsub_nodetree.erl
|
|||||||
$(OUTDIR)\mod_pubsub.beam : mod_pubsub.erl
|
$(OUTDIR)\mod_pubsub.beam : mod_pubsub.erl
|
||||||
erlc -W $(EFLAGS) -o $(OUTDIR) mod_pubsub.erl
|
erlc -W $(EFLAGS) -o $(OUTDIR) mod_pubsub.erl
|
||||||
|
|
||||||
$(OUTDIR)\nodetree_default.beam : nodetree_default.erl
|
$(OUTDIR)\nodetree_tree.beam : nodetree_tree.erl
|
||||||
erlc -W $(EFLAGS) -o $(OUTDIR) nodetree_default.erl
|
erlc -W $(EFLAGS) -o $(OUTDIR) nodetree_tree.erl
|
||||||
|
|
||||||
$(OUTDIR)\nodetree_virtual.beam : nodetree_virtual.erl
|
$(OUTDIR)\nodetree_virtual.beam : nodetree_virtual.erl
|
||||||
erlc -W $(EFLAGS) -o $(OUTDIR) 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
|
$(OUTDIR)\node_club.beam : node_club.erl
|
||||||
erlc -W $(EFLAGS) -o $(OUTDIR) node_club.erl
|
erlc -W $(EFLAGS) -o $(OUTDIR) node_club.erl
|
||||||
|
|
||||||
$(OUTDIR)\node_default.beam : node_default.erl
|
$(OUTDIR)\node_hometree.beam : node_hometree.erl
|
||||||
erlc -W $(EFLAGS) -o $(OUTDIR) node_default.erl
|
erlc -W $(EFLAGS) -o $(OUTDIR) node_hometree.erl
|
||||||
|
|
||||||
$(OUTDIR)\node_dispatch.beam : node_dispatch.erl
|
$(OUTDIR)\node_dispatch.beam : node_dispatch.erl
|
||||||
erlc -W $(EFLAGS) -o $(OUTDIR) node_dispatch.erl
|
erlc -W $(EFLAGS) -o $(OUTDIR) node_dispatch.erl
|
||||||
|
@ -47,6 +47,8 @@ behaviour_info(callbacks) ->
|
|||||||
{get_node, 1},
|
{get_node, 1},
|
||||||
{get_nodes, 2},
|
{get_nodes, 2},
|
||||||
{get_nodes, 1},
|
{get_nodes, 1},
|
||||||
|
{get_parentnodes, 3},
|
||||||
|
{get_parentnodes_tree, 3},
|
||||||
{get_subnodes, 3},
|
{get_subnodes, 3},
|
||||||
{get_subnodes_tree, 3},
|
{get_subnodes_tree, 3},
|
||||||
{create_node, 5},
|
{create_node, 5},
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
get_items/2,
|
get_items/2,
|
||||||
get_item/3,
|
get_item/3,
|
||||||
get_cached_item/2,
|
get_cached_item/2,
|
||||||
broadcast_stanza/7,
|
broadcast_stanza/8,
|
||||||
get_configure/5,
|
get_configure/5,
|
||||||
set_configure/5,
|
set_configure/5,
|
||||||
tree_action/3,
|
tree_action/3,
|
||||||
@ -304,7 +304,7 @@ update_node_database(Host, ServerHost) ->
|
|||||||
mnesia:delete({pubsub_node, NodeId}),
|
mnesia:delete({pubsub_node, NodeId}),
|
||||||
{[#pubsub_node{nodeid = NodeId,
|
{[#pubsub_node{nodeid = NodeId,
|
||||||
id = NodeIdx,
|
id = NodeIdx,
|
||||||
parent = element(2, ParentId),
|
parents = [element(2, ParentId)],
|
||||||
owners = Owners,
|
owners = Owners,
|
||||||
options = Options} |
|
options = Options} |
|
||||||
RecList], NodeIdx + 1}
|
RecList], NodeIdx + 1}
|
||||||
@ -334,12 +334,12 @@ update_node_database(Host, ServerHost) ->
|
|||||||
#pubsub_node{
|
#pubsub_node{
|
||||||
nodeid = NodeId,
|
nodeid = NodeId,
|
||||||
id = 0,
|
id = 0,
|
||||||
parent = Parent,
|
parents = [Parent],
|
||||||
type = Type,
|
type = Type,
|
||||||
owners = Owners,
|
owners = Owners,
|
||||||
options = Options}
|
options = Options}
|
||||||
end,
|
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() ->
|
FNew = fun() ->
|
||||||
lists:foldl(fun(#pubsub_node{nodeid = NodeId} = PubsubNode, NodeIdx) ->
|
lists:foldl(fun(#pubsub_node{nodeid = NodeId} = PubsubNode, NodeIdx) ->
|
||||||
mnesia:write(PubsubNode#pubsub_node{id = 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",
|
?ERROR_MSG("Problem updating Pubsub node tables:~n~p",
|
||||||
[Reason])
|
[Reason])
|
||||||
end;
|
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
|
ok
|
||||||
end.
|
end.
|
||||||
@ -1443,6 +1454,12 @@ replace_subscription_helper(_, OldSub, Acc) ->
|
|||||||
-define(STRINGXFIELD(Label, Var, Val),
|
-define(STRINGXFIELD(Label, Var, Val),
|
||||||
?XFIELD("text-single", 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),
|
-define(XFIELDOPT(Type, Label, Var, Val, Opts),
|
||||||
{xmlelement, "field", [{"type", Type},
|
{xmlelement, "field", [{"type", Type},
|
||||||
{"label", translate:translate(Lang, Label)},
|
{"label", translate:translate(Lang, Label)},
|
||||||
@ -1601,10 +1618,14 @@ delete_node(_Host, [], _Owner) ->
|
|||||||
{error, ?ERR_NOT_ALLOWED};
|
{error, ?ERR_NOT_ALLOWED};
|
||||||
delete_node(Host, Node, Owner) ->
|
delete_node(Host, Node, Owner) ->
|
||||||
Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
|
Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||||
|
SubsByDepth = get_collection_subscriptions(Host, Node),
|
||||||
case node_call(Type, get_affiliation, [NodeId, Owner]) of
|
case node_call(Type, get_affiliation, [NodeId, Owner]) of
|
||||||
{result, owner} ->
|
{result, owner} ->
|
||||||
Removed = tree_call(Host, delete_node, [Host, Node]),
|
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
|
%% Entity is not an owner
|
||||||
{error, ?ERR_FORBIDDEN}
|
{error, ?ERR_FORBIDDEN}
|
||||||
@ -1612,27 +1633,26 @@ delete_node(Host, Node, Owner) ->
|
|||||||
end,
|
end,
|
||||||
Reply = [],
|
Reply = [],
|
||||||
case transaction(Host, Node, Action, transaction) of
|
case transaction(Host, Node, Action, transaction) of
|
||||||
{result, {_, {Result, broadcast, Removed}}} ->
|
{result, {_, {SubsByDepth, {Result, broadcast, Removed}}}} ->
|
||||||
lists:foreach(fun({RNode, RSubscriptions}) ->
|
lists:foreach(fun({RNode, _RSubscriptions}) ->
|
||||||
{RH, RN} = RNode#pubsub_node.nodeid,
|
{RH, RN} = RNode#pubsub_node.nodeid,
|
||||||
NodeId = RNode#pubsub_node.id,
|
NodeId = RNode#pubsub_node.id,
|
||||||
Type = RNode#pubsub_node.type,
|
Type = RNode#pubsub_node.type,
|
||||||
Options = RNode#pubsub_node.options,
|
Options = RNode#pubsub_node.options,
|
||||||
broadcast_removed_node(RH, RN, NodeId, Type, Options, RSubscriptions),
|
broadcast_removed_node(RH, RN, NodeId, Type, Options, SubsByDepth)
|
||||||
unset_cached_item(RH, NodeId)
|
|
||||||
end, Removed),
|
end, Removed),
|
||||||
case Result of
|
case Result of
|
||||||
default -> {result, Reply};
|
default -> {result, Reply};
|
||||||
_ -> {result, Result}
|
_ -> {result, Result}
|
||||||
end;
|
end;
|
||||||
{result, {_, {Result, _Removed}}} ->
|
{result, {_, {_, {Result, _Removed}}}} ->
|
||||||
case Result of
|
case Result of
|
||||||
default -> {result, Reply};
|
default -> {result, Reply};
|
||||||
_ -> {result, Result}
|
_ -> {result, Result}
|
||||||
end;
|
end;
|
||||||
{result, {_, default}} ->
|
{result, {_, {_, default}}} ->
|
||||||
{result, Reply};
|
{result, Reply};
|
||||||
{result, {_, Result}} ->
|
{result, {_, {_, Result}}} ->
|
||||||
{result, Result};
|
{result, Result};
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
@ -2605,19 +2625,28 @@ service_jid(Host) ->
|
|||||||
|
|
||||||
%% @spec (LJID, PresenceDelivery) -> boolean()
|
%% @spec (LJID, PresenceDelivery) -> boolean()
|
||||||
%% LJID = jid()
|
%% LJID = jid()
|
||||||
|
%% NotifyType = items | nodes
|
||||||
|
%% Depth = integer()
|
||||||
%% NodeOptions = [{atom(), term()}]
|
%% NodeOptions = [{atom(), term()}]
|
||||||
%% SubOptions = [{atom(), term()}]
|
%% SubOptions = [{atom(), term()}]
|
||||||
%% @doc <p>Check if a notification must be delivered or not based on
|
%% @doc <p>Check if a notification must be delivered or not based on
|
||||||
%% node and subscription options.</p>
|
%% node and subscription options.</p>
|
||||||
is_to_deliver(LJID, NodeOptions, SubOptions) ->
|
is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) ->
|
||||||
sub_to_deliver(LJID, SubOptions) andalso node_to_deliver(LJID, NodeOptions).
|
sub_to_deliver(LJID, NotifyType, Depth, SubOptions)
|
||||||
|
andalso node_to_deliver(LJID, NodeOptions).
|
||||||
|
|
||||||
sub_to_deliver(_LJID, SubOptions) ->
|
sub_to_deliver(_LJID, NotifyType, Depth, SubOptions) ->
|
||||||
lists:all(fun sub_option_can_deliver/1, 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(items, _, {subscription_type, nodes}) -> false;
|
||||||
sub_option_can_deliver({expire, When}) -> now() < When;
|
sub_option_can_deliver(nodes, _, {subscription_type, items}) -> false;
|
||||||
sub_option_can_deliver(_) -> true.
|
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) ->
|
node_to_deliver(LJID, NodeOptions) ->
|
||||||
PresenceDelivery = get_option(NodeOptions, presence_based_delivery),
|
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_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, _From, Payload) ->
|
||||||
%broadcast(Host, Node, NodeId, NodeOptions, none, true, "items", ItemEls)
|
%broadcast(Host, Node, NodeId, NodeOptions, none, true, "items", ItemEls)
|
||||||
case node_action(Host, Type, get_node_subscriptions, [NodeId]) of
|
case get_collection_subscriptions(Host, Node) of
|
||||||
{result, []} ->
|
SubsByDepth when is_list(SubsByDepth) ->
|
||||||
{result, false};
|
|
||||||
{result, Subs} ->
|
|
||||||
SubOptions = get_options_for_subs(Host, Node, NodeId, Subs),
|
|
||||||
Content = case get_option(NodeOptions, deliver_payloads) of
|
Content = case get_option(NodeOptions, deliver_payloads) of
|
||||||
true -> Payload;
|
true -> Payload;
|
||||||
false -> []
|
false -> []
|
||||||
@ -2665,7 +2691,7 @@ broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, _
|
|||||||
[{xmlelement, "items", nodeAttr(Node),
|
[{xmlelement, "items", nodeAttr(Node),
|
||||||
[{xmlelement, "item", itemAttr(ItemId), Content}]}]),
|
[{xmlelement, "item", itemAttr(ItemId), Content}]}]),
|
||||||
broadcast_stanza(Host, Node, NodeId, Type,
|
broadcast_stanza(Host, Node, NodeId, Type,
|
||||||
NodeOptions, SubOptions, Stanza),
|
NodeOptions, SubsByDepth, items, Stanza),
|
||||||
case Removed of
|
case Removed of
|
||||||
[] ->
|
[] ->
|
||||||
ok;
|
ok;
|
||||||
@ -2676,8 +2702,8 @@ broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, _
|
|||||||
[{xmlelement, "items", nodeAttr(Node),
|
[{xmlelement, "items", nodeAttr(Node),
|
||||||
[{xmlelement, "retract", itemAttr(RId), []} || RId <- Removed]}]),
|
[{xmlelement, "retract", itemAttr(RId), []} || RId <- Removed]}]),
|
||||||
broadcast_stanza(Host, Node, NodeId, Type,
|
broadcast_stanza(Host, Node, NodeId, Type,
|
||||||
NodeOptions, SubOptions,
|
NodeOptions, SubsByDepth,
|
||||||
RetractStanza);
|
items, RetractStanza);
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end
|
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)
|
%broadcast(Host, Node, NodeId, NodeOptions, notify_retract, ForceNotify, "retract", RetractEls)
|
||||||
case (get_option(NodeOptions, notify_retract) or ForceNotify) of
|
case (get_option(NodeOptions, notify_retract) or ForceNotify) of
|
||||||
true ->
|
true ->
|
||||||
case node_action(Host, Type, get_node_subscriptions, [NodeId]) of
|
case get_collection_subscriptions(Host, Node) of
|
||||||
{result, []} ->
|
SubsByDepth when is_list(SubsByDepth) ->
|
||||||
{result, false};
|
|
||||||
{result, Subs} ->
|
|
||||||
SubOptions = get_options_for_subs(Host, Node, NodeId, Subs),
|
|
||||||
Stanza = event_stanza(
|
Stanza = event_stanza(
|
||||||
[{xmlelement, "items", nodeAttr(Node),
|
[{xmlelement, "items", nodeAttr(Node),
|
||||||
[{xmlelement, "retract", itemAttr(ItemId), []} || ItemId <- ItemIds]}]),
|
[{xmlelement, "retract", itemAttr(ItemId), []} || ItemId <- ItemIds]}]),
|
||||||
broadcast_stanza(Host, Node, NodeId, Type,
|
broadcast_stanza(Host, Node, NodeId, Type,
|
||||||
NodeOptions, SubOptions, Stanza),
|
NodeOptions, SubsByDepth, items, Stanza),
|
||||||
{result, true};
|
{result, true};
|
||||||
_ ->
|
_ ->
|
||||||
{result, false}
|
{result, false}
|
||||||
@ -2717,16 +2740,13 @@ broadcast_purge_node(Host, Node, NodeId, Type, NodeOptions) ->
|
|||||||
%broadcast(Host, Node, NodeId, NodeOptions, notify_retract, false, "purge", [])
|
%broadcast(Host, Node, NodeId, NodeOptions, notify_retract, false, "purge", [])
|
||||||
case get_option(NodeOptions, notify_retract) of
|
case get_option(NodeOptions, notify_retract) of
|
||||||
true ->
|
true ->
|
||||||
case node_action(Host, Type, get_node_subscriptions, [NodeId]) of
|
case get_collection_subscriptions(Host, Node) of
|
||||||
{result, []} ->
|
SubsByDepth when is_list(SubsByDepth) ->
|
||||||
{result, false};
|
|
||||||
{result, Subs} ->
|
|
||||||
SubOptions = get_options_for_subs(Host, Node, NodeId, Subs),
|
|
||||||
Stanza = event_stanza(
|
Stanza = event_stanza(
|
||||||
[{xmlelement, "purge", nodeAttr(Node),
|
[{xmlelement, "purge", nodeAttr(Node),
|
||||||
[]}]),
|
[]}]),
|
||||||
broadcast_stanza(Host, Node, NodeId, Type,
|
broadcast_stanza(Host, Node, NodeId, Type,
|
||||||
NodeOptions, SubOptions, Stanza),
|
NodeOptions, SubsByDepth, nodes, Stanza),
|
||||||
{result, true};
|
{result, true};
|
||||||
_ ->
|
_ ->
|
||||||
{result, false}
|
{result, false}
|
||||||
@ -2735,20 +2755,19 @@ broadcast_purge_node(Host, Node, NodeId, Type, NodeOptions) ->
|
|||||||
{result, false}
|
{result, false}
|
||||||
end.
|
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", [])
|
%broadcast(Host, Node, NodeId, NodeOptions, notify_delete, false, "delete", [])
|
||||||
case get_option(NodeOptions, notify_delete) of
|
case get_option(NodeOptions, notify_delete) of
|
||||||
true ->
|
true ->
|
||||||
case Subs of
|
case SubsByDepth of
|
||||||
[] ->
|
[] ->
|
||||||
{result, false};
|
{result, false};
|
||||||
_ ->
|
_ ->
|
||||||
SubOptions = get_options_for_subs(Host, Node, NodeId, Subs),
|
|
||||||
Stanza = event_stanza(
|
Stanza = event_stanza(
|
||||||
[{xmlelement, "delete", nodeAttr(Node),
|
[{xmlelement, "delete", nodeAttr(Node),
|
||||||
[]}]),
|
[]}]),
|
||||||
broadcast_stanza(Host, Node, NodeId, Type,
|
broadcast_stanza(Host, Node, NodeId, Type,
|
||||||
NodeOptions, SubOptions, Stanza),
|
NodeOptions, SubsByDepth, nodes, Stanza),
|
||||||
{result, true}
|
{result, true}
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
@ -2759,11 +2778,8 @@ broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
|
|||||||
%broadcast(Host, Node, NodeId, NodeOptions, notify_config, false, "items", ConfigEls)
|
%broadcast(Host, Node, NodeId, NodeOptions, notify_config, false, "items", ConfigEls)
|
||||||
case get_option(NodeOptions, notify_config) of
|
case get_option(NodeOptions, notify_config) of
|
||||||
true ->
|
true ->
|
||||||
case node_action(Host, Type, get_node_subscriptions, [NodeId]) of
|
case get_collection_subscriptions(Host, Node) of
|
||||||
{result, []} ->
|
SubsByDepth when is_list(SubsByDepth) ->
|
||||||
{result, false};
|
|
||||||
{result, Subs} ->
|
|
||||||
SubOptions = get_options_for_subs(Host, Node, NodeId, Subs),
|
|
||||||
Content = case get_option(NodeOptions, deliver_payloads) of
|
Content = case get_option(NodeOptions, deliver_payloads) of
|
||||||
true ->
|
true ->
|
||||||
[{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
|
[{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, "items", nodeAttr(Node),
|
||||||
[{xmlelement, "item", itemAttr("configuration"), Content}]}]),
|
[{xmlelement, "item", itemAttr("configuration"), Content}]}]),
|
||||||
broadcast_stanza(Host, Node, NodeId, Type,
|
broadcast_stanza(Host, Node, NodeId, Type,
|
||||||
NodeOptions, SubOptions, Stanza),
|
NodeOptions, SubsByDepth, nodes, Stanza),
|
||||||
{result, true};
|
{result, true};
|
||||||
_ ->
|
_ ->
|
||||||
{result, false}
|
{result, false}
|
||||||
@ -2784,6 +2800,28 @@ broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
|
|||||||
{result, false}
|
{result, false}
|
||||||
end.
|
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
|
% TODO: merge broadcast code that way
|
||||||
%broadcast(Host, Node, NodeId, Type, NodeOptions, Feature, Force, ElName, SubEls) ->
|
%broadcast(Host, Node, NodeId, Type, NodeOptions, Feature, Force, ElName, SubEls) ->
|
||||||
% case (get_option(NodeOptions, Feature) or Force) of
|
% case (get_option(NodeOptions, Feature) or Force) of
|
||||||
@ -2802,15 +2840,14 @@ broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
|
|||||||
% {result, false}
|
% {result, false}
|
||||||
% end
|
% 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),
|
%AccessModel = get_option(NodeOptions, access_model),
|
||||||
BroadcastAll = get_option(NodeOptions, broadcast_all_resources), %% XXX this is not standard, but usefull
|
BroadcastAll = get_option(NodeOptions, broadcast_all_resources), %% XXX this is not standard, but usefull
|
||||||
From = service_jid(Host),
|
From = service_jid(Host),
|
||||||
%% Handles explicit subscriptions
|
%% Handles explicit subscriptions
|
||||||
DeliverSubs = lists:filter(fun({LJID, _Node, SubOptions}) ->
|
FilteredSubsByDepth = depths_to_deliver(NotifyType, SubsByDepth),
|
||||||
is_to_deliver(LJID, NodeOptions, SubOptions)
|
NodesByJID = collate_subs_by_jid(FilteredSubsByDepth),
|
||||||
end, Subs),
|
lists:foreach(fun ({LJID, Nodes}) ->
|
||||||
lists:foreach(fun({LJID, _Node, _SubOptions}) ->
|
|
||||||
LJIDs = case BroadcastAll of
|
LJIDs = case BroadcastAll of
|
||||||
true ->
|
true ->
|
||||||
{U, S, _} = LJID,
|
{U, S, _} = LJID,
|
||||||
@ -2818,10 +2855,11 @@ broadcast_stanza(Host, Node, _NodeId, _Type, NodeOptions, Subs, Stanza) ->
|
|||||||
false ->
|
false ->
|
||||||
[LJID]
|
[LJID]
|
||||||
end,
|
end,
|
||||||
|
SHIMStanza = add_headers(Stanza, collection_shim(Node, Nodes)),
|
||||||
lists:foreach(fun(To) ->
|
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, LJIDs)
|
||||||
end, DeliverSubs),
|
end, NodesByJID),
|
||||||
%% Handles implicit presence subscriptions
|
%% Handles implicit presence subscriptions
|
||||||
case Host of
|
case Host of
|
||||||
{LUser, LServer, LResource} ->
|
{LUser, LServer, LResource} ->
|
||||||
@ -2869,6 +2907,37 @@ broadcast_stanza(Host, Node, _NodeId, _Type, NodeOptions, Subs, Stanza) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
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 we don't know the resource, just pick first if any
|
||||||
%% If no resource available, check if caps anyway (remote online)
|
%% If no resource available, check if caps anyway (remote online)
|
||||||
user_resources(User, Server) ->
|
user_resources(User, Server) ->
|
||||||
@ -3000,6 +3069,10 @@ max_items(Options) ->
|
|||||||
?LISTMXFIELD(Label, "pubsub#" ++ atom_to_list(Var),
|
?LISTMXFIELD(Label, "pubsub#" ++ atom_to_list(Var),
|
||||||
get_option(Options, Var), Opts)).
|
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) ->
|
get_configure_xfields(_Type, Options, Lang, Groups) ->
|
||||||
[?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NODE_CONFIG),
|
[?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NODE_CONFIG),
|
||||||
?BOOL_CONFIG_FIELD("Deliver payloads with event notifications", deliver_payloads),
|
?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),
|
?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,
|
?ALIST_CONFIG_FIELD("When to send the last published item", send_last_published_item,
|
||||||
[never, on_sub, on_sub_and_presence]),
|
[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)
|
||||||
].
|
].
|
||||||
|
|
||||||
%%<p>There are several reasons why the node configuration request might fail:</p>
|
%%<p>There are several reasons why the node configuration request might fail:</p>
|
||||||
@ -3052,8 +3126,10 @@ set_configure(Host, Node, From, Els, Lang) ->
|
|||||||
end,
|
end,
|
||||||
case set_xoption(XData, OldOpts) of
|
case set_xoption(XData, OldOpts) of
|
||||||
NewOpts when is_list(NewOpts) ->
|
NewOpts when is_list(NewOpts) ->
|
||||||
tree_call(Host, set_node, [N#pubsub_node{options = NewOpts}]),
|
case tree_call(Host, set_node, [N#pubsub_node{options = NewOpts}]) of
|
||||||
{result, ok};
|
ok -> {result, ok};
|
||||||
|
Err -> Err
|
||||||
|
end;
|
||||||
Err ->
|
Err ->
|
||||||
Err
|
Err
|
||||||
end
|
end
|
||||||
@ -3166,6 +3242,9 @@ set_xoption([{"pubsub#type", Value} | Opts], NewOpts) ->
|
|||||||
?SET_STRING_XOPT(type, Value);
|
?SET_STRING_XOPT(type, Value);
|
||||||
set_xoption([{"pubsub#body_xslt", Value} | Opts], NewOpts) ->
|
set_xoption([{"pubsub#body_xslt", Value} | Opts], NewOpts) ->
|
||||||
?SET_STRING_XOPT(body_xslt, Value);
|
?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) ->
|
set_xoption([_ | Opts], NewOpts) ->
|
||||||
% skip unknown field
|
% skip unknown field
|
||||||
set_xoption(Opts, NewOpts).
|
set_xoption(Opts, NewOpts).
|
||||||
@ -3257,7 +3336,7 @@ features() ->
|
|||||||
"member-affiliation", % RECOMMENDED
|
"member-affiliation", % RECOMMENDED
|
||||||
%TODO "meta-data", % RECOMMENDED
|
%TODO "meta-data", % RECOMMENDED
|
||||||
% see plugin "modify-affiliations", % OPTIONAL
|
% see plugin "modify-affiliations", % OPTIONAL
|
||||||
%TODO "multi-collection", % OPTIONAL
|
% see plugin "multi-collection", % OPTIONAL
|
||||||
% see plugin "multi-subscribe", % OPTIONAL
|
% see plugin "multi-subscribe", % OPTIONAL
|
||||||
% see plugin "outcast-affiliation", % RECOMMENDED
|
% see plugin "outcast-affiliation", % RECOMMENDED
|
||||||
% see plugin "persistent-items", % RECOMMENDED
|
% see plugin "persistent-items", % RECOMMENDED
|
||||||
@ -3395,3 +3474,10 @@ itemsEls(Items) ->
|
|||||||
lists:map(fun(#pubsub_item{itemid = {ItemId, _}, payload = Payload}) ->
|
lists:map(fun(#pubsub_item{itemid = {ItemId, _}, payload = Payload}) ->
|
||||||
{xmlelement, "item", itemAttr(ItemId), Payload}
|
{xmlelement, "item", itemAttr(ItemId), Payload}
|
||||||
end, Items).
|
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]].
|
||||||
|
@ -65,7 +65,8 @@
|
|||||||
get_items/2,
|
get_items/2,
|
||||||
get_item/7,
|
get_item/7,
|
||||||
get_item/2,
|
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).
|
node_hometree:terminate(Host, ServerHost).
|
||||||
|
|
||||||
options() ->
|
options() ->
|
||||||
[{node_type, __TO_BE_DEFINED__},
|
[{deliver_payloads, true},
|
||||||
{deliver_payloads, true},
|
|
||||||
{notify_config, false},
|
{notify_config, false},
|
||||||
{notify_delete, false},
|
{notify_delete, false},
|
||||||
{notify_retract, true},
|
{notify_retract, true},
|
||||||
@ -118,8 +118,8 @@ create_node(NodeId, Owner) ->
|
|||||||
delete_node(Removed) ->
|
delete_node(Removed) ->
|
||||||
node_hometree:delete_node(Removed).
|
node_hometree:delete_node(Removed).
|
||||||
|
|
||||||
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).
|
node_hometree:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options).
|
||||||
|
|
||||||
unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
|
unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
|
||||||
node_hometree: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).
|
node_hometree:get_subscriptions(NodeId, Owner).
|
||||||
|
|
||||||
set_subscriptions(NodeId, Owner, Subscriptions) ->
|
set_subscriptions(NodeId, Owner, Subscriptions) ->
|
||||||
node_hometree:set_subscriptions(NodeId, Owner, Subscription).
|
node_hometree:set_subscriptions(NodeId, Owner, Subscriptions).
|
||||||
|
|
||||||
get_states(NodeId) ->
|
get_states(NodeId) ->
|
||||||
node_hometree:get_states(NodeId).
|
node_hometree:get_states(NodeId).
|
||||||
@ -172,7 +172,7 @@ set_state(State) ->
|
|||||||
get_items(NodeId, From) ->
|
get_items(NodeId, From) ->
|
||||||
node_hometree: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).
|
node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
|
||||||
|
|
||||||
get_item(NodeId, ItemId) ->
|
get_item(NodeId, ItemId) ->
|
||||||
@ -183,3 +183,6 @@ get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, Su
|
|||||||
|
|
||||||
set_item(Item) ->
|
set_item(Item) ->
|
||||||
node_hometree:set_item(Item).
|
node_hometree:set_item(Item).
|
||||||
|
|
||||||
|
get_item_name(Host, Node, Id) ->
|
||||||
|
node_hometree:get_item_name(Host, Node, Id).
|
||||||
|
@ -78,8 +78,7 @@ terminate(Host, ServerHost) ->
|
|||||||
node_hometree:terminate(Host, ServerHost).
|
node_hometree:terminate(Host, ServerHost).
|
||||||
|
|
||||||
options() ->
|
options() ->
|
||||||
[{node_type, buddy},
|
[{deliver_payloads, true},
|
||||||
{deliver_payloads, true},
|
|
||||||
{notify_config, false},
|
{notify_config, false},
|
||||||
{notify_delete, false},
|
{notify_delete, false},
|
||||||
{notify_retract, true},
|
{notify_retract, true},
|
||||||
|
@ -78,8 +78,7 @@ terminate(Host, ServerHost) ->
|
|||||||
node_hometree:terminate(Host, ServerHost).
|
node_hometree:terminate(Host, ServerHost).
|
||||||
|
|
||||||
options() ->
|
options() ->
|
||||||
[{node_type, club},
|
[{deliver_payloads, true},
|
||||||
{deliver_payloads, true},
|
|
||||||
{notify_config, false},
|
{notify_config, false},
|
||||||
{notify_delete, false},
|
{notify_delete, false},
|
||||||
{notify_retract, true},
|
{notify_retract, true},
|
||||||
|
154
src/mod_pubsub/node_dag.erl
Normal file
154
src/mod_pubsub/node_dag.erl
Normal file
@ -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 Cully <bjc@kublai.com>
|
||||||
|
%%% @version {@vsn}, {@date} {@time}
|
||||||
|
%%% @end
|
||||||
|
%%% ====================================================================
|
||||||
|
|
||||||
|
-module(node_dag).
|
||||||
|
-author('bjc@kublai.com').
|
||||||
|
|
||||||
|
-include("pubsub.hrl").
|
||||||
|
-include("jlib.hrl").
|
||||||
|
|
||||||
|
-behaviour(gen_pubsub_node).
|
||||||
|
|
||||||
|
%% API definition
|
||||||
|
-export([init/3, terminate/2,
|
||||||
|
options/0, features/0,
|
||||||
|
create_node_permission/6,
|
||||||
|
create_node/2,
|
||||||
|
delete_node/1,
|
||||||
|
purge_node/2,
|
||||||
|
subscribe_node/8,
|
||||||
|
unsubscribe_node/4,
|
||||||
|
publish_item/6,
|
||||||
|
delete_item/4,
|
||||||
|
remove_extra_items/3,
|
||||||
|
get_entity_affiliations/2,
|
||||||
|
get_node_affiliations/1,
|
||||||
|
get_affiliation/2,
|
||||||
|
set_affiliation/3,
|
||||||
|
get_entity_subscriptions/2,
|
||||||
|
get_node_subscriptions/1,
|
||||||
|
get_subscriptions/2,
|
||||||
|
set_subscriptions/3,
|
||||||
|
get_states/1,
|
||||||
|
get_state/2,
|
||||||
|
set_state/1,
|
||||||
|
get_items/6,
|
||||||
|
get_items/2,
|
||||||
|
get_item/7,
|
||||||
|
get_item/2,
|
||||||
|
set_item/1,
|
||||||
|
get_item_name/3]).
|
||||||
|
|
||||||
|
|
||||||
|
init(Host, ServerHost, Opts) ->
|
||||||
|
node_hometree:init(Host, ServerHost, Opts).
|
||||||
|
|
||||||
|
terminate(Host, ServerHost) ->
|
||||||
|
node_hometree:terminate(Host, ServerHost).
|
||||||
|
|
||||||
|
options() ->
|
||||||
|
[{node_type, leaf} | node_hometree:options()].
|
||||||
|
|
||||||
|
features() ->
|
||||||
|
["multi-collection" | node_hometree:features()].
|
||||||
|
|
||||||
|
create_node_permission(_Host, _ServerHost, _Node, _ParentNode,
|
||||||
|
_Owner, _Access) ->
|
||||||
|
{result, true}.
|
||||||
|
|
||||||
|
create_node(NodeID, Owner) ->
|
||||||
|
node_hometree:create_node(NodeID, Owner).
|
||||||
|
|
||||||
|
delete_node(Removed) ->
|
||||||
|
node_hometree:delete_node(Removed).
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
publish_item(NodeID, Publisher, Model, MaxItems, ItemID, Payload) ->
|
||||||
|
node_hometree:publish_item(NodeID, Publisher, Model, MaxItems,
|
||||||
|
ItemID, Payload).
|
||||||
|
|
||||||
|
remove_extra_items(NodeID, MaxItems, ItemIDs) ->
|
||||||
|
node_hometree:remove_extra_items(NodeID, MaxItems, ItemIDs).
|
||||||
|
|
||||||
|
delete_item(NodeID, Publisher, PublishModel, ItemID) ->
|
||||||
|
node_hometree:delete_item(NodeID, Publisher, PublishModel, ItemID).
|
||||||
|
|
||||||
|
purge_node(NodeID, Owner) ->
|
||||||
|
node_hometree:purge_node(NodeID, Owner).
|
||||||
|
|
||||||
|
get_entity_affiliations(Host, Owner) ->
|
||||||
|
node_hometree:get_entity_affiliations(Host, Owner).
|
||||||
|
|
||||||
|
get_node_affiliations(NodeID) ->
|
||||||
|
node_hometree:get_node_affiliations(NodeID).
|
||||||
|
|
||||||
|
get_affiliation(NodeID, Owner) ->
|
||||||
|
node_hometree:get_affiliation(NodeID, Owner).
|
||||||
|
|
||||||
|
set_affiliation(NodeID, Owner, Affiliation) ->
|
||||||
|
node_hometree:set_affiliation(NodeID, Owner, Affiliation).
|
||||||
|
|
||||||
|
get_entity_subscriptions(Host, Owner) ->
|
||||||
|
node_hometree:get_entity_subscriptions(Host, Owner).
|
||||||
|
|
||||||
|
get_node_subscriptions(NodeID) ->
|
||||||
|
node_hometree:get_node_subscriptions(NodeID).
|
||||||
|
|
||||||
|
get_subscriptions(NodeID, Owner) ->
|
||||||
|
node_hometree:get_subscriptions(NodeID, Owner).
|
||||||
|
|
||||||
|
set_subscriptions(NodeID, Owner, Subscriptions) ->
|
||||||
|
node_hometree:set_subscriptions(NodeID, Owner, Subscriptions).
|
||||||
|
|
||||||
|
get_states(NodeID) ->
|
||||||
|
node_hometree:get_states(NodeID).
|
||||||
|
|
||||||
|
get_state(NodeID, JID) ->
|
||||||
|
node_hometree:get_state(NodeID, JID).
|
||||||
|
|
||||||
|
set_state(State) ->
|
||||||
|
node_hometree:set_state(State).
|
||||||
|
|
||||||
|
get_items(NodeID, From) ->
|
||||||
|
node_hometree:get_items(NodeID, From).
|
||||||
|
|
||||||
|
get_items(NodeID, JID, AccessModel, PresenceSubscription,
|
||||||
|
RosterGroup, SubID) ->
|
||||||
|
node_hometree:get_items(NodeID, JID, AccessModel, PresenceSubscription,
|
||||||
|
RosterGroup, SubID).
|
||||||
|
|
||||||
|
get_item(NodeID, ItemID) ->
|
||||||
|
node_hometree:get_item(NodeID, ItemID).
|
||||||
|
|
||||||
|
get_item(NodeID, ItemID, JID, AccessModel, PresenceSubscription,
|
||||||
|
RosterGroup, SubID) ->
|
||||||
|
node_hometree:get_item(NodeID, ItemID, JID, AccessModel,
|
||||||
|
PresenceSubscription, RosterGroup, SubID).
|
||||||
|
|
||||||
|
set_item(Item) ->
|
||||||
|
node_hometree:set_item(Item).
|
||||||
|
|
||||||
|
get_item_name(Host, Node, ID) ->
|
||||||
|
node_hometree:get_item_name(Host, Node, ID).
|
@ -76,8 +76,7 @@ terminate(Host, ServerHost) ->
|
|||||||
node_hometree:terminate(Host, ServerHost).
|
node_hometree:terminate(Host, ServerHost).
|
||||||
|
|
||||||
options() ->
|
options() ->
|
||||||
[{node_type, dispatch},
|
[{deliver_payloads, true},
|
||||||
{deliver_payloads, true},
|
|
||||||
{notify_config, false},
|
{notify_config, false},
|
||||||
{notify_delete, false},
|
{notify_delete, false},
|
||||||
{notify_retract, true},
|
{notify_retract, true},
|
||||||
@ -129,7 +128,7 @@ publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) ->
|
|||||||
node_hometree:publish_item(
|
node_hometree:publish_item(
|
||||||
SubNode#pubsub_node.id, Publisher, Model,
|
SubNode#pubsub_node.id, Publisher, Model,
|
||||||
MaxItems, ItemId, Payload)
|
MaxItems, ItemId, Payload)
|
||||||
end, nodetree_default:get_subnodes(NodeId, Publisher)).
|
end, nodetree_tree:get_subnodes(NodeId, Publisher, Publisher)).
|
||||||
|
|
||||||
remove_extra_items(_NodeId, _MaxItems, ItemIds) ->
|
remove_extra_items(_NodeId, _MaxItems, ItemIds) ->
|
||||||
{result, {ItemIds, []}}.
|
{result, {ItemIds, []}}.
|
||||||
|
@ -69,8 +69,7 @@ terminate(Host, ServerHost) ->
|
|||||||
node_hometree:terminate(Host, ServerHost).
|
node_hometree:terminate(Host, ServerHost).
|
||||||
|
|
||||||
options() ->
|
options() ->
|
||||||
[{node_type, flat},
|
[{deliver_payloads, true},
|
||||||
{deliver_payloads, true},
|
|
||||||
{notify_config, false},
|
{notify_config, false},
|
||||||
{notify_delete, false},
|
{notify_delete, false},
|
||||||
{notify_retract, true},
|
{notify_retract, true},
|
||||||
|
@ -132,8 +132,7 @@ terminate(_Host, _ServerHost) ->
|
|||||||
%% {send_last_published_item, never},
|
%% {send_last_published_item, never},
|
||||||
%% {presence_based_delivery, false}]'''
|
%% {presence_based_delivery, false}]'''
|
||||||
options() ->
|
options() ->
|
||||||
[{node_type, default},
|
[{deliver_payloads, true},
|
||||||
{deliver_payloads, true},
|
|
||||||
{notify_config, false},
|
{notify_config, false},
|
||||||
{notify_delete, false},
|
{notify_delete, false},
|
||||||
{notify_retract, true},
|
{notify_retract, true},
|
||||||
@ -597,7 +596,7 @@ get_entity_affiliations(Host, Owner) ->
|
|||||||
States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}),
|
States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}),
|
||||||
NodeTree = case ets:lookup(gen_mod:get_module_proc(Host, config), nodetree) of
|
NodeTree = case ets:lookup(gen_mod:get_module_proc(Host, config), nodetree) of
|
||||||
[{nodetree, N}] -> N;
|
[{nodetree, N}] -> N;
|
||||||
_ -> nodetree_default
|
_ -> nodetree_tree
|
||||||
end,
|
end,
|
||||||
Reply = lists:foldl(fun(#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) ->
|
Reply = lists:foldl(fun(#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) ->
|
||||||
case NodeTree:get_node(N) of
|
case NodeTree:get_node(N) of
|
||||||
@ -654,7 +653,7 @@ get_entity_subscriptions(Host, Owner) ->
|
|||||||
end,
|
end,
|
||||||
NodeTree = case ets:lookup(gen_mod:get_module_proc(Host, config), nodetree) of
|
NodeTree = case ets:lookup(gen_mod:get_module_proc(Host, config), nodetree) of
|
||||||
[{nodetree, N}] -> N;
|
[{nodetree, N}] -> N;
|
||||||
_ -> nodetree_default
|
_ -> nodetree_tree
|
||||||
end,
|
end,
|
||||||
Reply = lists:foldl(fun(#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) ->
|
Reply = lists:foldl(fun(#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) ->
|
||||||
case NodeTree:get_node(N) of
|
case NodeTree:get_node(N) of
|
||||||
@ -774,9 +773,10 @@ get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -
|
|||||||
SubKey = jlib:jid_tolower(JID),
|
SubKey = jlib:jid_tolower(JID),
|
||||||
GenKey = jlib:jid_remove_resource(SubKey),
|
GenKey = jlib:jid_remove_resource(SubKey),
|
||||||
GenState = get_state(NodeId, GenKey),
|
GenState = get_state(NodeId, GenKey),
|
||||||
|
SubState = get_state(NodeId, SubKey),
|
||||||
Affiliation = GenState#pubsub_state.affiliation,
|
Affiliation = GenState#pubsub_state.affiliation,
|
||||||
Subscription = GenState#pubsub_state.subscriptions,
|
Subscriptions = SubState#pubsub_state.subscriptions,
|
||||||
Whitelisted = can_fetch_item(Affiliation, Subscription),
|
Whitelisted = can_fetch_item(Affiliation, Subscriptions),
|
||||||
if
|
if
|
||||||
%%SubID == "", ?? ->
|
%%SubID == "", ?? ->
|
||||||
%% Entity has multiple subscriptions to the node but does not specify a subscription ID
|
%% Entity has multiple subscriptions to the node but does not specify a subscription ID
|
||||||
@ -796,7 +796,7 @@ get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -
|
|||||||
(AccessModel == whitelist) and (not Whitelisted) ->
|
(AccessModel == whitelist) and (not Whitelisted) ->
|
||||||
%% Node has whitelist access model and entity lacks required affiliation
|
%% Node has whitelist access model and entity lacks required affiliation
|
||||||
{error, ?ERR_EXTENDED(?ERR_NOT_ALLOWED, "closed-node")};
|
{error, ?ERR_EXTENDED(?ERR_NOT_ALLOWED, "closed-node")};
|
||||||
(AccessModel == authorize) -> % TODO: to be done
|
(AccessModel == authorize) and (not Whitelisted) ->
|
||||||
%% Node has authorize access model
|
%% Node has authorize access model
|
||||||
{error, ?ERR_FORBIDDEN};
|
{error, ?ERR_FORBIDDEN};
|
||||||
%%MustPay ->
|
%%MustPay ->
|
||||||
@ -887,8 +887,10 @@ can_fetch_item(owner, _) -> true;
|
|||||||
can_fetch_item(member, _) -> true;
|
can_fetch_item(member, _) -> true;
|
||||||
can_fetch_item(publisher, _) -> true;
|
can_fetch_item(publisher, _) -> true;
|
||||||
can_fetch_item(outcast, _) -> false;
|
can_fetch_item(outcast, _) -> false;
|
||||||
can_fetch_item(none, subscribed) -> true;
|
can_fetch_item(none, Subscriptions) ->
|
||||||
can_fetch_item(none, none) -> false;
|
lists:any(fun ({subscribed, _SubID}) -> true;
|
||||||
|
(_) -> false
|
||||||
|
end, Subscriptions);
|
||||||
can_fetch_item(_Affiliation, _Subscription) -> false.
|
can_fetch_item(_Affiliation, _Subscription) -> false.
|
||||||
|
|
||||||
%% Returns the first item where Pred() is true in List
|
%% Returns the first item where Pred() is true in List
|
||||||
|
@ -81,8 +81,7 @@ terminate(Host, ServerHost) ->
|
|||||||
ok.
|
ok.
|
||||||
|
|
||||||
options() ->
|
options() ->
|
||||||
[{node_type, pep},
|
[{deliver_payloads, true},
|
||||||
{deliver_payloads, true},
|
|
||||||
{notify_config, false},
|
{notify_config, false},
|
||||||
{notify_delete, false},
|
{notify_delete, false},
|
||||||
{notify_retract, false},
|
{notify_retract, false},
|
||||||
|
@ -76,8 +76,7 @@ terminate(Host, ServerHost) ->
|
|||||||
ok.
|
ok.
|
||||||
|
|
||||||
options() ->
|
options() ->
|
||||||
[{node_type, pep},
|
[{deliver_payloads, true},
|
||||||
{deliver_payloads, true},
|
|
||||||
{notify_config, false},
|
{notify_config, false},
|
||||||
{notify_delete, false},
|
{notify_delete, false},
|
||||||
{notify_retract, false},
|
{notify_retract, false},
|
||||||
@ -174,7 +173,7 @@ get_entity_affiliations(_Host, Owner) ->
|
|||||||
States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}),
|
States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}),
|
||||||
NodeTree = case ets:lookup(gen_mod:get_module_proc(D, config), nodetree) of
|
NodeTree = case ets:lookup(gen_mod:get_module_proc(D, config), nodetree) of
|
||||||
[{nodetree, N}] -> N;
|
[{nodetree, N}] -> N;
|
||||||
_ -> nodetree_default
|
_ -> nodetree_tree
|
||||||
end,
|
end,
|
||||||
Reply = lists:foldl(fun(#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) ->
|
Reply = lists:foldl(fun(#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) ->
|
||||||
case NodeTree:get_node(N) of
|
case NodeTree:get_node(N) of
|
||||||
@ -206,7 +205,7 @@ get_entity_subscriptions(_Host, Owner) ->
|
|||||||
end,
|
end,
|
||||||
NodeTree = case ets:lookup(gen_mod:get_module_proc(D, config), nodetree) of
|
NodeTree = case ets:lookup(gen_mod:get_module_proc(D, config), nodetree) of
|
||||||
[{nodetree, N}] -> N;
|
[{nodetree, N}] -> N;
|
||||||
_ -> nodetree_default
|
_ -> nodetree_tree
|
||||||
end,
|
end,
|
||||||
Reply = lists:foldl(fun(#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) ->
|
Reply = lists:foldl(fun(#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) ->
|
||||||
case NodeTree:get_node(N) of
|
case NodeTree:get_node(N) of
|
||||||
|
@ -78,8 +78,7 @@ terminate(Host, ServerHost) ->
|
|||||||
node_hometree:terminate(Host, ServerHost).
|
node_hometree:terminate(Host, ServerHost).
|
||||||
|
|
||||||
options() ->
|
options() ->
|
||||||
[{node_type, private},
|
[{deliver_payloads, true},
|
||||||
{deliver_payloads, true},
|
|
||||||
{notify_config, false},
|
{notify_config, false},
|
||||||
{notify_delete, false},
|
{notify_delete, false},
|
||||||
{notify_retract, true},
|
{notify_retract, true},
|
||||||
|
@ -78,8 +78,7 @@ terminate(Host, ServerHost) ->
|
|||||||
node_hometree:terminate(Host, ServerHost).
|
node_hometree:terminate(Host, ServerHost).
|
||||||
|
|
||||||
options() ->
|
options() ->
|
||||||
[{node_type, public},
|
[{deliver_payloads, true},
|
||||||
{deliver_payloads, true},
|
|
||||||
{notify_config, false},
|
{notify_config, false},
|
||||||
{notify_delete, false},
|
{notify_delete, false},
|
||||||
{notify_retract, true},
|
{notify_retract, true},
|
||||||
|
246
src/mod_pubsub/nodetree_dag.erl
Normal file
246
src/mod_pubsub/nodetree_dag.erl
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
%%% ====================================================================
|
||||||
|
%%% ``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 Cully <bjc@kublai.com>
|
||||||
|
%%% @version {@vsn}, {@date} {@time}
|
||||||
|
%%% @end
|
||||||
|
%%% ====================================================================
|
||||||
|
|
||||||
|
-module(nodetree_dag).
|
||||||
|
-author('bjc@kublai.com').
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/3,
|
||||||
|
terminate/2,
|
||||||
|
options/0,
|
||||||
|
set_node/1,
|
||||||
|
get_node/3,
|
||||||
|
get_node/2,
|
||||||
|
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,
|
||||||
|
delete_node/2]).
|
||||||
|
|
||||||
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
|
|
||||||
|
-include("ejabberd.hrl").
|
||||||
|
-include("pubsub.hrl").
|
||||||
|
-include("jlib.hrl").
|
||||||
|
|
||||||
|
-behaviour(gen_pubsub_nodetree).
|
||||||
|
|
||||||
|
-define(DEFAULT_NODETYPE, leaf).
|
||||||
|
-define(DEFAULT_PARENTS, []).
|
||||||
|
-define(DEFAULT_CHILDREN, []).
|
||||||
|
|
||||||
|
-compile(export_all).
|
||||||
|
|
||||||
|
%%====================================================================
|
||||||
|
%% API
|
||||||
|
%%====================================================================
|
||||||
|
init(Host, ServerHost, Opts) ->
|
||||||
|
nodetree_tree:init(Host, ServerHost, Opts),
|
||||||
|
mnesia:transaction(fun create_node/5,
|
||||||
|
[Host, [], "default", service_jid(ServerHost), []]).
|
||||||
|
|
||||||
|
terminate(Host, ServerHost) ->
|
||||||
|
nodetree_tree:terminate(Host, ServerHost).
|
||||||
|
|
||||||
|
create_node(Key, NodeID, Type, Owner, Options) ->
|
||||||
|
OwnerJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
|
||||||
|
case find_node(Key, NodeID) of
|
||||||
|
false ->
|
||||||
|
ID = pubsub_index:new(node),
|
||||||
|
N = #pubsub_node{nodeid = oid(Key, NodeID),
|
||||||
|
id = ID,
|
||||||
|
type = Type,
|
||||||
|
owners = [OwnerJID],
|
||||||
|
options = Options},
|
||||||
|
case set_node(N) of
|
||||||
|
ok -> {ok, ID};
|
||||||
|
Other -> Other
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
{error, ?ERR_CONFLICT}
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_node(#pubsub_node{nodeid = {Key, _},
|
||||||
|
owners = Owners,
|
||||||
|
options = Options} = Node) ->
|
||||||
|
Parents = find_opt(collection, ?DEFAULT_PARENTS, Options),
|
||||||
|
case validate_parentage(Key, Owners, Parents) of
|
||||||
|
true ->
|
||||||
|
%% Update parents whenever the config changes.
|
||||||
|
mnesia:write(Node#pubsub_node{parents = Parents});
|
||||||
|
Other ->
|
||||||
|
Other
|
||||||
|
end.
|
||||||
|
|
||||||
|
delete_node(Key, NodeID) ->
|
||||||
|
case find_node(Key, NodeID) of
|
||||||
|
false ->
|
||||||
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
|
Node ->
|
||||||
|
%% Find all of N's children, update their configs to
|
||||||
|
%% remove N from the collection setting.
|
||||||
|
lists:foreach(fun (#pubsub_node{options = Opts} = Child) ->
|
||||||
|
NewOpts = remove_config_parent(NodeID, Opts),
|
||||||
|
Parents = find_opt(collection, ?DEFAULT_PARENTS, NewOpts),
|
||||||
|
ok = mnesia:write(pubsub_node,
|
||||||
|
Child#pubsub_node{
|
||||||
|
parents = Parents,
|
||||||
|
options = NewOpts},
|
||||||
|
write)
|
||||||
|
end, get_subnodes(Key, NodeID)),
|
||||||
|
|
||||||
|
%% Remove and return the requested node.
|
||||||
|
mnesia:delete_object(pubsub_node, Node, write),
|
||||||
|
[Node]
|
||||||
|
end.
|
||||||
|
|
||||||
|
options() ->
|
||||||
|
nodetree_tree:options().
|
||||||
|
|
||||||
|
get_node(Host, NodeID, _From) ->
|
||||||
|
get_node(Host, NodeID).
|
||||||
|
|
||||||
|
get_node(Host, NodeID) ->
|
||||||
|
case find_node(Host, NodeID) of
|
||||||
|
false -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||||
|
Node -> Node
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_node(NodeId) ->
|
||||||
|
nodetree_tree:get_node(NodeId).
|
||||||
|
|
||||||
|
get_nodes(Key, From) ->
|
||||||
|
nodetree_tree:get_nodes(Key, From).
|
||||||
|
|
||||||
|
get_nodes(Key) ->
|
||||||
|
nodetree_tree:get_nodes(Key).
|
||||||
|
|
||||||
|
get_parentnodes(Host, NodeID, _From) ->
|
||||||
|
case find_node(Host, NodeID) of
|
||||||
|
false -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||||
|
#pubsub_node{parents = Parents} ->
|
||||||
|
Q = qlc:q([N || #pubsub_node{nodeid = {NHost, NNode}} = N <- mnesia:table(pubsub_node),
|
||||||
|
Parent <- Parents,
|
||||||
|
Host == NHost,
|
||||||
|
Parent == NNode]),
|
||||||
|
qlc:e(Q)
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_parentnodes_tree(Host, NodeID, _From) ->
|
||||||
|
Pred = fun (NID, #pubsub_node{nodeid = {_, NNodeID}}) ->
|
||||||
|
NID == NNodeID
|
||||||
|
end,
|
||||||
|
Tr = fun (#pubsub_node{parents = Parents}) -> Parents end,
|
||||||
|
traversal_helper(Pred, Tr, Host, [NodeID]).
|
||||||
|
|
||||||
|
get_subnodes(Host, NodeID, _From) ->
|
||||||
|
get_subnodes(Host, NodeID).
|
||||||
|
|
||||||
|
get_subnodes(Host, NodeID) ->
|
||||||
|
case find_node(Host, NodeID) of
|
||||||
|
false -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||||
|
_ ->
|
||||||
|
Q = qlc:q([Node || #pubsub_node{nodeid = {NHost, _},
|
||||||
|
parents = Parents} = Node <- mnesia:table(pubsub_node),
|
||||||
|
Host == NHost,
|
||||||
|
lists:member(NodeID, Parents)]),
|
||||||
|
qlc:e(Q)
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_subnodes_tree(Host, NodeID, _From) ->
|
||||||
|
Pred = fun (NID, #pubsub_node{parents = Parents}) ->
|
||||||
|
lists:member(NID, Parents)
|
||||||
|
end,
|
||||||
|
Tr = fun (#pubsub_node{nodeid = {_, N}}) -> N end,
|
||||||
|
traversal_helper(Pred, Tr, Host, [NodeID]).
|
||||||
|
|
||||||
|
%%====================================================================
|
||||||
|
%% Internal functions
|
||||||
|
%%====================================================================
|
||||||
|
oid(Key, Name) -> {Key, Name}.
|
||||||
|
|
||||||
|
%% Key = jlib:jid() | host()
|
||||||
|
%% NodeID = string()
|
||||||
|
find_node(Key, NodeID) ->
|
||||||
|
case mnesia:read(pubsub_node, oid(Key, NodeID), read) of
|
||||||
|
[] -> false;
|
||||||
|
[Node] -> Node
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% Key = jlib:jid() | host()
|
||||||
|
%% Default = term()
|
||||||
|
%% Options = [{Key = atom(), Value = term()}]
|
||||||
|
find_opt(Key, Default, Options) ->
|
||||||
|
case lists:keysearch(Key, 1, Options) of
|
||||||
|
{value, {Key, Val}} -> Val;
|
||||||
|
_ -> Default
|
||||||
|
end.
|
||||||
|
|
||||||
|
traversal_helper(Pred, Tr, Host, NodeIDs) ->
|
||||||
|
traversal_helper(Pred, Tr, 0, Host, NodeIDs, []).
|
||||||
|
|
||||||
|
traversal_helper(_Pred, _Tr, _Depth, _Host, [], Acc) ->
|
||||||
|
Acc;
|
||||||
|
traversal_helper(Pred, Tr, Depth, Host, NodeIDs, Acc) ->
|
||||||
|
Q = qlc:q([Node || #pubsub_node{nodeid = {NHost, _}} = Node <- mnesia:table(pubsub_node),
|
||||||
|
NodeID <- NodeIDs,
|
||||||
|
Host == NHost,
|
||||||
|
Pred(NodeID, Node)]),
|
||||||
|
Nodes = qlc:e(Q),
|
||||||
|
IDs = lists:flatmap(Tr, Nodes),
|
||||||
|
traversal_helper(Pred, Tr, Depth + 1, Host, IDs, [{Depth, Nodes} | Acc]).
|
||||||
|
|
||||||
|
remove_config_parent(NodeID, Options) ->
|
||||||
|
remove_config_parent(NodeID, Options, []).
|
||||||
|
|
||||||
|
remove_config_parent(_NodeID, [], Acc) ->
|
||||||
|
lists:reverse(Acc);
|
||||||
|
remove_config_parent(NodeID, [{collection, Parents} | T], Acc) ->
|
||||||
|
remove_config_parent(NodeID, T,
|
||||||
|
[{collection, lists:delete(NodeID, Parents)} | Acc]);
|
||||||
|
remove_config_parent(NodeID, [H | T], Acc) ->
|
||||||
|
remove_config_parent(NodeID, T, [H | Acc]).
|
||||||
|
|
||||||
|
validate_parentage(_Key, _Owners, []) ->
|
||||||
|
true;
|
||||||
|
validate_parentage(Key, Owners, [[] | T]) ->
|
||||||
|
validate_parentage(Key, Owners, T);
|
||||||
|
validate_parentage(Key, Owners, [ParentID | T]) ->
|
||||||
|
case find_node(Key, ParentID) of
|
||||||
|
false -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||||
|
#pubsub_node{owners = POwners, options = POptions} ->
|
||||||
|
NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions),
|
||||||
|
MutualOwners = [O || O <- Owners, PO <- POwners,
|
||||||
|
O == PO],
|
||||||
|
case {MutualOwners, NodeType} of
|
||||||
|
{[], _} -> {error, ?ERR_NOT_ALLOWED};
|
||||||
|
{_, collection} -> validate_parentage(Key, Owners, T)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @spec (Host) -> jid()
|
||||||
|
%% Host = host()
|
||||||
|
%% @doc <p>Generate pubsub service JID.</p>
|
||||||
|
service_jid(Host) ->
|
||||||
|
case Host of
|
||||||
|
{U,S,_} -> {jid, U, S, "", U, S, ""};
|
||||||
|
_ -> {jid, "", Host, "", "", Host, ""}
|
||||||
|
end.
|
@ -36,6 +36,8 @@
|
|||||||
-module(nodetree_tree).
|
-module(nodetree_tree).
|
||||||
-author('christophe.romain@process-one.net').
|
-author('christophe.romain@process-one.net').
|
||||||
|
|
||||||
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
|
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
|
||||||
@ -50,6 +52,8 @@
|
|||||||
get_node/1,
|
get_node/1,
|
||||||
get_nodes/2,
|
get_nodes/2,
|
||||||
get_nodes/1,
|
get_nodes/1,
|
||||||
|
get_parentnodes/3,
|
||||||
|
get_parentnodes_tree/3,
|
||||||
get_subnodes/3,
|
get_subnodes/3,
|
||||||
get_subnodes_tree/3,
|
get_subnodes_tree/3,
|
||||||
create_node/5,
|
create_node/5,
|
||||||
@ -124,6 +128,32 @@ get_nodes(Host, _From) ->
|
|||||||
get_nodes(Host) ->
|
get_nodes(Host) ->
|
||||||
mnesia:match_object(#pubsub_node{nodeid = {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()
|
||||||
|
%% @doc <p>Default node tree does not handle parents, return empty list.</p>
|
||||||
|
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()
|
||||||
|
%% @doc <p>Default node tree does not handle parents, return a list
|
||||||
|
%% containing just this node.</p>
|
||||||
|
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}
|
%% @spec (Host, Node, From) -> [pubsubNode()] | {error, Reason}
|
||||||
%% Host = mod_pubsub:host()
|
%% Host = mod_pubsub:host()
|
||||||
%% Node = mod_pubsub:pubsubNode()
|
%% Node = mod_pubsub:pubsubNode()
|
||||||
@ -131,7 +161,11 @@ get_nodes(Host) ->
|
|||||||
get_subnodes(Host, Node, _From) ->
|
get_subnodes(Host, Node, _From) ->
|
||||||
get_subnodes(Host, Node).
|
get_subnodes(Host, Node).
|
||||||
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}
|
%% @spec (Host, Index) -> [pubsubNodeIdx()] | {error, Reason}
|
||||||
%% Host = mod_pubsub:host()
|
%% Host = mod_pubsub:host()
|
||||||
@ -180,7 +214,7 @@ create_node(Host, Node, Type, Owner, Options) ->
|
|||||||
NodeId = pubsub_index:new(node),
|
NodeId = pubsub_index:new(node),
|
||||||
mnesia:write(#pubsub_node{nodeid = {Host, Node},
|
mnesia:write(#pubsub_node{nodeid = {Host, Node},
|
||||||
id = NodeId,
|
id = NodeId,
|
||||||
parent = ParentNode,
|
parents = [ParentNode],
|
||||||
type = Type,
|
type = Type,
|
||||||
owners = [BJID],
|
owners = [BJID],
|
||||||
options = Options}),
|
options = Options}),
|
||||||
|
@ -47,6 +47,8 @@
|
|||||||
get_node/1,
|
get_node/1,
|
||||||
get_nodes/2,
|
get_nodes/2,
|
||||||
get_nodes/1,
|
get_nodes/1,
|
||||||
|
get_parentnodes/3,
|
||||||
|
get_parentnodes_tree/3,
|
||||||
get_subnodes/3,
|
get_subnodes/3,
|
||||||
get_subnodes_tree/3,
|
get_subnodes_tree/3,
|
||||||
create_node/5,
|
create_node/5,
|
||||||
@ -104,6 +106,22 @@ get_nodes(Host, _From) ->
|
|||||||
get_nodes(_Host) ->
|
get_nodes(_Host) ->
|
||||||
[].
|
[].
|
||||||
|
|
||||||
|
%% @spec (Host, Node, From) -> [pubsubNode()]
|
||||||
|
%% Host = mod_pubsub:host()
|
||||||
|
%% Node = mod_pubsub:pubsubNode()
|
||||||
|
%% From = mod_pubsub:jid()
|
||||||
|
%% @doc <p>Virtual node tree does not handle parent/child. Child list is empty.</p>
|
||||||
|
get_parentnodes(_Host, _Node, _From) ->
|
||||||
|
[].
|
||||||
|
|
||||||
|
%% @spec (Host, Node, From) -> [pubsubNode()]
|
||||||
|
%% Host = mod_pubsub:host()
|
||||||
|
%% Node = mod_pubsub:pubsubNode()
|
||||||
|
%% From = mod_pubsub:jid()
|
||||||
|
%% @doc <p>Virtual node tree does not handle parent/child. Child list is empty.</p>
|
||||||
|
get_parentnodes_tree(_Host, _Node, _From) ->
|
||||||
|
[].
|
||||||
|
|
||||||
%% @spec (Host, Node, From) -> [pubsubNode()]
|
%% @spec (Host, Node, From) -> [pubsubNode()]
|
||||||
%% Host = mod_pubsub:host()
|
%% Host = mod_pubsub:host()
|
||||||
%% Node = mod_pubsub:pubsubNode()
|
%% Node = mod_pubsub:pubsubNode()
|
||||||
|
@ -91,7 +91,7 @@
|
|||||||
%%% <p>The <tt>parentid</tt> and <tt>type</tt> fields are indexed.</p>
|
%%% <p>The <tt>parentid</tt> and <tt>type</tt> fields are indexed.</p>
|
||||||
-record(pubsub_node, {nodeid,
|
-record(pubsub_node, {nodeid,
|
||||||
id,
|
id,
|
||||||
parent,
|
parents = [],
|
||||||
type = "flat",
|
type = "flat",
|
||||||
owners = [],
|
owners = [],
|
||||||
options = []
|
options = []
|
||||||
|
Loading…
Reference in New Issue
Block a user