aboutsummaryrefslogtreecommitdiff
path: root/src/nodetree_tree.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/nodetree_tree.erl')
-rw-r--r--src/nodetree_tree.erl322
1 files changed, 322 insertions, 0 deletions
diff --git a/src/nodetree_tree.erl b/src/nodetree_tree.erl
new file mode 100644
index 000000000..23159d7a2
--- /dev/null
+++ b/src/nodetree_tree.erl
@@ -0,0 +1,322 @@
+%%% ====================================================================
+%%% ``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.
+%%%
+%%%
+%%% The Initial Developer of the Original Code is ProcessOne.
+%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
+%%% All Rights Reserved.''
+%%% This software is copyright 2006-2013, ProcessOne.
+%%%
+%%%
+%%% @copyright 2006-2013 ProcessOne
+%%% @author Christophe Romain <christophe.romain@process-one.net>
+%%% [http://www.process-one.net/]
+%%% @version {@vsn}, {@date} {@time}
+%%% @end
+%%% ====================================================================
+
+%%% @doc The module <strong>{@module}</strong> is the default PubSub node tree plugin.
+%%% <p>It is used as a default for all unknown PubSub node type. It can serve
+%%% as a developer basis and reference to build its own custom pubsub node tree
+%%% types.</p>
+%%% <p>PubSub node tree plugins are using the {@link gen_nodetree} behaviour.</p>
+%%% <p><strong>The API isn't stabilized yet</strong>. The pubsub plugin
+%%% development is still a work in progress. However, the system is already
+%%% useable and useful as is. Please, send us comments, feedback and
+%%% improvements.</p>
+
+-module(nodetree_tree).
+
+-author('christophe.romain@process-one.net').
+
+-include_lib("stdlib/include/qlc.hrl").
+
+-include("pubsub.hrl").
+
+-include("jlib.hrl").
+
+-behaviour(gen_pubsub_nodetree).
+
+-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/6,
+ delete_node/2]).
+
+%% ================
+%% API definition
+%% ================
+
+%% @spec (Host, ServerHost, Options) -> ok
+%% Host = string()
+%% ServerHost = string()
+%% Options = [{atom(), term()}]
+%% @doc <p>Called during pubsub modules initialisation. Any pubsub plugin must
+%% implement this function. It can return anything.</p>
+%% <p>This function is mainly used to trigger the setup task necessary for the
+%% plugin. It can be used for example by the developer to create the specific
+%% module database schema if it does not exists yet.</p>
+init(_Host, _ServerHost, _Options) ->
+ mnesia:create_table(pubsub_node,
+ [{disc_copies, [node()]},
+ {attributes, record_info(fields, pubsub_node)}]),
+ mnesia:add_table_index(pubsub_node, id),
+ NodesFields = record_info(fields, pubsub_node),
+ case mnesia:table_info(pubsub_node, attributes) of
+ NodesFields -> ok;
+ _ -> ok
+ end,
+ %% mnesia:transform_table(pubsub_state, ignore, StatesFields)
+ ok.
+%% @spec (Host, ServerHost) -> ok
+%% Host = string()
+%% ServerHost = string()
+
+%% @spec () -> Options
+%% Options = [mod_pubsub:nodeOption()]
+%% @doc Returns the default pubsub node tree options.
+terminate(_Host, _ServerHost) -> ok.
+
+options() -> [{virtual_tree, false}].
+
+%% @spec (Node) -> ok | {error, Reason}
+%% Node = mod_pubsub:pubsubNode()
+%% Reason = mod_pubsub:stanzaError()
+-spec(set_node/1 ::
+(
+ Node::mod_pubsub:pubsubNode())
+ -> ok
+).
+set_node(Node) when is_record(Node, pubsub_node) ->
+ mnesia:write(Node).
+%set_node(_) -> {error, ?ERR_INTERNAL_SERVER_ERROR}.
+
+get_node(Host, Node, _From) -> get_node(Host, Node).
+
+%% @spec (Host, NodeId) -> Node | {error, Reason}
+%% Host = mod_pubsub:host()
+%% NodeId = mod_pubsub:nodeId()
+%% Node = mod_pubsub:pubsubNode()
+%% Reason = mod_pubsub:stanzaError()
+-spec(get_node/2 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId())
+ -> mod_pubsub:pubsubNode()
+ | {error, xmlel()}
+).
+get_node(Host, NodeId) ->
+ case catch mnesia:read({pubsub_node, {Host, NodeId}}) of
+ [Record] when is_record(Record, pubsub_node) -> Record;
+ [] -> {error, ?ERR_ITEM_NOT_FOUND}
+% Error -> Error
+ end.
+
+-spec(get_node/1 ::
+(
+ NodeIdx::mod_pubsub:nodeIdx())
+ -> mod_pubsub:pubsubNode()
+ | {error, xmlel()}
+).
+get_node(NodeIdx) ->
+ case catch mnesia:index_read(pubsub_node, NodeIdx, #pubsub_node.id) of
+ [Record] when is_record(Record, pubsub_node) -> Record;
+ [] -> {error, ?ERR_ITEM_NOT_FOUND}
+% Error -> Error
+ end.
+
+get_nodes(Host, _From) -> get_nodes(Host).
+
+%% @spec (Host) -> Nodes | {error, Reason}
+%% Host = mod_pubsub:host()
+%% Nodes = [mod_pubsub:pubsubNode()]
+%% Reason = {aborted, atom()}
+-spec(get_nodes/1 ::
+(
+ Host::mod_pubsub:host())
+ -> [mod_pubsub:pubsubNode()]
+).
+get_nodes(Host) ->
+ mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}).
+
+%% @spec (Host, Node, From) -> []
+%% Host = mod_pubsub:host()
+%% NodeId = mod_pubsub:nodeId()
+%% From = mod_pubsub:jid()
+%% @doc <p>Default node tree does not handle parents, return empty list.</p>
+get_parentnodes(_Host, _NodeId, _From) -> [].
+
+%% @spec (Host, NodeId, From) -> [{Depth, Node}] | []
+%% Host = mod_pubsub:host()
+%% NodeId = mod_pubsub:nodeId()
+%% From = mod_pubsub:jid()
+%% Depth = integer()
+%% Node = mod_pubsub:pubsubNode()
+%% @doc <p>Default node tree does not handle parents, return a list
+%% containing just this node.</p>
+-spec(get_parentnodes_tree/3 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId(),
+ From :: jid())
+ -> [{0, [mod_pubsub:pubsubNode(),...]}]
+).
+get_parentnodes_tree(Host, NodeId, From) ->
+ case get_node(Host, NodeId, From) of
+ Node when is_record(Node, pubsub_node) -> [{0, [Node]}];
+ _Error -> []
+ end.
+
+%% @spec (Host, NodeId, From) -> Nodes
+%% Host = mod_pubsub:host()
+%% NodeId = mod_pubsub:nodeId()
+%% From = mod_pubsub:jid()
+%% Nodes = [mod_pubsub:pubsubNode()]
+get_subnodes(Host, NodeId, _From) ->
+ get_subnodes(Host, NodeId).
+
+-spec(get_subnodes/2 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId())
+ -> [mod_pubsub:pubsubNode()]
+).
+get_subnodes(Host, <<>>) ->
+ Q = qlc:q([N
+ || #pubsub_node{nodeid = {NHost, _},
+ parents = Parents} =
+ N
+ <- mnesia:table(pubsub_node),
+ Host == NHost, Parents == []]),
+ qlc:e(Q);
+get_subnodes(Host, 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).
+
+get_subnodes_tree(Host, Node, _From) ->
+ get_subnodes_tree(Host, Node).
+
+%% @spec (Host, NodeId) -> Nodes
+%% Host = mod_pubsub:host()
+%% NodeId = mod_pubsub:nodeId()
+%% Nodes = [] | [mod_pubsub:pubsubNode()]
+-spec(get_subnodes_tree/2 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId())
+ -> [mod_pubsub:pubsubNode()]
+).
+get_subnodes_tree(Host, NodeId) ->
+ case get_node(Host, NodeId) of
+ {error, _} -> [];
+ Rec ->
+ BasePlugin = jlib:binary_to_atom(<<"node_",
+ (Rec#pubsub_node.type)/binary>>),
+ BasePath = BasePlugin:node_to_path(NodeId),
+ mnesia:foldl(fun (#pubsub_node{nodeid = {H, N}} = R,
+ Acc) ->
+ Plugin = jlib:binary_to_atom(<<"node_",
+ (R#pubsub_node.type)/binary>>),
+ Path = Plugin:node_to_path(N),
+ case lists:prefix(BasePath, Path) and (H == Host) of
+ true -> [R | Acc];
+ false -> Acc
+ end
+ end,
+ [], pubsub_node)
+ end.
+
+%% @spec (Host, NodeId, Type, Owner, Options, Parents) ->
+%% {ok, NodeIdx} | {error, Reason}
+%% Host = mod_pubsub:host()
+%% NodeId = mod_pubsub:nodeId()
+%% Type = mod_pubsub:nodeType()
+%% Owner = mod_pubsub:jid()
+%% Options = [mod_pubsub:nodeOption()]
+%% Parents = [] | [mod_pubsub:nodeId()]
+%% NodeIdx = mod_pubsub:nodeIdx()
+%% Reason = mod_pubsub:stanzaError()
+-spec(create_node/6 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId(),
+ Type :: binary(),
+ Owner :: jid(),
+ Options :: mod_pubsub:nodeOptions(),
+ Parents :: [mod_pubsub:nodeId()])
+ -> {ok, NodeIdx::mod_pubsub:nodeIdx()}
+ %%%
+ | {error, xmlel()}
+).
+create_node(Host, NodeId, Type, Owner, Options, Parents) ->
+ BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
+ case catch mnesia:read({pubsub_node, {Host, NodeId}}) of
+ [] ->
+ ParentExists = case Host of
+ {_U, _S, _R} ->
+ %% This is special case for PEP handling
+ %% PEP does not uses hierarchy
+ true;
+ _ ->
+ case Parents of
+ [] -> true;
+ [Parent | _] ->
+ case catch mnesia:read({pubsub_node,
+ {Host, Parent}})
+ of
+ [#pubsub_node{owners =
+ [{[], Host, []}]}] ->
+ true;
+ [#pubsub_node{owners = Owners}] ->
+ lists:member(BJID, Owners);
+ _ -> false
+ end;
+ _ -> false
+ end
+ end,
+ case ParentExists of
+ true ->
+ NodeIdx = pubsub_index:new(node),
+ mnesia:write(#pubsub_node{nodeid = {Host, NodeId},
+ id = NodeIdx, parents = Parents,
+ type = Type, owners = [BJID],
+ options = Options}),
+ {ok, NodeIdx};
+ false -> {error, ?ERR_FORBIDDEN}
+ end;
+ _ -> {error, ?ERR_CONFLICT}
+ end.
+
+%% @spec (Host, NodeId) -> Removed
+%% Host = mod_pubsub:host()
+%% NodeId = mod_pubsub:nodeId()
+%% Removed = [mod_pubsub:pubsubNode()]
+-spec(delete_node/2 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId())
+ -> [mod_pubsub:pubsubNode(),...]
+).
+delete_node(Host, NodeId) ->
+ Removed = get_subnodes_tree(Host, NodeId),
+ lists:foreach(fun (#pubsub_node{nodeid = {_, SubNodeId}, id = SubNodeIdx}) ->
+ pubsub_index:free(node, SubNodeIdx),
+ mnesia:delete({pubsub_node, {Host, SubNodeId}})
+ end,
+ Removed),
+ Removed.