24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-09-29 14:37:44 +02:00
xmpp.chapril.org-ejabberd/src/mod_pubsub/nodetree_tree.erl
Pablo Polvorin 938a4007b3 Initial port of pubsub changes(up to r2444) to exmpp branch.
pubsub odbc isn't ported yet.  
Not tested (only basic node creation and configuration), should still have losts of bugs to discover.

SVN Revision: 2533
2009-08-25 17:14:30 +00:00

238 lines
7.7 KiB
Erlang

%%% ====================================================================
%%% ``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-2009, ProcessOne
%%% All Rights Reserved.''
%%% This software is copyright 2006-2009, ProcessOne.
%%%
%%%
%%% @copyright 2006-2009 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_lib("exmpp/include/exmpp.hrl").
-include("pubsub.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/5,
delete_node/2
]).
%% ================
%% API definition
%% ================
%% @spec (Host, ServerHost, Opts) -> any()
%% Host = mod_pubsub:host()
%% ServerHost = host()
%% Opts = list()
%% @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, _Opts) ->
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
%% mnesia:transform_table(pubsub_state, ignore, StatesFields)
end,
ok.
terminate(_Host, _ServerHost) ->
ok.
%% @spec () -> [Option]
%% Option = mod_pubsub:nodetreeOption()
%% @doc Returns the default pubsub node tree options.
options() ->
[{virtual_tree, false}].
%% @spec (NodeRecord) -> ok | {error, Reason}
%% Record = mod_pubsub:pubsub_node()
set_node(Record) when is_record(Record, pubsub_node) ->
mnesia:write(Record);
set_node(_) ->
{error, 'internal-server-error'}.
%% @spec (Host, Node) -> pubsubNode() | {error, Reason}
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
get_node(Host, Node, _From) ->
get_node(Host, Node).
get_node(Host, Node) ->
case catch mnesia:read({pubsub_node, {Host, Node}}) of
[Record] when is_record(Record, pubsub_node) -> Record;
[] -> {error, 'item-not-found'};
Error -> Error
end.
get_node(NodeId) ->
case catch mnesia:index_read(pubsub_node, NodeId, #pubsub_node.id) of
[Record] when is_record(Record, pubsub_node) -> Record;
[] -> {error, 'item-not-found'};
Error -> Error
end.
%% @spec (Host) -> [pubsubNode()] | {error, Reason}
%% Host = mod_pubsub:host() | mod_pubsub:jid()
get_nodes(Host, _From) ->
get_nodes(Host).
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()
%% @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, [N]}];
Error -> Error
end.
%% @spec (Host, Node, From) -> [pubsubNode()] | {error, Reason}
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% From = mod_pubsub:jid()
get_subnodes(Host, Node, _From) ->
get_subnodes(Host, Node).
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).
%% @spec (Host, Index) -> [pubsubNodeIdx()] | {error, Reason}
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% From = mod_pubsub:jid()
get_subnodes_tree(Host, Node, _From) ->
get_subnodes_tree(Host, Node).
get_subnodes_tree(Host, Node) ->
mnesia:foldl(fun(#pubsub_node{nodeid = {H, N}} = R, Acc) ->
case lists:prefix(Node, N) and (H == Host) of
true -> [R | Acc];
_ -> Acc
end
end, [], pubsub_node).
%% @spec (Host, Node, Type, Owner, Options) -> ok | {error, Reason}
%% Host = mod_pubsub:host() | mod_pubsub:jid()
%% Node = mod_pubsub:pubsubNode()
%% NodeType = mod_pubsub:nodeType()
%% Owner = mod_pubsub:jid()
%% Options = list()
create_node(Host, Node, Type, Owner, Options) ->
BJID = jlib:short_prepd_bare_jid(Owner),
case mnesia:read({pubsub_node, {Host, Node}}) of
[] ->
{ParentNode, ParentExists} =
case Host of
{_U, _S, _R} ->
%% This is special case for PEP handling
%% PEP does not uses hierarchy
{[], true};
_ ->
Parent = lists:sublist(Node, length(Node) - 1),
case Parent of
[] ->
{[], true};
_ ->
case mnesia:read({pubsub_node, {Host, Parent}}) of
[] -> {Parent, false};
_ -> {Parent, lists:member(BJID, Parent#pubsub_node.owners)}
end
end
end,
case ParentExists of
true ->
NodeId = pubsub_index:new(node),
mnesia:write(#pubsub_node{nodeid = {Host, Node},
id = NodeId,
parents = [ParentNode],
type = Type,
owners = [BJID],
options = Options}),
{ok, NodeId};
false ->
%% Requesting entity is prohibited from creating nodes
{error, 'forbidden'}
end;
_ ->
%% NodeID already exists
{error, 'conflict'}
end.
%% @spec (Host, Node) -> [mod_pubsub:node()]
%% Host = mod_pubsub:host() | mod_pubsub:jid()
%% Node = mod_pubsub:pubsubNode()
delete_node(Host, Node) ->
Removed = get_subnodes_tree(Host, Node),
lists:foreach(fun(#pubsub_node{nodeid = {_, N}, id = I}) ->
pubsub_index:free(node, I),
mnesia:delete({pubsub_node, {Host, N}})
end, Removed),
Removed.