1 |
|
%%% XEP-0248: PubSub Collection Nodes |
2 |
|
%%% DAG stands for Directed Acyclic Graph |
3 |
|
%%% |
4 |
|
%%% ==================================================================== |
5 |
|
%%% ``The contents of this file are subject to the Erlang Public License, |
6 |
|
%%% Version 1.1, (the "License"); you may not use this file except in |
7 |
|
%%% compliance with the License. You should have received a copy of the |
8 |
|
%%% Erlang Public License along with this software. If not, it can be |
9 |
|
%%% retrieved via the world wide web at http://www.erlang.org/. |
10 |
|
%%% |
11 |
|
%%% |
12 |
|
%%% Software distributed under the License is distributed on an "AS IS" |
13 |
|
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See |
14 |
|
%%% the License for the specific language governing rights and limitations |
15 |
|
%%% under the License. |
16 |
|
%%% |
17 |
|
%%% |
18 |
|
%%% @author Brian Cully <bjc@kublai.com> |
19 |
|
%%% @end |
20 |
|
%%% ==================================================================== |
21 |
|
|
22 |
|
-module(nodetree_dag). |
23 |
|
-behaviour(gen_pubsub_nodetree). |
24 |
|
-author('bjc@kublai.com'). |
25 |
|
|
26 |
|
-include_lib("stdlib/include/qlc.hrl"). |
27 |
|
|
28 |
|
-include("pubsub.hrl"). |
29 |
|
-include("jlib.hrl"). |
30 |
|
|
31 |
|
-export([init/2, terminate/2, set_node/1, |
32 |
|
get_node/2, get_node/1, get_nodes/2, |
33 |
|
get_parentnodes_tree/3, |
34 |
|
get_subnodes/3, create_node/6, |
35 |
|
delete_node/2]). |
36 |
|
|
37 |
|
-ignore_xref([{mod_pubsub_db_backend, get_parentnodes_tree, 2}]). |
38 |
|
|
39 |
|
-define(DEFAULT_NODETYPE, leaf). |
40 |
|
-define(DEFAULT_PARENTS, []). |
41 |
|
-define(DEFAULT_CHILDREN, []). |
42 |
|
|
43 |
|
init(HostType, Opts) -> |
44 |
22 |
nodetree_tree:init(HostType, Opts). |
45 |
|
|
46 |
|
terminate(Host, ServerHost) -> |
47 |
22 |
nodetree_tree:terminate(Host, ServerHost). |
48 |
|
|
49 |
|
set_node(#pubsub_node{nodeid = {Key, _}, owners = Owners, options = Options} = Node) -> |
50 |
152 |
Parents = find_opt(collection, ?DEFAULT_PARENTS, Options), |
51 |
152 |
case validate_parentage(Key, Owners, Parents) of |
52 |
152 |
true -> mod_pubsub_db_backend:set_node(Node#pubsub_node{parents = Parents}); |
53 |
:-( |
Other -> Other |
54 |
|
end. |
55 |
|
|
56 |
|
create_node(Key, Node, Type, Owner, Options, Parents) -> |
57 |
147 |
OwnerJID = jid:to_lower(jid:to_bare(Owner)), |
58 |
147 |
case mod_pubsub_db_backend:find_node_by_name(Key, Node) of |
59 |
|
false -> |
60 |
147 |
N = #pubsub_node{nodeid = {Key, Node}, |
61 |
|
type = Type, parents = Parents, owners = [OwnerJID], |
62 |
|
options = Options}, |
63 |
147 |
set_node(N); |
64 |
|
_ -> |
65 |
:-( |
{error, mongoose_xmpp_errors:conflict()} |
66 |
|
end. |
67 |
|
|
68 |
|
delete_node(Key, Node) -> |
69 |
93 |
case mod_pubsub_db_backend:find_node_by_name(Key, Node) of |
70 |
|
false -> |
71 |
:-( |
{error, mongoose_xmpp_errors:item_not_found()}; |
72 |
|
Record -> |
73 |
93 |
lists:foreach(fun (#pubsub_node{options = Opts} = Child) -> |
74 |
1 |
NewOpts = remove_config_parent(Node, Opts), |
75 |
1 |
Parents = find_opt(collection, ?DEFAULT_PARENTS, NewOpts), |
76 |
1 |
{ok, _} = mod_pubsub_db_backend:set_node( |
77 |
|
Child#pubsub_node{parents = Parents, |
78 |
|
options = NewOpts}) |
79 |
|
end, |
80 |
|
get_subnodes(Key, Node)), |
81 |
93 |
mod_pubsub_db_backend:delete_node(Record), |
82 |
93 |
[Record] |
83 |
|
end. |
84 |
|
|
85 |
|
get_node(Key, Node) -> |
86 |
381 |
case mod_pubsub_db_backend:find_node_by_name(Key, Node) of |
87 |
14 |
false -> {error, mongoose_xmpp_errors:item_not_found()}; |
88 |
367 |
Record -> Record |
89 |
|
end. |
90 |
|
|
91 |
|
get_node(Node) -> |
92 |
220 |
nodetree_tree:get_node(Node). |
93 |
|
|
94 |
|
get_nodes(Key, From) -> |
95 |
30 |
nodetree_tree:get_nodes(Key, From). |
96 |
|
|
97 |
|
get_parentnodes_tree(Key, Node, _From) -> |
98 |
311 |
mod_pubsub_db_backend:get_parentnodes_tree(Key, Node). |
99 |
|
|
100 |
|
get_subnodes(Host, Node, _From) -> |
101 |
6 |
get_subnodes(Host, Node). |
102 |
|
|
103 |
|
get_subnodes(Host, <<>>) -> |
104 |
4 |
mod_pubsub_db_backend:get_subnodes(Host, <<>>); |
105 |
|
|
106 |
|
get_subnodes(Host, Node) -> |
107 |
95 |
case mod_pubsub_db_backend:find_node_by_name(Host, Node) of |
108 |
:-( |
false -> {error, mongoose_xmpp_errors:item_not_found()}; |
109 |
95 |
_ -> mod_pubsub_db_backend:get_subnodes(Host, Node) |
110 |
|
end. |
111 |
|
|
112 |
|
%%==================================================================== |
113 |
|
%% Internal functions |
114 |
|
%%==================================================================== |
115 |
|
|
116 |
|
%% Key = jid:jid() | host() |
117 |
|
%% Default = term() |
118 |
|
%% Options = [{Key = atom(), Value = term()}] |
119 |
|
find_opt(Key, Default, Options) -> |
120 |
174 |
case lists:keysearch(Key, 1, Options) of |
121 |
42 |
{value, {Key, Val}} -> Val; |
122 |
132 |
_ -> Default |
123 |
|
end. |
124 |
|
|
125 |
|
remove_config_parent(Node, Options) -> |
126 |
1 |
remove_config_parent(Node, Options, []). |
127 |
|
|
128 |
|
remove_config_parent(_Node, [], Acc) -> |
129 |
1 |
lists:reverse(Acc); |
130 |
|
remove_config_parent(Node, [{collection, Parents} | T], Acc) -> |
131 |
1 |
remove_config_parent(Node, T, [{collection, lists:delete(Node, Parents)} | Acc]); |
132 |
|
remove_config_parent(Node, [H | T], Acc) -> |
133 |
17 |
remove_config_parent(Node, T, [H | Acc]). |
134 |
|
|
135 |
|
-spec validate_parentage( |
136 |
|
Key :: mod_pubsub:hostPubsub(), |
137 |
|
Owners :: [jid:ljid(), ...], |
138 |
|
ParentNodes :: [mod_pubsub:nodeId()]) |
139 |
|
-> true | {error, exml:element()}. |
140 |
|
validate_parentage(_Key, _Owners, []) -> |
141 |
152 |
true; |
142 |
|
validate_parentage(Key, Owners, [[] | T]) -> |
143 |
:-( |
validate_parentage(Key, Owners, T); |
144 |
|
validate_parentage(Key, Owners, [<<>> | T]) -> |
145 |
:-( |
validate_parentage(Key, Owners, T); |
146 |
|
validate_parentage(Key, Owners, [ParentID | T]) -> |
147 |
21 |
case mod_pubsub_db_backend:find_node_by_name(Key, ParentID) of |
148 |
|
false -> |
149 |
:-( |
{error, mongoose_xmpp_errors:item_not_found()}; |
150 |
|
#pubsub_node{owners = POwners, options = POptions} -> |
151 |
21 |
NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions), |
152 |
21 |
MutualOwners = [O || O <- Owners, PO <- POwners, O == PO], |
153 |
21 |
case {MutualOwners, NodeType} of |
154 |
:-( |
{[], _} -> {error, mongoose_xmpp_errors:forbidden()}; |
155 |
21 |
{_, collection} -> validate_parentage(Key, Owners, T); |
156 |
:-( |
{_, _} -> {error, mongoose_xmpp_errors:not_allowed()} |
157 |
|
end |
158 |
|
end. |