25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-24 16:23:40 +01:00

PubSub improvements

This commit contains
- code cleanup
- use of db_type instead of old mod_pubsub_odbc
- some minor optimizations
- some minor bugfixes
This commit is contained in:
Christophe Romain 2015-04-08 17:12:05 +02:00
parent 63926efd20
commit e0563e3918
31 changed files with 6999 additions and 15289 deletions

View File

@ -27,8 +27,7 @@
%% -------------------------------
%% Pubsub constants
-define(ERR_EXTENDED(E, C),
mod_pubsub:extended_error(E, C)).
-define(ERR_EXTENDED(E, C), mod_pubsub:extended_error(E, C)).
%% The actual limit can be configured with mod_pubsub's option max_items_node
-define(MAXITEMS, 10).
@ -40,7 +39,6 @@
%% -------------------------------
%% Pubsub types
%% @type hostPubsub() = string().
-type(hostPubsub() :: binary()).
%% <p><tt>hostPubsub</tt> is the name of the PubSub service. For example, it can be
%% <tt>"pubsub.localhost"</tt>.</p>
@ -60,11 +58,14 @@
%% @type nodeId() = binary().
%% <p>A node is defined by a list of its ancestors. The last element is the name
%% of the current node. For example:
%% of the current node. For example:
%% ```<<"/home/localhost/user">>'''</p>
-type(nodeIdx() :: pos_integer()).
%% @type nodeIdx() = integer().
-type(nodeIdx() :: pos_integer() | binary()).
%% @type nodeIdx() = integer() | binary().
%% note: pos_integer() should always be used, but we allow anything else coded
%% as binary, so one can have a custom implementation of nodetree with custom
%% indexing (see nodetree_virtual). this also allows to use any kind of key for
%% indexing nodes, as this can be usefull with external backends such as odbc.
-type(itemId() :: binary()).
%% @type itemId() = string().
@ -72,28 +73,12 @@
-type(subId() :: binary()).
%% @type subId() = string().
%% @type payload() = [#xmlelement{} | #xmlcdata{}].
%% @type stanzaError() = #xmlelement{}.
%% Example:
%% Example:
%% ```{xmlelement, "error",
%% [{"code", Code}, {"type", Type}],
%% [{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}'''
%% @type pubsubIQResponse() = #xmlelement{}.
%% Example:
%% ```{xmlelement, "pubsub",
%% [{"xmlns", ?NS_PUBSUB_EVENT}],
%% [{xmlelement, "affiliations", [],
%% []}]}'''
-type(nodeOption() ::
{Option::atom(),
Value::binary() | [binary()] | boolean() | non_neg_integer()
Value::atom() | [binary()] | boolean() | non_neg_integer()
}).
-type(nodeOptions() :: [NodeOption::mod_pubsub:nodeOption(),...]).
-type(nodeOptions() :: [mod_pubsub:nodeOption(),...]).
%% @type nodeOption() = {Option, Value}
%% Option = atom()
@ -106,26 +91,9 @@
Value::binary() | [binary()] | boolean()
}).
-type(subOptions() :: [SubOption::mod_pubsub:subOption(),...]).
-type(subOptions() :: [mod_pubsub:subOption(),...]).
%% @type nodeType() = string().
%% <p>The <tt>nodeType</tt> is a string containing the name of the PubSub
%% plugin to use to manage a given node. For example, it can be
%% <tt>"flat"</tt>, <tt>"hometree"</tt> or <tt>"blog"</tt>.</p>
%% @type jid() = {jid, User, Server, Resource, LUser, LServer, LResource}
%% User = string()
%% Server = string()
%% Resource = string()
%% LUser = string()
%% LServer = string()
%% LResource = string().
%-type(ljid() :: {binary(), binary(), binary()}).
%% @type ljid() = {User, Server, Resource}
%% User = string()
%% Server = string()
%% Resource = string().
-type(affiliation() :: 'none'
| 'owner'
@ -151,16 +119,11 @@
).
%% @type accessModel() = 'open' | 'presence' | 'roster' | 'authorize' | 'whitelist'.
%% @type pubsubIndex() = {pubsub_index, Index, Last, Free}
%% Index = atom()
%% Last = integer()
%% Free = [integer()].
%% internal pubsub index table
-type(publishModel() :: 'publishers'
| 'subscribers'
| 'open'
).
%% @type publishModel() = 'publishers' | 'subscribers' | 'open'
-record(pubsub_index,
{
@ -169,91 +132,42 @@
free :: [mod_pubsub:nodeIdx()]
}).
%% @type pubsubNode() = {pubsub_node, NodeId, Id, Parents, Type, Owners, Options}
%% NodeId = {host() | ljid(), nodeId()}
%% Id = nodeIdx()
%% Parents = [nodeId()]
%% Type = nodeType()
%% Owners = [ljid()]
%% Options = [nodeOption()].
%% <p>This is the format of the <tt>nodes</tt> table. The type of the table
%% is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
%% <p>The <tt>Parents</tt> and <tt>type</tt> fields are indexed.</p>
%% <tt>id</tt> can be anything you want.
-record(pubsub_node,
{
nodeid ,%:: {Host::mod_pubsub:host(), NodeId::mod_pubsub:nodeId()},
id ,%:: mod_pubsub:nodeIdx(),
parents = [] ,%:: [Parent_NodeId::mod_pubsub:nodeId()],
type = <<"flat">> ,%:: binary(),
owners = [] ,%:: [Owner::ljid(),...],
options = [] %:: mod_pubsub:nodeOptions()
nodeid ,% :: {mod_pubsub:host(), mod_pubsub:nodeId()},
id ,% :: mod_pubsub:nodeIdx(),
parents = [] ,% :: [mod_pubsub:nodeId(),...],
type = <<"flat">>,% :: binary(),
owners = [] ,% :: [jlib:ljid(),...],
options = [] % :: mod_pubsub:nodeOptions()
}).
%% @type pubsubState() = {pubsub_state, StateId, Items, Affiliation, Subscriptions}
%% StateId = {ljid(), nodeIdx()}
%% Items = [itemId()]
%% Affiliation = affiliation()
%% Subscriptions = [{subscription(), subId()}].
%% <p>This is the format of the <tt>affiliations</tt> table. The type of the
%% table is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
%-record(pubsub_state,
% {stateid, items = [], affiliation = none,
% subscriptions = []}).
-record(pubsub_state,
{
stateid ,%:: {Entity::ljid(), NodeIdx::mod_pubsub:nodeIdx()},
items = [] ,%:: [ItemId::mod_pubsub:itemId()],
affiliation = 'none' ,%:: mod_pubsub:affiliation(),
subscriptions = [] %:: [{mod_pubsub:subscription(), mod_pubsub:subId()}]
stateid ,% :: {jlib:ljid(), mod_pubsub:nodeIdx()},
items = [] ,% :: [mod_pubsub:itemId(),...],
affiliation = 'none',% :: mod_pubsub:affiliation(),
subscriptions = [] % :: [{mod_pubsub:subscription(), mod_pubsub:subId()}]
}).
%% @type pubsubItem() = {pubsub_item, ItemId, Creation, Modification, Payload}
%% ItemId = {itemId(), nodeIdx()}
%% Creation = {now(), ljid()}
%% Modification = {now(), ljid()}
%% Payload = payload().
%% <p>This is the format of the <tt>published items</tt> table. The type of the
%% table is: <tt>set</tt>,<tt>disc</tt>,<tt>fragmented</tt>.</p>
%-record(pubsub_item,
% {itemid, creation = {unknown, unknown},
% modification = {unknown, unknown}, payload = []}).
-record(pubsub_item,
{
itemid ,%:: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
creation = {unknown, unknown} ,%:: {erlang:timestamp(), ljid()},
modification = {unknown, unknown} ,%:: {erlang:timestamp(), ljid()},
payload = [] %:: mod_pubsub:payload()
itemid ,% :: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
creation = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
modification = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
payload = [] % :: mod_pubsub:payload()
}).
%% @type pubsubSubscription() = {pubsub_subscription, SubId, Options}
%% SubId = subId()
%% Options = [nodeOption()].
%% <p>This is the format of the <tt>subscriptions</tt> table. The type of the
%% table is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
%-record(pubsub_subscription, {subid, options}).
-record(pubsub_subscription,
{
subid ,%:: mod_pubsub:subId(),
options %:: [] | mod_pubsub:subOptions()
subid ,% :: mod_pubsub:subId(),
options = [] % :: mod_pubsub:subOptions()
}).
%% @type pubsubLastItem() = {pubsub_last_item, NodeId, ItemId, Creation, Payload}
%% NodeId = nodeIdx()
%% ItemId = itemId()
%% Creation = {now(),ljid()}
%% Payload = payload().
%% <p>This is the format of the <tt>last items</tt> table. it stores last item payload
%% for every node</p>
%-record(pubsub_last_item,
% {nodeid, itemid, creation, payload}).
-record(pubsub_last_item,
{
nodeid ,%:: mod_pubsub:nodeIdx(),
itemid ,%:: mod_pubsub:itemId(),
creation ,%:: {erlang:timestamp(), ljid()},
payload %:: mod_pubsub:payload()
nodeid ,% :: mod_pubsub:nodeIdx(),
itemid ,% :: mod_pubsub:itemId(),
creation ,% :: {erlang:timestamp(), jlib:ljid()},
payload % :: mod_pubsub:payload()
}).

View File

@ -32,191 +32,146 @@
-include("jlib.hrl").
-type(host() :: mod_pubsub:host()
| mod_pubsub_odbc:host()
).
-type(nodeId() :: mod_pubsub:nodeId()
| mod_pubsub_odbc:nodeId()
).
-type(nodeIdx() :: mod_pubsub:nodeIdx()
| mod_pubsub_odbc:nodeIdx()
).
-type(itemId() :: mod_pubsub:itemId()
| mod_pubsub_odbc:itemId()
).
-type(pubsubNode() :: mod_pubsub:pubsubNode()
| mod_pubsub_odbc:pubsubNode()
).
-type(pubsubState() :: mod_pubsub:pubsubState()
| mod_pubsub_odbc:pubsubState()
).
-type(pubsubItem() :: mod_pubsub:pubsubItem()
| mod_pubsub_odbc:pubsubItem()
).
-type(nodeOptions() :: mod_pubsub:nodeOptions()
| mod_pubsub_odbc:nodeOptions()
).
-type(subOptions() :: mod_pubsub:subOptions()
| mod_pubsub_odbc:subOptions()
).
-type(affiliation() :: mod_pubsub:affiliation()
| mod_pubsub_odbc:affiliation()
).
-type(subscription() :: mod_pubsub:subscription()
| mod_pubsub_odbc:subscription()
).
-type(subId() :: mod_pubsub:subId()
| mod_pubsub_odbc:subId()
).
-type(accessModel() :: mod_pubsub:accessModel()
| mod_pubsub_odbc:accessModel()
).
-type(publishModel() :: mod_pubsub:publishModel()
| mod_pubsub_odbc:publishModel()
).
-type(payload() :: mod_pubsub:payload()
| mod_pubsub_odbc:payload()
).
-type(host() :: mod_pubsub:host()).
-type(nodeId() :: mod_pubsub:nodeId()).
-type(nodeIdx() :: mod_pubsub:nodeIdx()).
-type(itemId() :: mod_pubsub:itemId()).
-type(pubsubNode() :: mod_pubsub:pubsubNode()).
-type(pubsubState() :: mod_pubsub:pubsubState()).
-type(pubsubItem() :: mod_pubsub:pubsubItem()).
-type(subOptions() :: mod_pubsub:subOptions()).
-type(affiliation() :: mod_pubsub:affiliation()).
-type(subscription() :: mod_pubsub:subscription()).
-type(subId() :: mod_pubsub:subId()).
-type(accessModel() :: mod_pubsub:accessModel()).
-type(publishModel() :: mod_pubsub:publishModel()).
-type(payload() :: mod_pubsub:payload()).
-callback init(Host :: binary(),
ServerHost :: binary(),
Opts :: [any()]) -> atom().
ServerHost :: binary(),
Opts :: [any()]) -> atom().
-callback terminate(Host :: host(),
ServerHost :: binary()) -> atom().
ServerHost :: binary()) -> atom().
-callback options() -> [{atom(), any()}].
-callback features() -> [binary()].
-callback create_node_permission(Host :: host(),
ServerHost :: binary(),
Node :: nodeId(),
ParentNode :: nodeId(),
Owner :: jid(), Access :: atom()) ->
ServerHost :: binary(),
Node :: nodeId(),
ParentNode :: nodeId(),
Owner :: jid(), Access :: atom()) ->
{result, boolean()}.
-callback create_node(NodeIdx :: nodeIdx(),
Owner :: jid()) ->
Owner :: jid()) ->
{result, {default, broadcast}}.
-callback delete_node(Nodes :: [pubsubNode(),...]) ->
{result,
{default, broadcast,
[{pubsubNode(),
[{ljid(), [{subscription(), subId()}]},...]},...]
}
}
{default, broadcast,
[{pubsubNode(),
[{ljid(), [{subscription(), subId()}]},...]},...]
}
}
|
{result,
{[],
[{pubsubNode(),
[{ljid(), [{subscription(), subId()}]},...]},...]
}
}.
{[],
[{pubsubNode(),
[{ljid(), [{subscription(), subId()}]},...]},...]
}
}.
-callback purge_node(NodeIdx :: nodeIdx(),
Owner :: jid()) ->
Owner :: jid()) ->
{result, {default, broadcast}} |
{error, xmlel()}.
-callback subscribe_node(NodeIdx :: nodeIdx(),
Sender :: jid(),
Subscriber :: ljid(),
AccessModel :: accessModel(),
SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence',
PresenceSubscription :: boolean(),
RosterGroup :: boolean(),
Options :: subOptions()) ->
Sender :: jid(),
Subscriber :: jid(),
AccessModel :: accessModel(),
SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence',
PresenceSubscription :: boolean(),
RosterGroup :: boolean(),
Options :: subOptions()) ->
{result, {default, subscribed, subId()}} |
{result, {default, subscribed, subId(), send_last}} |
{result, {default, pending, subId()}} |
{error, xmlel()}.
-callback unsubscribe_node(NodeIdx :: nodeIdx(),
Sender :: jid(),
Subscriber :: ljid(),
SubId :: subId()) ->
Sender :: jid(),
Subscriber :: jid(),
SubId :: subId()) ->
{result, default} |
{error, xmlel()}.
-callback publish_item(NodeId :: nodeIdx(),
Publisher :: jid(),
PublishModel :: publishModel(),
Max_Items :: non_neg_integer(),
ItemId :: <<>> | itemId(),
Payload :: payload()) ->
Publisher :: jid(),
PublishModel :: publishModel(),
Max_Items :: non_neg_integer(),
ItemId :: <<>> | itemId(),
Payload :: payload()) ->
{result, {default, broadcast, [itemId()]}} |
{error, xmlel()}.
-callback delete_item(NodeIdx :: nodeIdx(),
Publisher :: jid(),
PublishModel :: publishModel(),
ItemId :: <<>> | itemId()) ->
Publisher :: jid(),
PublishModel :: publishModel(),
ItemId :: <<>> | itemId()) ->
{result, {default, broadcast}} |
{error, xmlel()}.
-callback remove_extra_items(NodeIdx :: nodeIdx(),
Max_Items :: unlimited | non_neg_integer(),
ItemIds :: [itemId()]) ->
Max_Items :: unlimited | non_neg_integer(),
ItemIds :: [itemId()]) ->
{result, {[itemId()], [itemId()]}
}.
}.
-callback get_node_affiliations(NodeIdx :: nodeIdx()) ->
{result, [{ljid(), affiliation()}]}.
-callback get_entity_affiliations(Host :: host(),
Owner :: jid()) ->
Owner :: jid()) ->
{result, [{pubsubNode(), affiliation()}]}.
-callback get_affiliation(NodeIdx :: nodeIdx(),
Owner :: jid()) ->
Owner :: jid()) ->
{result, affiliation()}.
-callback set_affiliation(NodeIdx :: nodeIdx(),
Owner :: ljid(),
Affiliation :: affiliation()) ->
Owner :: jid(),
Affiliation :: affiliation()) ->
ok |
{error, xmlel()}.
-callback get_node_subscriptions(NodeIdx :: nodeIdx()) ->
{result,
[{ljid(), subscription(), subId()}] |
[{ljid(), none},...]
}.
[{ljid(), subscription(), subId()}] |
[{ljid(), none},...]
}.
-callback get_entity_subscriptions(Host :: host(),
Owner :: jid()) ->
Key :: jid()) ->
{result, [{pubsubNode(), subscription(), subId(), ljid()}]
}.
}.
-callback get_subscriptions(NodeIdx :: nodeIdx(),
Owner :: ljid()) ->
Owner :: jid()) ->
{result, [{subscription(), subId()}]}.
-callback get_pending_nodes(Host :: host(),
Owner :: jid()) ->
Owner :: jid()) ->
{result, [nodeId()]}.
-callback get_states(NodeIdx::nodeIdx()) ->
{result, [pubsubState()]}.
-callback get_state(NodeIdx :: nodeIdx(),
JID :: ljid()) ->
Key :: ljid()) ->
pubsubState().
-callback set_state(State::pubsubState()) ->
@ -224,30 +179,32 @@
{error, xmlel()}.
-callback get_items(NodeIdx :: nodeIdx(),
JID :: jid(),
AccessModel :: accessModel(),
Presence_Subscription :: boolean(),
RosterGroup :: boolean(),
SubId :: subId()) ->
{result, [pubsubItem()]} |
JID :: jid(),
AccessModel :: accessModel(),
Presence_Subscription :: boolean(),
RosterGroup :: boolean(),
SubId :: subId(),
RSM :: none | rsm_in()) ->
{result, {[pubsubItem()], none | rsm_out()}} |
{error, xmlel()}.
-callback get_items(NodeIdx :: nodeIdx(),
From :: jid()) ->
{result, [pubsubItem()]}.
From :: jid(),
RSM :: none | rsm_in()) ->
{result, {[pubsubItem()], none | rsm_out()}}.
-callback get_item(NodeIdx :: nodeIdx(),
ItemId :: itemId(),
JID :: jid(),
AccessModel :: accessModel(),
PresenceSubscription :: boolean(),
RosterGroup :: boolean(),
SubId :: subId()) ->
ItemId :: itemId(),
JID :: jid(),
AccessModel :: accessModel(),
PresenceSubscription :: boolean(),
RosterGroup :: boolean(),
SubId :: subId()) ->
{result, pubsubItem()} |
{error, xmlel()}.
-callback get_item(NodeIdx :: nodeIdx(),
ItemId :: itemId()) ->
ItemId :: itemId()) ->
{result, pubsubItem()} |
{error, xmlel()}.
@ -256,8 +213,8 @@
% | {error, _}.
-callback get_item_name(Host :: host(),
ServerHost :: binary(),
Node :: nodeId()) ->
ServerHost :: binary(),
Node :: nodeId()) ->
itemId().
-callback node_to_path(Node :: nodeId()) ->

View File

@ -32,49 +32,31 @@
-include("jlib.hrl").
-type(host() :: mod_pubsub:host()
| mod_pubsub_odbc:host()
).
-type(nodeId() :: mod_pubsub:nodeId()
| mod_pubsub_odbc:nodeId()
).
-type(nodeIdx() :: mod_pubsub:nodeIdx()
| mod_pubsub_odbc:nodeIdx()
).
-type(itemId() :: mod_pubsub:itemId()
| mod_pubsub_odbc:itemId()
).
-type(pubsubNode() :: mod_pubsub:pubsubNode()
| mod_pubsub_odbc:pubsubNode()
).
-type(nodeOptions() :: mod_pubsub:nodeOptions()
| mod_pubsub_odbc:nodeOptions()
).
-type(host() :: mod_pubsub:host()).
-type(nodeId() :: mod_pubsub:nodeId()).
-type(nodeIdx() :: mod_pubsub:nodeIdx()).
-type(pubsubNode() :: mod_pubsub:pubsubNode()).
-type(nodeOptions() :: mod_pubsub:nodeOptions()).
-callback init(Host :: host(),
ServerHost :: binary(),
Opts :: [any()]) -> atom().
ServerHost :: binary(),
Opts :: [any()]) -> atom().
-callback terminate(Host :: host(), ServerHost :: binary()) -> atom().
-callback options() -> nodeOptions().
-callback set_node(PubsubNode :: pubsubNode()) ->
ok | {result, NodeIdx::mod_pubsub_odbc:nodeIdx()} | {error, xmlel()}.
ok | {result, NodeIdx::nodeIdx()} | {error, xmlel()}.
-callback get_node(Host :: host(),
NodeId :: nodeId(),
From :: jid()) ->
NodeId :: nodeId(),
From :: jid()) ->
pubsubNode() |
{error, xmlel()}.
-callback get_node(Host :: host(),
NodeId :: nodeId()) ->
NodeId :: nodeId()) ->
pubsubNode() |
{error, xmlel()}.
@ -83,42 +65,43 @@
{error, xmlel()}.
-callback get_nodes(Host :: host(),
From :: jid())->
From :: jid())->
[pubsubNode()].
-callback get_nodes(Host :: host())->
[pubsubNode()].
-callback get_parentnodes(Host :: host(),
NodeId :: nodeId(),
From :: jid()) ->
NodeId :: nodeId(),
From :: jid()) ->
[pubsubNode()] |
{error, xmlel()}.
-callback get_parentnodes_tree(Host :: host(),
NodeId :: nodeId(),
From :: jid()) ->
NodeId :: nodeId(),
From :: jid()) ->
[{0, [pubsubNode(),...]}].
-callback get_subnodes(Host :: host(),
NodeId :: nodeId(),
From :: ljid()) ->
NodeId :: nodeId(),
From :: jid()) ->
[pubsubNode()].
-callback get_subnodes_tree(Host :: host(),
NodeId :: nodeId(),
From :: ljid()) ->
NodeId :: nodeId(),
From :: jid()) ->
[pubsubNode()].
-callback create_node(Host :: host(),
NodeId :: nodeId(),
Type :: binary(),
Owner :: jid(),
Options :: nodeOptions(),
Parents :: [nodeId()]) ->
NodeId :: nodeId(),
Type :: binary(),
Owner :: jid(),
Options :: nodeOptions(),
Parents :: [nodeId()]) ->
{ok, NodeIdx::nodeIdx()} |
{error, xmlel()}.
{error, xmlel()} |
{error, {virtual, {host(), nodeId()}}}.
-callback delete_node(Host :: host(),
NodeId :: nodeId()) ->
NodeId :: nodeId()) ->
[pubsubNode()].

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,194 +0,0 @@
%%% ====================================================================
%%% ``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-2015, ProcessOne
%%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne.
%%%
%%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time}
%%% @end
%%% ====================================================================
-module(__TO_BE_DEFINED__).
-author(__TO_BE_DEFINED__).
-include("pubsub.hrl").
-include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% Note on function definition
%% included is all defined plugin function
%% it's possible not to define some function at all
%% in that case, warning will be generated at compilation
%% and function call will fail,
%% then mod_pubsub will call function from node_hometree
%% (this makes code cleaner, but execution a little bit longer)
%% 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/4,
get_pending_nodes/2,
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() ->
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, open},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() ->
["create-nodes",
"delete-nodes",
"delete-items",
"instant-nodes",
"outcast-affiliation",
"persistent-items",
"publish",
"purge-nodes",
"retract-items",
"retrieve-affiliations",
"retrieve-items",
"retrieve-subscriptions",
"subscribe",
"subscription-notifications"
].
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
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, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
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).

View File

@ -17,45 +17,32 @@
%%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne.
%%%
%%%
%%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net>
%%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time}
%%% @end
%%% ====================================================================
-module(node_buddy).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% Note on function definition
%% included is all defined plugin function
%% it's possible not to define some function at all
%% in that case, warning will be generated at compilation
%% and function call will fail,
%% then mod_pubsub will call function from node_hometree
%% (this makes code cleaner, but execution a little bit longer)
%% 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/4,
get_pending_nodes/2, 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, node_to_path/1,
path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts).
@ -64,121 +51,127 @@ terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost).
options() ->
[{deliver_payloads, true}, {notify_config, false},
{notify_delete, false}, {notify_retract, true},
{purge_offline, false}, {persist_items, true},
{max_items, ?MAXITEMS}, {subscribe, true},
{access_model, presence}, {roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, presence},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() ->
[<<"create-nodes">>, <<"delete-nodes">>,
<<"delete-items">>, <<"instant-nodes">>, <<"item-ids">>,
<<"outcast-affiliation">>, <<"persistent-items">>,
<<"publish">>, <<"purge-nodes">>, <<"retract-items">>,
<<"retrieve-affiliations">>, <<"retrieve-items">>,
<<"retrieve-subscriptions">>, <<"subscribe">>,
<<"subscription-notifications">>].
[<<"create-nodes">>,
<<"delete-nodes">>,
<<"delete-items">>,
<<"instant-nodes">>,
<<"item-ids">>,
<<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>,
<<"subscription-notifications">>].
create_node_permission(Host, ServerHost, Node,
ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost,
Node, ParentNode, Owner, Access).
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) ->
node_hometree:create_node(NodeId, Owner).
create_node(Nidx, Owner) ->
node_hometree:create_node(Nidx, 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).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
node_hometree:unsubscribe_node(NodeId, Sender,
Subscriber, SubID).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
Payload) ->
node_hometree:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems,
ItemIds).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher,
PublishModel, ItemId).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
purge_node(NodeId, Owner) ->
node_hometree:purge_node(NodeId, Owner).
purge_node(Nidx, Owner) ->
node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) ->
node_hometree:get_node_affiliations(NodeId).
get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) ->
node_hometree:get_affiliation(NodeId, Owner).
get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner,
Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner).
get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner,
Subscription, SubId).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId).
get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) ->
node_hometree:get_state(NodeId, JID).
get_state(Nidx, JID) ->
node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State).
set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) ->
node_hometree:get_items(NodeId, From).
get_items(Nidx, From, RSM) ->
node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(NodeId, ItemId) ->
node_hometree:get_item(NodeId, ItemId).
get_item(Nidx, ItemId) ->
node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item).
set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node).
node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path).
path_to_node(Path) ->
node_flat:path_to_node(Path).

View File

@ -1,4 +1,4 @@
%%% ====================================================================
%% ====================================================================
%%% ``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
@ -17,45 +17,32 @@
%%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne.
%%%
%%%
%%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net>
%%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time}
%%% @end
%%% ====================================================================
-module(node_club).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% Note on function definition
%% included is all defined plugin function
%% it's possible not to define some function at all
%% in that case, warning will be generated at compilation
%% and function call will fail,
%% then mod_pubsub will call function from node_hometree
%% (this makes code cleaner, but execution a little bit longer)
%% 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/4,
get_pending_nodes/2, 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, node_to_path/1,
path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts).
@ -64,121 +51,126 @@ terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost).
options() ->
[{deliver_payloads, true}, {notify_config, false},
{notify_delete, false}, {notify_retract, true},
{purge_offline, false}, {persist_items, true},
{max_items, ?MAXITEMS}, {subscribe, true},
{access_model, authorize}, {roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, authorize},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() ->
[<<"create-nodes">>, <<"delete-nodes">>,
<<"delete-items">>, <<"instant-nodes">>,
<<"outcast-affiliation">>, <<"persistent-items">>,
<<"publish">>, <<"purge-nodes">>, <<"retract-items">>,
<<"retrieve-affiliations">>, <<"retrieve-items">>,
<<"retrieve-subscriptions">>, <<"subscribe">>,
<<"subscription-notifications">>].
[<<"create-nodes">>,
<<"delete-nodes">>,
<<"delete-items">>,
<<"instant-nodes">>,
<<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>,
<<"subscription-notifications">>].
create_node_permission(Host, ServerHost, Node,
ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost,
Node, ParentNode, Owner, Access).
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) ->
node_hometree:create_node(NodeId, Owner).
create_node(Nidx, Owner) ->
node_hometree:create_node(Nidx, 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).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
node_hometree:unsubscribe_node(NodeId, Sender,
Subscriber, SubID).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
Payload) ->
node_hometree:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems,
ItemIds).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher,
PublishModel, ItemId).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
purge_node(NodeId, Owner) ->
node_hometree:purge_node(NodeId, Owner).
purge_node(Nidx, Owner) ->
node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) ->
node_hometree:get_node_affiliations(NodeId).
get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) ->
node_hometree:get_affiliation(NodeId, Owner).
get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner,
Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner).
get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner,
Subscription, SubId).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId).
get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) ->
node_hometree:get_state(NodeId, JID).
get_state(Nidx, JID) ->
node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State).
set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) ->
node_hometree:get_items(NodeId, From).
get_items(Nidx, From, RSM) ->
node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(NodeId, ItemId) ->
node_hometree:get_item(NodeId, ItemId).
get_item(Nidx, ItemId) ->
node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item).
set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node).
node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path).
path_to_node(Path) ->
node_flat:path_to_node(Path).

View File

@ -5,39 +5,37 @@
%%% 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).
-behaviour(gen_pubsub_node).
-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/4,
get_pending_nodes/2, 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, node_to_path/1,
path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts).
@ -51,113 +49,108 @@ options() ->
features() ->
[<<"multi-collection">> | node_hometree:features()].
create_node_permission(_Host, _ServerHost, _Node,
_ParentNode, _Owner, _Access) ->
create_node_permission(_Host, _ServerHost, _Node, _ParentNode, _Owner, _Access) ->
{result, true}.
create_node(NodeID, Owner) ->
node_hometree:create_node(NodeID, Owner).
create_node(Nidx, Owner) ->
node_hometree:create_node(Nidx, 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).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(NodeID, Sender, Subscriber, SubID) ->
node_hometree:unsubscribe_node(NodeID, Sender,
Subscriber, SubID).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
publish_item(NodeID, Publisher, Model, MaxItems, ItemID,
Payload) ->
case nodetree_dag:get_node(NodeID) of
#pubsub_node{options = Options} ->
case find_opt(node_type, Options) of
collection ->
{error,
?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"publish">>)};
_ ->
node_hometree:publish_item(NodeID, Publisher, Model,
MaxItems, ItemID, Payload)
end;
Err -> Err
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
case nodetree_dag:get_node(Nidx) of
#pubsub_node{options = Options} ->
case find_opt(node_type, Options) of
collection ->
{error,
?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"publish">>)};
_ ->
node_hometree:publish_item(Nidx, Publisher, Model,
MaxItems, ItemId, Payload)
end;
Err -> Err
end.
find_opt(_, []) -> false;
find_opt(Option, [{Option, Value} | _]) -> Value;
find_opt(Option, [_ | T]) -> find_opt(Option, T).
remove_extra_items(NodeID, MaxItems, ItemIDs) ->
node_hometree:remove_extra_items(NodeID, MaxItems,
ItemIDs).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(NodeID, Publisher, PublishModel, ItemID) ->
node_hometree:delete_item(NodeID, Publisher,
PublishModel, ItemID).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
purge_node(NodeID, Owner) ->
node_hometree:purge_node(NodeID, Owner).
purge_node(Nidx, Owner) ->
node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeID) ->
node_hometree:get_node_affiliations(NodeID).
get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeID, Owner) ->
node_hometree:get_affiliation(NodeID, Owner).
get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeID, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeID, Owner,
Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeID, Owner) ->
node_hometree:get_subscriptions(NodeID, Owner).
get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeID, Owner, Subscription, SubID) ->
node_hometree:set_subscriptions(NodeID, Owner,
Subscription, SubID).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeID) -> node_hometree:get_states(NodeID).
get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeID, JID) ->
node_hometree:get_state(NodeID, JID).
get_state(Nidx, JID) ->
node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State).
set_state(State) ->
node_hometree:set_state(State).
get_items(NodeID, From) ->
node_hometree:get_items(NodeID, From).
get_items(Nidx, From, RSM) ->
node_hometree:get_items(Nidx, From, RSM).
get_items(NodeID, JID, AccessModel,
PresenceSubscription, RosterGroup, SubID) ->
node_hometree:get_items(NodeID, JID, AccessModel,
PresenceSubscription, RosterGroup, SubID).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(NodeID, ItemID) ->
node_hometree:get_item(NodeID, ItemID).
get_item(Nidx, ItemId) ->
node_hometree:get_item(Nidx, ItemId).
get_item(NodeID, ItemID, JID, AccessModel,
PresenceSubscription, RosterGroup, SubID) ->
node_hometree:get_item(NodeID, ItemID, JID, AccessModel,
PresenceSubscription, RosterGroup, SubID).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item).
set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, ID) ->
node_hometree:get_item_name(Host, Node, ID).
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_hometree:node_to_path(Node).
node_to_path(Node) ->
node_hometree:node_to_path(Node).
path_to_node(Path) -> node_hometree:path_to_node(Path).
path_to_node(Path) ->
node_hometree:path_to_node(Path).

View File

@ -17,43 +17,38 @@
%%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne.
%%%
%%%
%%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net>
%%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time}
%%% @end
%%% ====================================================================
-module(node_dispatch).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-include("jlib.hrl").
-behaviour(gen_pubsub_node).
%%% @doc <p>The <strong>{@module}</strong> module is a PubSub plugin whose
%%% goal is to republished each published item to all its children.</p>
%%% <p>Users cannot subscribe to this node, but are supposed to subscribe to
%%% its children.</p>
%%% This module can not work with virtual nodetree
%% 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/4,
get_pending_nodes/2, 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, node_to_path/1,
path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts).
@ -62,115 +57,129 @@ terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost).
options() ->
[{deliver_payloads, true}, {notify_config, false},
{notify_delete, false}, {notify_retract, true},
{purge_offline, false}, {persist_items, true},
{max_items, ?MAXITEMS}, {subscribe, true},
{access_model, open}, {roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, presence},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() ->
[<<"create-nodes">>, <<"delete-nodes">>,
<<"instant-nodes">>, <<"outcast-affiliation">>,
<<"persistent-items">>, <<"publish">>,
<<"retrieve-items">>].
[<<"create-nodes">>,
<<"delete-nodes">>,
<<"instant-nodes">>,
<<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>,
<<"retrieve-items">>].
create_node_permission(Host, ServerHost, Node,
ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost,
Node, ParentNode, Owner, Access).
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) ->
node_hometree:create_node(NodeId, Owner).
create_node(Nidx, Owner) ->
node_hometree:create_node(Nidx, Owner).
delete_node(Removed) ->
node_hometree:delete_node(Removed).
delete_node(Nodes) ->
node_hometree:delete_node(Nodes).
subscribe_node(_NodeId, _Sender, _Subscriber,
_AccessModel, _SendLast, _PresenceSubscription,
_RosterGroup, _Options) ->
subscribe_node(_Nidx, _Sender, _Subscriber, _AccessModel, _SendLast, _PresenceSubscription,
_RosterGroup, _Options) ->
{error, ?ERR_FORBIDDEN}.
unsubscribe_node(_NodeId, _Sender, _Subscriber,
_SubID) ->
unsubscribe_node(_Nidx, _Sender, _Subscriber, _SubId) ->
{error, ?ERR_FORBIDDEN}.
publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
Payload) ->
lists:foreach(fun (SubNode) ->
node_hometree:publish_item(SubNode#pubsub_node.id,
Publisher, Model, MaxItems,
ItemId, Payload)
end,
nodetree_tree:get_subnodes(NodeId, Publisher,
Publisher)).
publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
case nodetree_tree:get_node(Nidx) of
#pubsub_node{nodeid = {Host, Node}} ->
lists:foreach(fun (SubNode) ->
node_hometree:publish_item(SubNode#pubsub_node.id,
Publisher, PublishModel, MaxItems,
ItemId, Payload)
end,
nodetree_tree:get_subnodes(Host, Node, Publisher)),
{result, {default, broadcast, []}};
Error ->
Error
end.
remove_extra_items(_NodeId, _MaxItems, ItemIds) ->
remove_extra_items(_Nidx, _MaxItems, ItemIds) ->
{result, {ItemIds, []}}.
delete_item(_NodeId, _Publisher, _PublishModel,
_ItemId) ->
delete_item(_Nidx, _Publisher, _PublishModel, _ItemId) ->
{error, ?ERR_ITEM_NOT_FOUND}.
purge_node(_NodeId, _Owner) -> {error, ?ERR_FORBIDDEN}.
purge_node(_Nidx, _Owner) ->
{error, ?ERR_FORBIDDEN}.
get_entity_affiliations(_Host, _Owner) -> {result, []}.
get_entity_affiliations(_Host, _Owner) ->
{result, []}.
get_node_affiliations(_NodeId) -> {result, []}.
get_node_affiliations(_Nidx) ->
{result, []}.
get_affiliation(_NodeId, _Owner) -> {result, []}.
get_affiliation(_Nidx, _Owner) ->
{result, none}.
set_affiliation(NodeId, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner,
Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(Nidx, Owner, Affiliation).
get_entity_subscriptions(_Host, _Owner) -> {result, []}.
get_entity_subscriptions(_Host, _Owner) ->
{result, []}.
get_node_subscriptions(NodeId) ->
node_hometree:get_node_subscriptions(NodeId).
get_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(_NodeId, _Owner) -> {result, []}.
get_subscriptions(_Nidx, _Owner) ->
{result, []}.
set_subscriptions(NodeId, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner,
Subscription, SubId).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId).
get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) ->
node_hometree:get_state(NodeId, JID).
get_state(Nidx, JID) ->
node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State).
set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) ->
node_hometree:get_items(NodeId, From).
get_items(Nidx, From, RSM) ->
node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(NodeId, ItemId) ->
node_hometree:get_item(NodeId, ItemId).
get_item(Nidx, ItemId) ->
node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item).
set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node).
node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path).
path_to_node(Path) ->
node_flat:path_to_node(Path).

View File

@ -25,162 +25,156 @@
%%% ====================================================================
-module(node_flat).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-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/4,
get_pending_nodes/2, 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, node_to_path/1,
path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts).
init(_Host, _ServerHost, _Opts) ->
pubsub_subscription:init(),
mnesia:create_table(pubsub_state,
[{disc_copies, [node()]},
{type, ordered_set},
{attributes, record_info(fields, pubsub_state)}]),
mnesia:create_table(pubsub_item,
[{disc_only_copies, [node()]},
{attributes, record_info(fields, pubsub_item)}]),
ItemsFields = record_info(fields, pubsub_item),
case mnesia:table_info(pubsub_item, attributes) of
ItemsFields -> ok;
_ -> mnesia:transform_table(pubsub_item, ignore, ItemsFields)
end,
ok.
terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost).
options() ->
[{deliver_payloads, true}, {notify_config, false},
{notify_delete, false}, {notify_retract, true},
{purge_offline, false}, {persist_items, true},
{max_items, ?MAXITEMS}, {subscribe, true},
{access_model, open}, {roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, false}].
node_hometree:options().
features() -> node_hometree:features().
features() ->
node_hometree:features().
%% use same code as node_hometree, but do not limite node to
%% the home/localhost/user/... hierarchy
%% any node is allowed
create_node_permission(Host, ServerHost, _Node,
_ParentNode, Owner, Access) ->
create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) ->
LOwner = jlib:jid_tolower(Owner),
Allowed = case LOwner of
{<<"">>, Host, <<"">>} ->
true; % pubsub service always allowed
_ ->
acl:match_rule(ServerHost, Access, LOwner) =:= allow
end,
{<<"">>, Host, <<"">>} ->
true; % pubsub service always allowed
_ ->
acl:match_rule(ServerHost, Access, LOwner) =:= allow
end,
{result, Allowed}.
create_node(NodeId, Owner) ->
node_hometree:create_node(NodeId, Owner).
create_node(Nidx, Owner) ->
node_hometree:create_node(Nidx, 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).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(Nidx, Sender, Subscriber,
AccessModel, SendLast, PresenceSubscription,
RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
node_hometree:unsubscribe_node(NodeId, Sender,
Subscriber, SubID).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
Payload) ->
node_hometree:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems,
ItemIds).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher,
PublishModel, ItemId).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
purge_node(NodeId, Owner) ->
node_hometree:purge_node(NodeId, Owner).
purge_node(Nidx, Owner) ->
node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) ->
node_hometree:get_node_affiliations(NodeId).
get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) ->
node_hometree:get_affiliation(NodeId, Owner).
get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner,
Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner).
get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner,
Subscription, SubId).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId).
get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) ->
node_hometree:get_state(NodeId, JID).
get_state(Nidx, JID) ->
node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State).
set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) ->
node_hometree:get_items(NodeId, From).
get_items(Nidx, From, RSM) ->
node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(NodeId, ItemId) ->
node_hometree:get_item(NodeId, ItemId).
get_item(Nidx, ItemId) ->
node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item).
set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> [(Node)].
node_to_path(Node) ->
[(Node)].
path_to_node(Path) ->
case Path of
% default slot
[Node] -> iolist_to_binary(Node);
% handle old possible entries, used when migrating database content to new format
[Node | _] when is_binary(Node) ->
iolist_to_binary(str:join([<<"">> | Path], <<"/">>));
% default case (used by PEP for example)
_ -> iolist_to_binary(Path)
% default slot
[Node] -> iolist_to_binary(Node);
% handle old possible entries, used when migrating database content to new format
[Node | _] when is_binary(Node) ->
iolist_to_binary(str:join([<<"">> | Path], <<"/">>));
% default case (used by PEP for example)
_ -> iolist_to_binary(Path)
end.

View File

@ -25,30 +25,25 @@
%%% ====================================================================
-module(node_flat_odbc).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-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_entity_subscriptions_for_send_last/2,
get_node_subscriptions/1, get_subscriptions/2,
set_subscriptions/4, get_pending_nodes/2, get_states/1,
get_state/2, set_state/1, get_items/7, get_items/6,
get_items/3, get_items/2, get_item/7, get_item/2,
set_item/1, get_item_name/3, get_last_items/3,
node_to_path/1, path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1,
get_entity_subscriptions_for_send_last/2, get_last_items/3]).
init(Host, ServerHost, Opts) ->
node_hometree_odbc:init(Host, ServerHost, Opts).
@ -57,157 +52,115 @@ terminate(Host, ServerHost) ->
node_hometree_odbc:terminate(Host, ServerHost).
options() ->
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, open},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, false}, {odbc, true},
{rsm, true}].
[{odbc, true}, {rsm, true} | node_flat:options()].
features() -> node_hometree_odbc:features().
features() ->
[<<"rsm">> | node_flat:features()].
%% use same code as node_hometree_odbc, but do not limite node to
%% the home/localhost/user/... hierarchy
%% any node is allowed
create_node_permission(Host, ServerHost, _Node,
_ParentNode, Owner, Access) ->
LOwner = jlib:jid_tolower(Owner),
Allowed = case LOwner of
{<<"">>, Host, <<"">>} ->
true; % pubsub service always allowed
_ ->
acl:match_rule(ServerHost, Access, LOwner) =:= allow
end,
{result, Allowed}.
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) ->
node_hometree_odbc:create_node(NodeId, Owner).
create_node(Nidx, Owner) ->
node_hometree_odbc:create_node(Nidx, Owner).
delete_node(Removed) ->
node_hometree_odbc:delete_node(Removed).
subscribe_node(NodeId, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree_odbc:subscribe_node(NodeId, Sender,
Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup,
Options).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree_odbc:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
node_hometree_odbc:unsubscribe_node(NodeId, Sender,
Subscriber, SubID).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree_odbc:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
Payload) ->
node_hometree_odbc:publish_item(NodeId, Publisher,
Model, MaxItems, ItemId, Payload).
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree_odbc:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree_odbc:remove_extra_items(NodeId, MaxItems,
ItemIds).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree_odbc:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
node_hometree_odbc:delete_item(NodeId, Publisher,
PublishModel, ItemId).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree_odbc:delete_item(Nidx, Publisher, PublishModel, ItemId).
purge_node(NodeId, Owner) ->
node_hometree_odbc:purge_node(NodeId, Owner).
purge_node(Nidx, Owner) ->
node_hometree_odbc:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) ->
node_hometree_odbc:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) ->
node_hometree_odbc:get_node_affiliations(NodeId).
get_node_affiliations(Nidx) ->
node_hometree_odbc:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) ->
node_hometree_odbc:get_affiliation(NodeId, Owner).
get_affiliation(Nidx, Owner) ->
node_hometree_odbc:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) ->
node_hometree_odbc:set_affiliation(NodeId, Owner,
Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree_odbc:set_affiliation(Nidx, Owner, Affiliation).
get_entity_subscriptions(Host, Owner) ->
node_hometree_odbc:get_entity_subscriptions(Host,
Owner).
node_hometree_odbc:get_entity_subscriptions(Host, Owner).
get_entity_subscriptions_for_send_last(Host, Owner) ->
node_hometree_odbc:get_entity_subscriptions_for_send_last(Host,
Owner).
node_hometree_odbc:get_entity_subscriptions_for_send_last(Host, Owner).
get_node_subscriptions(NodeId) ->
node_hometree_odbc:get_node_subscriptions(NodeId).
get_node_subscriptions(Nidx) ->
node_hometree_odbc:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) ->
node_hometree_odbc:get_subscriptions(NodeId, Owner).
get_subscriptions(Nidx, Owner) ->
node_hometree_odbc:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) ->
node_hometree_odbc:set_subscriptions(NodeId, Owner,
Subscription, SubId).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree_odbc:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree_odbc:get_pending_nodes(Host, Owner).
get_states(NodeId) ->
node_hometree_odbc:get_states(NodeId).
get_states(Nidx) ->
node_hometree_odbc:get_states(Nidx).
get_state(NodeId, JID) ->
node_hometree_odbc:get_state(NodeId, JID).
get_state(Nidx, JID) ->
node_hometree_odbc:get_state(Nidx, JID).
set_state(State) -> node_hometree_odbc:set_state(State).
set_state(State) ->
node_hometree_odbc:set_state(State).
get_items(NodeId, From) ->
node_hometree_odbc:get_items(NodeId, From).
get_items(Nidx, From, RSM) ->
node_hometree_odbc:get_items(Nidx, From, RSM).
get_items(NodeId, From, RSM) ->
node_hometree_odbc:get_items(NodeId, From, RSM).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree_odbc:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, none).
get_item(Nidx, ItemId) ->
node_hometree_odbc:get_item(Nidx, ItemId).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree_odbc:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree_odbc:get_item(Nidx, ItemId, JID,
AccessModel, PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) ->
node_hometree_odbc:get_item(NodeId, ItemId).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree_odbc:get_item(NodeId, ItemId, JID,
AccessModel, PresenceSubscription, RosterGroup,
SubId).
set_item(Item) -> node_hometree_odbc:set_item(Item).
set_item(Item) ->
node_hometree_odbc:set_item(Item).
get_item_name(Host, Node, Id) ->
node_hometree_odbc:get_item_name(Host, Node, Id).
get_last_items(NodeId, From, Count) ->
node_hometree_odbc:get_last_items(NodeId, From, Count).
get_last_items(Nidx, From, Count) ->
node_hometree_odbc:get_last_items(Nidx, From, Count).
node_to_path(Node) -> [(Node)].
node_to_path(Node) ->
[(Node)].
path_to_node(Path) ->
case Path of
% default slot
[Node] -> iolist_to_binary(Node);
% handle old possible entries, used when migrating database content to new format
[Node | _] when is_binary(Node) ->
iolist_to_binary(str:join([<<"">> | Path], <<"/">>));
% default case (used by PEP for example)
_ -> iolist_to_binary(Path)
% default slot
[Node] -> iolist_to_binary(Node);
% handle old possible entries, used when migrating database content to new format
[Node | _] when is_binary(Node) ->
iolist_to_binary(str:join([<<"">> | Path], <<"/">>));
% default case (used by PEP for example)
_ -> iolist_to_binary(Path)
end.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -17,49 +17,41 @@
%%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne.
%%%
%%%
%%% @copyright 2006-2015 ProcessOne
%%% @author Eric Cestari <eric@ohmforce.com>
%%% @version {@vsn}, {@date} {@time}
%%% @end
%%% ====================================================================
-module(node_mb).
-behaviour(gen_pubsub_node).
-author('eric@ohmforce.com').
-include("pubsub.hrl").
-include("jlib.hrl").
%%% @doc The module <strong>{@module}</strong> is the pep microblog PubSub plugin.
%%% <p> To be used, mod_pubsub must be configured :
%%% {mod_pubsub, [ % requires mod_caps
%%% {access_createnode, pubsub_createnode},
%%% {plugins, ["default", "pep","mb"]},
%%% {pep_mapping, [{"urn:xmpp:microblog", "mb"}]}
%%% ]},
%%% {access_createnode, pubsub_createnode},
%%% {plugins, ["default", "pep","mb"]},
%%% {pep_mapping, [{"urn:xmpp:microblog", "mb"}]}
%%% ]},
%%% </p>
%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
-module(node_mb).
-author('eric@ohmforce.com').
-include("ejabberd.hrl").
-include("logger.hrl").
-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/4,
get_pending_nodes/2, 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, node_to_path/1,
path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
init(Host, ServerHost, Opts) ->
node_pep:init(Host, ServerHost, Opts).
@ -68,124 +60,127 @@ terminate(Host, ServerHost) ->
node_pep:terminate(Host, ServerHost), ok.
options() ->
[{deliver_payloads, true}, {notify_config, false},
{notify_delete, false}, {notify_retract, false},
{purge_offline, false}, {persist_items, true},
{max_items, ?MAXITEMS}, {subscribe, true},
{access_model, presence}, {roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, true}].
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, false},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, presence},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, true}].
features() ->
[<<"create-nodes">>, %*
<<"auto-create">>, %*
<<"auto-subscribe">>, %*
<<"delete-nodes">>, %*
<<"delete-items">>, %*
<<"filtered-notifications">>, %*
<<"modify-affiliations">>, <<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>, %*
<<"purge-nodes">>, <<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>, %*
<<"retrieve-subscriptions">>, <<"subscribe">>].
[<<"create-nodes">>,
<<"auto-create">>,
<<"auto-subscribe">>,
<<"delete-nodes">>,
<<"delete-items">>,
<<"filtered-notifications">>,
<<"modify-affiliations">>,
<<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>].
create_node_permission(Host, ServerHost, Node,
ParentNode, Owner, Access) ->
node_pep:create_node_permission(Host, ServerHost, Node,
ParentNode, Owner, Access).
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_pep:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) ->
node_pep:create_node(NodeId, Owner).
create_node(Nidx, Owner) ->
node_pep:create_node(Nidx, Owner).
delete_node(Removed) -> node_pep:delete_node(Removed).
delete_node(Removed) ->
node_pep:delete_node(Removed).
subscribe_node(NodeId, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_pep:subscribe_node(NodeId, Sender, Subscriber,
AccessModel, SendLast, PresenceSubscription,
RosterGroup, Options).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_pep:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
node_pep:unsubscribe_node(NodeId, Sender, Subscriber,
SubID).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_pep:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
Payload) ->
node_pep:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_pep:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_pep:remove_extra_items(NodeId, MaxItems, ItemIds).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_pep:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
node_pep:delete_item(NodeId, Publisher, PublishModel,
ItemId).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_pep:delete_item(Nidx, Publisher, PublishModel, ItemId).
purge_node(NodeId, Owner) ->
node_pep:purge_node(NodeId, Owner).
purge_node(Nidx, Owner) ->
node_pep:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) ->
node_pep:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) ->
node_pep:get_node_affiliations(NodeId).
get_node_affiliations(Nidx) ->
node_pep:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) ->
node_pep:get_affiliation(NodeId, Owner).
get_affiliation(Nidx, Owner) ->
node_pep:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) ->
node_pep:set_affiliation(NodeId, Owner, Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_pep:set_affiliation(Nidx, Owner, Affiliation).
get_entity_subscriptions(Host, Owner) ->
node_pep:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(NodeId) ->
node_pep:get_node_subscriptions(NodeId).
get_node_subscriptions(Nidx) ->
node_pep:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) ->
node_pep:get_subscriptions(NodeId, Owner).
get_subscriptions(Nidx, Owner) ->
node_pep:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) ->
node_pep:set_subscriptions(NodeId, Owner, Subscription,
SubId).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_pep:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_pep:get_states(NodeId).
get_states(Nidx) ->
node_pep:get_states(Nidx).
get_state(NodeId, JID) ->
node_pep:get_state(NodeId, JID).
get_state(Nidx, JID) ->
node_pep:get_state(Nidx, JID).
set_state(State) -> node_pep:set_state(State).
set_state(State) ->
node_pep:set_state(State).
get_items(NodeId, From) ->
node_pep:get_items(NodeId, From).
get_items(Nidx, From, RSM) ->
node_pep:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_pep:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_pep:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM).
get_item(NodeId, ItemId) ->
node_pep:get_item(NodeId, ItemId).
get_item(Nidx, ItemId) ->
node_pep:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_pep:get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_pep:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_pep:set_item(Item).
set_item(Item) ->
node_pep:set_item(Item).
get_item_name(Host, Node, Id) ->
node_pep:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_pep:node_to_path(Node).
node_to_path(Node) ->
node_pep:node_to_path(Node).
path_to_node(Path) -> node_pep:path_to_node(Path).
path_to_node(Path) ->
node_pep:path_to_node(Path).

View File

@ -17,7 +17,6 @@
%%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne.
%%%
%%%
%%% @copyright 2006-2015 ProcessOne
%%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/]
@ -25,35 +24,29 @@
%%% @end
%%% ====================================================================
-module(node_pep).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-include("jlib.hrl").
-include("logger.hrl").
%%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin.
%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
-module(node_pep).
-author('christophe.romain@process-one.net').
-include("ejabberd.hrl").
-include("logger.hrl").
-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/4,
get_pending_nodes/2, 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, node_to_path/1,
path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts),
@ -63,423 +56,193 @@ init(Host, ServerHost, Opts) ->
terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost), ok.
-spec(options/0 :: () -> NodeOptions::mod_pubsub:nodeOptions()).
options() ->
[{deliver_payloads, true}, {notify_config, false},
{notify_delete, false}, {notify_retract, false},
{purge_offline, false}, {persist_items, true},
{max_items, 1}, {subscribe, true},
{access_model, presence}, {roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, true}].
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, false},
{purge_offline, false},
{persist_items, false},
{max_items, 1},
{subscribe, true},
{access_model, presence},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, true}].
-spec(features/0 :: () -> Features::[binary(),...]).
features() ->
[<<"create-nodes">>, %*
<<"auto-create">>, %*
<<"auto-subscribe">>, %*
<<"delete-nodes">>, %*
<<"delete-items">>, %*
<<"filtered-notifications">>, %*
<<"modify-affiliations">>, <<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>, %*
<<"purge-nodes">>, <<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>, %*
<<"retrieve-subscriptions">>, <<"subscribe">>].
-spec(create_node_permission/6 ::
(
Host :: mod_pubsub:hostPEP(),
ServerHost :: binary(),
NodeId :: mod_pubsub:nodeId(),
_ParentNodeId :: mod_pubsub:nodeId(),
Owner :: jid(),
Access :: atom())
-> {result, boolean()}
).
[<<"create-nodes">>,
<<"auto-create">>,
<<"auto-subscribe">>,
<<"delete-nodes">>,
<<"delete-items">>,
<<"filtered-notifications">>,
<<"modify-affiliations">>,
<<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>].
create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) ->
LOwner = jlib:jid_tolower(Owner),
{User, Server, _Resource} = LOwner,
Allowed = case LOwner of
{<<"">>, Host, <<"">>} ->
true; % pubsub service always allowed
{<<"">>, Host, <<"">>} ->
true; % pubsub service always allowed
_ ->
case acl:match_rule(ServerHost, Access, LOwner) of
allow ->
case Host of
{User, Server, _} -> true;
_ -> false
end;
_ ->
case acl:match_rule(ServerHost, Access, LOwner) of
allow ->
case Host of
{User, Server, _} -> true;
_ -> false
end;
E -> ?DEBUG("Create not allowed : ~p~n", [E]), false
end
end,
false
end
end,
{result, Allowed}.
-spec(create_node/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid())
-> {result, {default, broadcast}}
).
create_node(NodeIdx, Owner) ->
node_hometree:create_node(NodeIdx, Owner).
create_node(Nidx, Owner) ->
node_hometree:create_node(Nidx, Owner).
-spec(delete_node/1 ::
(
Nodes :: [mod_pubsub:pubsubNode(),...])
-> {result,
{[],
[{mod_pubsub:pubsubNode(),
[{ljid(), [{mod_pubsub:subscription(), mod_pubsub:subId()}]},...]},...]
}
}
).
delete_node(Nodes) ->
{result, {_, _, Result}} = node_hometree:delete_node(Nodes),
{result, {[], Result}}.
delete_node(Removed) ->
case node_hometree:delete_node(Removed) of
{result, {_, _, Result}} -> {result, {[], Result}};
Error -> Error
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
case node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId) of
{error, Error} -> {error, Error};
{result, _} -> {result, []}
end.
-spec(subscribe_node/8 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Sender :: jid(),
Subscriber :: ljid(),
AccessModel :: mod_pubsub:accessModel(),
SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence',
PresenceSubscription :: boolean(),
RosterGroup :: boolean(),
Options :: mod_pubsub:subOptions())
-> {result, {default, subscribed, mod_pubsub:subId()}}
| {result, {default, subscribed, mod_pubsub:subId(), send_last}}
| {result, {default, pending, mod_pubsub:subId()}}
%%%
| {error, xmlel()}
).
subscribe_node(NodeIdx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(NodeIdx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options).
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
-spec(unsubscribe_node/4 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Sender :: jid(),
Subscriber :: ljid(),
SubId :: subId())
-> {result, default}
%
| {error, xmlel()}
).
unsubscribe_node(NodeIdx, Sender, Subscriber, SubID) ->
case node_hometree:unsubscribe_node(NodeIdx, Sender, Subscriber, SubID) of
{error, Error} -> {error, Error};
{result, _} -> {result, []}
end.
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
-spec(publish_item/6 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Publisher :: jid(),
PublishModel :: mod_pubsub:publishModel(),
Max_Items :: non_neg_integer(),
ItemId :: <<>> | mod_pubsub:itemId(),
Payload :: mod_pubsub:payload())
-> {result, {default, broadcast, [mod_pubsub:itemId()]}}
%%%
| {error, xmlel()}
).
publish_item(NodeIdx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree:publish_item(NodeIdx, Publisher, Model, MaxItems, ItemId, Payload).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
-spec(remove_extra_items/3 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Max_Items :: unlimited | non_neg_integer(),
ItemIds :: [mod_pubsub:itemId()])
-> {result,
{NewItems::[mod_pubsub:itemId()],
OldItems::[mod_pubsub:itemId()]}
}
).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems, ItemIds).
purge_node(Nidx, Owner) ->
node_hometree:purge_node(Nidx, Owner).
-spec(delete_item/4 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Publisher :: jid(),
PublishModel :: mod_pubsub:publishModel(),
ItemId :: <<>> | mod_pubsub:itemId())
-> {result, {default, broadcast}}
%%%
| {error, xmlel()}
).
delete_item(NodeIdx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeIdx, Publisher, PublishModel, ItemId).
-spec(purge_node/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid())
-> {result, {default, broadcast}}
| {error, xmlel()}
).
purge_node(NodeIdx, Owner) ->
node_hometree:purge_node(NodeIdx, Owner).
-spec(get_entity_affiliations/2 ::
(
Host :: mod_pubsub:hostPEP(),
Owner :: jid())
-> {result, [{mod_pubsub:pubsubNode(), mod_pubsub:affiliation()}]}
).
get_entity_affiliations(_Host, Owner) ->
get_entity_affiliations(Host, Owner) ->
{_, D, _} = SubKey = jlib:jid_tolower(Owner),
SubKey = jlib:jid_tolower(Owner),
GenKey = jlib:jid_remove_resource(SubKey),
States = mnesia:match_object(#pubsub_state{stateid =
{GenKey, '_'},
_ = '_'}),
NodeTree = case catch
ets:lookup(gen_mod:get_module_proc(D, config), nodetree)
of
[{nodetree, N}] -> N;
_ -> nodetree_tree
end,
Reply = lists:foldl(fun (#pubsub_state{stateid = {_, N},
affiliation = A},
Acc) ->
case NodeTree:get_node(N) of
#pubsub_node{nodeid = {{_, D, _}, _}} =
Node ->
[{Node, A} | Acc];
_ -> Acc
end
end,
[], States),
States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}),
NodeTree = mod_pubsub:tree(Host),
Reply = lists:foldl(fun (#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) ->
case NodeTree:get_node(N) of
#pubsub_node{nodeid = {{_, D, _}, _}} = Node -> [{Node, A} | Acc];
_ -> Acc
end
end,
[], States),
{result, Reply}.
-spec(get_node_affiliations/1 ::
(
NodeIdx::mod_pubsub:nodeIdx())
-> {result, [{ljid(), mod_pubsub:affiliation()}]}
).
get_node_affiliations(NodeIdx) ->
node_hometree:get_node_affiliations(NodeIdx).
get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(Nidx).
-spec(get_affiliation/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid())
-> {result, mod_pubsub:affiliation()}
).
get_affiliation(NodeIdx, Owner) ->
node_hometree:get_affiliation(NodeIdx, Owner).
get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(Nidx, Owner).
-spec(set_affiliation/3 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: ljid(),
Affiliation :: mod_pubsub:affiliation())
-> ok
).
set_affiliation(NodeIdx, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeIdx, Owner, Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(Nidx, Owner, Affiliation).
-spec(get_entity_subscriptions/2 ::
(
Host :: mod_pubsub:hostPEP(),
Owner :: jid())
-> {result,
[{mod_pubsub:pubsubNode(),
mod_pubsub:subscription(),
mod_pubsub:subId(),
ljid()}]
}
).
get_entity_subscriptions(_Host, Owner) ->
get_entity_subscriptions(Host, Owner) ->
{U, D, _} = SubKey = jlib:jid_tolower(Owner),
GenKey = jlib:jid_remove_resource(SubKey),
States = case SubKey of
GenKey ->
mnesia:match_object(#pubsub_state{stateid =
{{U, D, '_'}, '_'},
_ = '_'});
_ ->
mnesia:match_object(#pubsub_state{stateid =
{GenKey, '_'},
_ = '_'})
++
mnesia:match_object(#pubsub_state{stateid =
{SubKey, '_'},
_ = '_'})
end,
NodeTree = case catch
ets:lookup(gen_mod:get_module_proc(D, config), nodetree)
of
[{nodetree, N}] -> N;
_ -> nodetree_tree
end,
Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N},
subscriptions = Ss},
Acc) ->
case NodeTree:get_node(N) of
#pubsub_node{nodeid = {{_, D, _}, _}} = Node ->
lists:foldl(fun
({subscribed, SubID}, Acc2) ->
[{Node, subscribed, SubID, J} | Acc2];
({pending, _SubID}, Acc2) ->
[{Node, pending, J} | Acc2];
(S, Acc2) ->
[{Node, S, J} | Acc2]
end, Acc, Ss);
_ -> Acc
end
end,
[], States),
GenKey ->
mnesia:match_object(#pubsub_state{stateid = {{U, D, '_'}, '_'}, _ = '_'});
_ ->
mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'})
++
mnesia:match_object(#pubsub_state{stateid = {SubKey, '_'}, _ = '_'})
end,
NodeTree = mod_pubsub:tree(Host),
Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) ->
case NodeTree:get_node(N) of
#pubsub_node{nodeid = {{_, D, _}, _}} = Node ->
lists:foldl(fun
({subscribed, SubId}, Acc2) ->
[{Node, subscribed, SubId, J} | Acc2];
({pending, _SubId}, Acc2) ->
[{Node, pending, J} | Acc2];
(S, Acc2) ->
[{Node, S, J} | Acc2]
end,
Acc, Ss);
_ ->
Acc
end
end,
[], States),
{result, Reply}.
-spec(get_node_subscriptions/1 ::
(
NodeIdx::mod_pubsub:nodeIdx())
-> {result,
[{ljid(), mod_pubsub:subscription(), mod_pubsub:subId()}] |
[{ljid(), none},...]
}
).
get_node_subscriptions(NodeIdx) ->
node_hometree:get_node_subscriptions(NodeIdx).
get_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(Nidx).
-spec(get_subscriptions/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: ljid())
-> {result, [{mod_pubsub:subscription(), mod_pubsub:subId()}]}
).
get_subscriptions(NodeIdx, Owner) ->
node_hometree:get_subscriptions(NodeIdx, Owner).
get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(Nidx, Owner).
-spec(set_subscriptions/4 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid(),
Subscription :: mod_pubsub:subscription(),
SubId :: mod_pubsub:subId())
-> ok
%%%
| {error, xmlel()}
).
set_subscriptions(NodeIdx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeIdx, Owner, Subscription, SubId).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
-spec(get_pending_nodes/2 ::
(
Host :: mod_pubsub:hostPubsub(),
Owner :: jid())
-> {result, [mod_pubsub:nodeId()]}
).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
-spec(get_states/1 ::
(
NodeIdx::mod_pubsub:nodeIdx())
-> {result, [mod_pubsub:pubsubState()]}
).
get_states(NodeIdx) -> node_hometree:get_states(NodeIdx).
get_states(Nidx) ->
node_hometree:get_states(Nidx).
-spec(get_state/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
JID :: ljid())
-> mod_pubsub:pubsubState()
).
get_state(NodeIdx, JID) ->
node_hometree:get_state(NodeIdx, JID).
get_state(Nidx, JID) ->
node_hometree:get_state(Nidx, JID).
-spec(set_state/1 ::
(
State::mod_pubsub:pubsubState())
-> ok
).
set_state(State) -> node_hometree:set_state(State).
set_state(State) ->
node_hometree:set_state(State).
-spec(get_items/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
_From :: jid())
-> {result, [mod_pubsub:pubsubItem()]}
).
get_items(NodeIdx, From) ->
node_hometree:get_items(NodeIdx, From).
get_items(Nidx, From, RSM) ->
node_hometree:get_items(Nidx, From, RSM).
-spec(get_items/6 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
JID :: jid(),
AccessModel :: mod_pubsub:accessModel(),
Presence_Subscription :: boolean(),
RosterGroup :: boolean(),
_SubId :: mod_pubsub:subId())
-> {result, [mod_pubsub:pubsubItem()]}
%%%
| {error, xmlel()}
).
get_items(NodeIdx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_items(NodeIdx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
-spec(get_item/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
ItemId :: mod_pubsub:itemId())
-> {result, mod_pubsub:pubsubItem()}
| {error, xmlel()}
).
get_item(NodeIdx, ItemId) ->
node_hometree:get_item(NodeIdx, ItemId).
get_item(Nidx, ItemId) ->
node_hometree:get_item(Nidx, ItemId).
-spec(get_item/7 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
ItemId :: mod_pubsub:itemId(),
JID :: jid(),
AccessModel :: mod_pubsub:accessModel(),
PresenceSubscription :: boolean(),
RosterGroup :: boolean(),
SubId :: mod_pubsub:subId())
-> {result, mod_pubsub:pubsubItem()}
| {error, xmlel()}
).
get_item(NodeIdx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup,
SubId) ->
node_hometree:get_item(NodeIdx, ItemId, JID, AccessModel, PresenceSubscription,
RosterGroup, SubId).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
-spec(set_item/1 ::
(
Item::mod_pubsub:pubsubItem())
-> ok
).
set_item(Item) -> node_hometree:set_item(Item).
set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node).
node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path).
path_to_node(Path) ->
node_flat:path_to_node(Path).
%%%
%%% Internal
@ -491,10 +254,10 @@ path_to_node(Path) -> node_flat:path_to_node(Path).
%% If not, show a warning message in the ejabberd log file.
complain_if_modcaps_disabled(ServerHost) ->
case gen_mod:is_loaded(ServerHost, mod_caps) of
false ->
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
"of host ~p. This plugin requires mod_caps "
"to be enabled, but it isn't.",
[ServerHost]);
true -> ok
false ->
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
"of host ~p. This plugin requires mod_caps "
"to be enabled, but it isn't.",
[ServerHost]);
true -> ok
end.

View File

@ -17,7 +17,6 @@
%%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne.
%%%
%%%
%%% @copyright 2006-2015 ProcessOne
%%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/]
@ -25,39 +24,30 @@
%%% @end
%%% ====================================================================
-module(node_pep_odbc).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-include("jlib.hrl").
-include("logger.hrl").
%%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin.
%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
-module(node_pep_odbc).
-author('christophe.romain@process-one.net').
-include("ejabberd.hrl").
-include("logger.hrl").
-include("pubsub.hrl").
-include("jlib.hrl").
-define(PUBSUB, mod_pubsub_odbc).
-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_entity_subscriptions_for_send_last/2,
get_node_subscriptions/1, get_subscriptions/2,
set_subscriptions/4, get_pending_nodes/2, get_states/1,
get_state/2, set_state/1, get_items/7, get_items/6,
get_items/3, get_items/2, get_item/7, get_item/2,
set_item/1, get_item_name/3, get_last_items/3,
node_to_path/1, path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1,
get_entity_subscriptions_for_send_last/2, get_last_items/3]).
init(Host, ServerHost, Opts) ->
node_hometree_odbc:init(Host, ServerHost, Opts),
@ -67,362 +57,183 @@ init(Host, ServerHost, Opts) ->
terminate(Host, ServerHost) ->
node_hometree_odbc:terminate(Host, ServerHost), ok.
-spec(options/0 :: () -> NodeOptions::mod_pubsub:nodeOptions()).
options() ->
[{odbc, true},
{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, false},
{purge_offline, false},
{persist_items, true},
{max_items, 1},
{subscribe, true},
{access_model, presence},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, true}].
[{odbc, true}, {rsm, true} | node_pep:options()].
-spec(features/0 :: () -> Features::[binary(),...]).
features() ->
[<<"create-nodes">>, %*
<<"auto-create">>, %*
<<"auto-subscribe">>, %*
<<"delete-nodes">>, %*
<<"delete-items">>, %*
<<"filtered-notifications">>, %*
<<"modify-affiliations">>, <<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>, %*
<<"purge-nodes">>, <<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>, %*
<<"retrieve-subscriptions">>, <<"subscribe">>].
[<<"rsm">> | node_pep:features()].
-spec(create_node_permission/6 ::
(
Host :: mod_pubsub:hostPEP(),
ServerHost :: binary(),
NodeId :: mod_pubsub:nodeId(),
_ParentNodeId :: mod_pubsub:nodeId(),
Owner :: jid(),
Access :: atom())
-> {result, boolean()}
).
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_pep:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node_permission(Host, ServerHost, _NodeId, _ParentNode, Owner, Access) ->
LOwner = jlib:jid_tolower(Owner),
{User, Server, _Resource} = LOwner,
Allowed = case LOwner of
{<<"">>, Host, <<"">>} ->
true; % pubsub service always allowed
_ ->
case acl:match_rule(ServerHost, Access, LOwner) of
allow ->
case Host of
{User, Server, _} -> true;
_ -> false
end;
E -> ?DEBUG("Create not allowed : ~p~n", [E]), false
end
end,
{result, Allowed}.
create_node(Nidx, Owner) ->
node_hometree_odbc:create_node(Nidx, Owner),
{result, {default, broadcast}}.
-spec(create_node/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid())
-> {result, []}
).
create_node(NodeIdx, Owner) ->
case node_hometree_odbc:create_node(NodeIdx, Owner) of
{result, _} -> {result, []};
Error -> Error
delete_node(Nodes) ->
{result, {_, _, Result}} = node_hometree_odbc:delete_node(Nodes),
{result, {[], Result}}.
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree_odbc:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
case node_hometree_odbc:unsubscribe_node(Nidx, Sender, Subscriber, SubId) of
{error, Error} -> {error, Error};
{result, _} -> {result, []}
end.
-spec(delete_node/1 ::
(
Removed :: [mod_pubsub:pubsubNode(),...])
-> {result,
{[],
[{mod_pubsub:pubsubNode(),
[{ljid(), [{mod_pubsub:subscription(), mod_pubsub:subId()}]}]}]
}
}
).
delete_node(Removed) ->
case node_hometree_odbc:delete_node(Removed) of
{result, {_, _, Result}} -> {result, {[], Result}};
Error -> Error
end.
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree_odbc:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
-spec(subscribe_node/8 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Sender :: jid(),
Subscriber :: ljid(),
AccessModel :: mod_pubsub:accessModel(),
SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence',
PresenceSubscription :: boolean(),
RosterGroup :: boolean(),
Options :: mod_pubsub:subOptions())
-> {result, {default, subscribed, mod_pubsub:subId()}}
| {result, {default, subscribed, mod_pubsub:subId(), send_last}}
| {result, {default, pending, mod_pubsub:subId()}}
%%%
| {error, _}
| {error, _, binary()}
).
subscribe_node(NodeId, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree_odbc:subscribe_node(NodeId, Sender,
Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup,
Options).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree_odbc:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree_odbc:delete_item(Nidx, Publisher, PublishModel, ItemId).
-spec(unsubscribe_node/4 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Sender :: jid(),
Subscriber :: jid(),
SubId :: subId())
-> {result, []}
%
| {error, _}
| {error, _, binary()}
).
unsubscribe_node(NodeIdx, Sender, Subscriber, SubID) ->
case node_hometree_odbc:unsubscribe_node(NodeIdx, Sender, Subscriber, SubID) of
{error, Error} -> {error, Error};
{result, _} -> {result, []}
end.
purge_node(Nidx, Owner) ->
node_hometree_odbc:purge_node(Nidx, Owner).
-spec(publish_item/6 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Publisher :: jid(),
PublishModel :: mod_pubsub:publishModel(),
Max_Items :: non_neg_integer(),
ItemId :: <<>> | mod_pubsub:itemId(),
Payload :: mod_pubsub:payload())
-> {result, {default, broadcast, [mod_pubsub:itemId()]}}
%%%
| {error, _}
).
publish_item(NodeIdx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree_odbc:publish_item(NodeIdx, Publisher,
Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree_odbc:remove_extra_items(NodeId, MaxItems,
ItemIds).
-spec(delete_item/4 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Publisher :: jid(),
PublishModel :: mod_pubsub:publishModel(),
ItemId :: <<>> | mod_pubsub:itemId())
-> {result, {default, broadcast}}
%%%
| {error, _}
).
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
node_hometree_odbc:delete_item(NodeId, Publisher,
PublishModel, ItemId).
-spec(purge_node/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid())
-> {result, {default, broadcast}}
| {error, _}
).
purge_node(NodeIdx, Owner) ->
node_hometree_odbc:purge_node(NodeIdx, Owner).
-spec(get_entity_affiliations/2 ::
(
Host :: mod_pubsub:hostPubsub(),
Owner :: jid())
-> {result, [{mod_pubsub:pubsubNode(), mod_pubsub:affiliation()}]}
).
get_entity_affiliations(_Host, Owner) ->
OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
node_hometree_odbc:get_entity_affiliations(OwnerKey, Owner).
-spec(get_node_affiliations/1 ::
(
NodeIdx::mod_pubsub:nodeIdx())
-> {result, [{ljid(), mod_pubsub:affiliation()}]}
).
get_node_affiliations(NodeIdx) ->
node_hometree_odbc:get_node_affiliations(NodeIdx).
get_node_affiliations(Nidx) ->
node_hometree_odbc:get_node_affiliations(Nidx).
-spec(get_affiliation/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: ljid())
-> {result, mod_pubsub:affiliation()}
).
get_affiliation(NodeIdx, Owner) ->
node_hometree_odbc:get_affiliation(NodeIdx, Owner).
get_affiliation(Nidx, Owner) ->
node_hometree_odbc:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) ->
node_hometree_odbc:set_affiliation(NodeId, Owner, Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree_odbc:set_affiliation(Nidx, Owner, Affiliation).
get_entity_subscriptions(_Host, Owner) ->
SubKey = jlib:jid_tolower(Owner),
GenKey = jlib:jid_remove_resource(SubKey),
Host = (?PUBSUB):escape(element(2, SubKey)),
Host = node_hometree_odbc:encode_host(element(2, SubKey)),
SJ = node_hometree_odbc:encode_jid(SubKey),
GJ = node_hometree_odbc:encode_jid(GenKey),
Query = case SubKey of
GenKey ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_nod"
"e n where i.nodeid = n.nodeid and jid "
"like '">>,
GJ, <<"%' and host like '%@">>, Host, <<"';">>];
_ ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_nod"
"e n where i.nodeid = n.nodeid and jid "
"in ('">>,
SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host,
<<"';">>]
end,
GenKey ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and jid "
"like '">>, GJ, <<"%' and host like '%@">>, Host, <<"';">>];
_ ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and jid "
"in ('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host, <<"';">>]
end,
Reply = case catch ejabberd_odbc:sql_query_t(Query) of
{selected,
[<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>,
<<"jid">>, <<"subscriptions">>],
RItems} ->
lists:map(fun ([H, N, T, I, J, S]) ->
O = node_hometree_odbc:decode_jid(H),
Node = nodetree_tree_odbc:raw_to_node(O,
[N,
<<"">>,
T,
I]),
{Node,
node_hometree_odbc:decode_subscriptions(S),
node_hometree_odbc:decode_jid(J)}
end,
RItems);
_ -> []
end,
{selected,
[<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>, <<"jid">>, <<"subscriptions">>],
RItems} ->
lists:map(fun ([H, N, T, I, J, S]) ->
O = node_hometree_odbc:decode_jid(H),
Node = nodetree_tree_odbc:raw_to_node(O, [N, <<"">>, T, I]),
{Node,
node_hometree_odbc:decode_subscriptions(S),
node_hometree_odbc:decode_jid(J)}
end,
RItems);
_ ->
[]
end,
{result, Reply}.
get_entity_subscriptions_for_send_last(_Host, Owner) ->
SubKey = jlib:jid_tolower(Owner),
GenKey = jlib:jid_remove_resource(SubKey),
Host = (?PUBSUB):escape(element(2, SubKey)),
Host = node_hometree_odbc:encode_host(element(2, SubKey)),
SJ = node_hometree_odbc:encode_jid(SubKey),
GJ = node_hometree_odbc:encode_jid(GenKey),
Query = case SubKey of
GenKey ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_nod"
"e n, pubsub_node_option o where i.nodeid "
"= n.nodeid and n.nodeid = o.nodeid and "
"name='send_last_published_item' and "
"val='on_sub_and_presence' and jid like "
"'">>,
GJ, <<"%' and host like '%@">>, Host, <<"';">>];
_ ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_nod"
"e n, pubsub_node_option o where i.nodeid "
"= n.nodeid and n.nodeid = o.nodeid and "
"name='send_last_published_item' and "
"val='on_sub_and_presence' and jid in "
"('">>,
SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host,
<<"';">>]
end,
GenKey ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_node n, "
"pubsub_node_option o where i.nodeid = n.nodeid "
"and n.nodeid = o.nodeid and name='send_last_published_item' and "
"val='on_sub_and_presence' and jid like '">>,
GJ, <<"%' and host like '%@">>, Host, <<"';">>];
_ ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_node n, "
"pubsub_node_option o where i.nodeid = n.nodeid "
"and n.nodeid = o.nodeid and name='send_last_published_item' and "
"val='on_sub_and_presence' and jid in ",
"('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host, <<"';">>]
end,
Reply = case catch ejabberd_odbc:sql_query_t(Query) of
{selected,
[<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>,
<<"jid">>, <<"subscriptions">>],
RItems} ->
lists:map(fun ([H, N, T, I, J, S]) ->
O = node_hometree_odbc:decode_jid(H),
Node = nodetree_tree_odbc:raw_to_node(O,
[N,
<<"">>,
T,
I]),
{Node,
node_hometree_odbc:decode_subscriptions(S),
node_hometree_odbc:decode_jid(J)}
end,
RItems);
_ -> []
end,
{selected,
[<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>, <<"jid">>, <<"subscriptions">>],
RItems} ->
lists:map(fun ([H, N, T, I, J, S]) ->
O = node_hometree_odbc:decode_jid(H),
Node = nodetree_tree_odbc:raw_to_node(O, [N, <<"">>, T, I]),
{Node,
node_hometree_odbc:decode_subscriptions(S),
node_hometree_odbc:decode_jid(J)}
end,
RItems);
_ ->
[]
end,
{result, Reply}.
get_node_subscriptions(NodeId) ->
node_hometree_odbc:get_node_subscriptions(NodeId).
get_node_subscriptions(Nidx) ->
node_hometree_odbc:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) ->
node_hometree_odbc:get_subscriptions(NodeId, Owner).
get_subscriptions(Nidx, Owner) ->
node_hometree_odbc:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) ->
node_hometree_odbc:set_subscriptions(NodeId, Owner,
Subscription, SubId).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree_odbc:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree_odbc:get_pending_nodes(Host, Owner).
get_states(NodeId) ->
node_hometree_odbc:get_states(NodeId).
get_states(Nidx) ->
node_hometree_odbc:get_states(Nidx).
get_state(NodeId, JID) ->
node_hometree_odbc:get_state(NodeId, JID).
get_state(Nidx, JID) ->
node_hometree_odbc:get_state(Nidx, JID).
set_state(State) -> node_hometree_odbc:set_state(State).
set_state(State) ->
node_hometree_odbc:set_state(State).
get_items(NodeId, From) ->
node_hometree_odbc:get_items(NodeId, From).
get_items(Nidx, From, RSM) ->
node_hometree_odbc:get_items(Nidx, From, RSM).
get_items(NodeId, From, RSM) ->
node_hometree_odbc:get_items(NodeId, From, RSM).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree_odbc:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, none).
get_last_items(Nidx, JID, Count) ->
node_hometree_odbc:get_last_items(Nidx, JID, Count).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree_odbc:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(Nidx, ItemId) ->
node_hometree_odbc:get_item(Nidx, ItemId).
get_last_items(NodeId, JID, Count) ->
node_hometree_odbc:get_last_items(NodeId, JID, Count).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree_odbc:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) ->
node_hometree_odbc:get_item(NodeId, ItemId).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree_odbc:get_item(NodeId, ItemId, JID,
AccessModel, PresenceSubscription, RosterGroup,
SubId).
set_item(Item) -> node_hometree_odbc:set_item(Item).
set_item(Item) ->
node_hometree_odbc:set_item(Item).
get_item_name(Host, Node, Id) ->
node_hometree_odbc:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat_odbc:node_to_path(Node).
node_to_path(Node) ->
node_flat_odbc:node_to_path(Node).
path_to_node(Path) -> node_flat_odbc:path_to_node(Path).
path_to_node(Path) ->
node_flat_odbc:path_to_node(Path).
%%%
%%% Internal
@ -433,16 +244,11 @@ path_to_node(Path) -> node_flat_odbc:path_to_node(Path).
%% Check that the mod_caps module is enabled in that Jabber Host
%% If not, show a warning message in the ejabberd log file.
complain_if_modcaps_disabled(ServerHost) ->
Modules = ejabberd_config:get_option({modules,
ServerHost},
fun(Ms) when is_list(Ms) -> Ms end),
ModCaps = [mod_caps_enabled
|| {mod_caps, _Opts} <- Modules],
case ModCaps of
[] ->
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
"of host ~p. This plugin requires mod_caps "
"to be enabled, but it isn't.",
[ServerHost]);
_ -> ok
case gen_mod:is_loaded(ServerHost, mod_caps) of
false ->
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
"of host ~p. This plugin requires mod_caps "
"to be enabled, but it isn't.",
[ServerHost]);
true -> ok
end.

View File

@ -17,45 +17,32 @@
%%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne.
%%%
%%%
%%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net>
%%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time}
%%% @end
%%% ====================================================================
-module(node_private).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% Note on function definition
%% included is all defined plugin function
%% it's possible not to define some function at all
%% in that case, warning will be generated at compilation
%% and function call will fail,
%% then mod_pubsub will call function from node_hometree
%% (this makes code cleaner, but execution a little bit longer)
%% 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/4,
get_pending_nodes/2, 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, node_to_path/1,
path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts).
@ -64,121 +51,126 @@ terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost).
options() ->
[{deliver_payloads, true}, {notify_config, false},
{notify_delete, false}, {notify_retract, true},
{purge_offline, false}, {persist_items, true},
{max_items, ?MAXITEMS}, {subscribe, true},
{access_model, whitelist}, {roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, false},
{presence_based_delivery, false}].
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, whitelist},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, false},
{presence_based_delivery, false}].
features() ->
[<<"create-nodes">>, <<"delete-nodes">>,
<<"delete-items">>, <<"instant-nodes">>,
<<"outcast-affiliation">>, <<"persistent-items">>,
<<"publish">>, <<"purge-nodes">>, <<"retract-items">>,
<<"retrieve-affiliations">>, <<"retrieve-items">>,
<<"retrieve-subscriptions">>, <<"subscribe">>,
<<"subscription-notifications">>].
[<<"create-nodes">>,
<<"delete-nodes">>,
<<"delete-items">>,
<<"instant-nodes">>,
<<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>,
<<"subscription-notifications">>].
create_node_permission(Host, ServerHost, Node,
ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost,
Node, ParentNode, Owner, Access).
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) ->
node_hometree:create_node(NodeId, Owner).
create_node(Nidx, Owner) ->
node_hometree:create_node(Nidx, 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).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
node_hometree:unsubscribe_node(NodeId, Sender,
Subscriber, SubID).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
Payload) ->
node_hometree:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems,
ItemIds).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher,
PublishModel, ItemId).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
purge_node(NodeId, Owner) ->
node_hometree:purge_node(NodeId, Owner).
purge_node(Nidx, Owner) ->
node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) ->
node_hometree:get_node_affiliations(NodeId).
get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) ->
node_hometree:get_affiliation(NodeId, Owner).
get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner,
Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner).
get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner,
Subscription, SubId).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId).
get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) ->
node_hometree:get_state(NodeId, JID).
get_state(Nidx, JID) ->
node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State).
set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) ->
node_hometree:get_items(NodeId, From).
get_items(Nidx, From, RSM) ->
node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(NodeId, ItemId) ->
node_hometree:get_item(NodeId, ItemId).
get_item(Nidx, ItemId) ->
node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item).
set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node).
node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path).
path_to_node(Path) ->
node_flat:path_to_node(Path).

View File

@ -17,45 +17,32 @@
%%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne.
%%%
%%%
%%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net>
%%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time}
%%% @end
%%% ====================================================================
-module(node_public).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% Note on function definition
%% included is all defined plugin function
%% it's possible not to define some function at all
%% in that case, warning will be generated at compilation
%% and function call will fail,
%% then mod_pubsub will call function from node_hometree
%% (this makes code cleaner, but execution a little bit longer)
%% 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/4,
get_pending_nodes/2, 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, node_to_path/1,
path_to_node/1]).
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/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts).
@ -64,123 +51,126 @@ terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost).
options() ->
[{deliver_payloads, true}, {notify_config, false},
{notify_delete, false}, {notify_retract, true},
{purge_offline, false}, {persist_items, true},
{max_items, ?MAXITEMS}, {subscribe, true},
{access_model, open}, {roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, open},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() ->
[<<"create-nodes">>, <<"delete-nodes">>,
<<"delete-items">>, <<"instant-nodes">>,
<<"outcast-affiliation">>, <<"persistent-items">>,
<<"publish">>, <<"purge-nodes">>, <<"retract-items">>,
<<"retrieve-affiliations">>, <<"retrieve-items">>,
<<"retrieve-subscriptions">>, <<"subscribe">>,
<<"subscription-notifications">>].
[<<"create-nodes">>,
<<"delete-nodes">>,
<<"delete-items">>,
<<"instant-nodes">>,
<<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>,
<<"subscription-notifications">>].
create_node_permission(Host, ServerHost, Node,
ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost,
Node, ParentNode, Owner, Access).
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) ->
node_hometree:create_node(NodeId, Owner).
create_node(Nidx, Owner) ->
node_hometree:create_node(Nidx, 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).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
node_hometree:unsubscribe_node(NodeId, Sender,
Subscriber, SubID).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
Payload) ->
node_hometree:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems,
ItemIds).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher,
PublishModel, ItemId).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
purge_node(NodeId, Owner) ->
node_hometree:purge_node(NodeId, Owner).
purge_node(Nidx, Owner) ->
node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) ->
node_hometree:get_node_affiliations(NodeId).
get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) ->
node_hometree:get_affiliation(NodeId, Owner).
get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner,
Affiliation).
set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner).
get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner,
Subscription, SubId).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId).
get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) ->
node_hometree:get_state(NodeId, JID).
get_state(Nidx, JID) ->
node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State).
set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) ->
node_hometree:get_items(NodeId, From).
get_items(Nidx, From, RSM) ->
node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(NodeId, ItemId) ->
node_hometree:get_item(NodeId, ItemId).
get_item(Nidx, ItemId) ->
node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item).
set_item(Item) ->
node_hometree:set_item(Item).
%% @doc <p>Return the name of the node if known: Default is to return
%% node id.</p>
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node).
node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path).
path_to_node(Path) ->
node_flat:path_to_node(Path).

View File

@ -5,230 +5,154 @@
%%% 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).
-behaviour(gen_pubsub_nodetree).
-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/6,
delete_node/2]).
-include_lib("stdlib/include/qlc.hrl").
-include("ejabberd.hrl").
-include("logger.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]).
-define(DEFAULT_NODETYPE, leaf).
-define(DEFAULT_PARENTS, []).
-define(DEFAULT_CHILDREN, []).
-compile(export_all).
%%====================================================================
%% API
%%====================================================================
init(Host, ServerHost, Opts) ->
nodetree_tree:init(Host, ServerHost, Opts).
terminate(Host, ServerHost) ->
nodetree_tree:terminate(Host, ServerHost).
-spec(create_node/6 ::
(
Key :: mod_pubsub:hostPubsub(),
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(Key, NodeID, Type, Owner, Options, Parents) ->
OwnerJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
case find_node(Key, NodeID) of
false ->
NodeIdx = pubsub_index:new(node),
N = #pubsub_node{nodeid = oid(Key, NodeID), id = NodeIdx,
type = Type, parents = Parents, owners = [OwnerJID],
options = Options},
case set_node(N) of
ok -> {ok, NodeIdx};
Other -> Other
end;
_ -> {error, ?ERR_CONFLICT}
end.
-spec(set_node/1 ::
(
PubsubNode::mod_pubsub:pubsubNode())
-> ok
%%%
| {error, xmlel()}
).
set_node(#pubsub_node{nodeid = {Key, _}, owners = Owners, options = Options} =
Node) ->
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 ->
mnesia:write(Node#pubsub_node{parents = Parents});
Other -> Other
true -> mnesia:write(Node#pubsub_node{parents = Parents});
Other -> Other
end.
-spec(delete_node/2 ::
(
Key :: mod_pubsub:hostPubsub(),
NodeID :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode(),...]
%%%
| {error, xmlel()}
).
delete_node(Key, NodeID) ->
case find_node(Key, NodeID) of
false -> {error, ?ERR_ITEM_NOT_FOUND};
Node ->
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)),
pubsub_index:free(node, Node#pubsub_node.id),
mnesia:delete_object(pubsub_node, Node, write),
[Node]
create_node(Key, Node, Type, Owner, Options, Parents) ->
OwnerJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
case find_node(Key, Node) of
false ->
Nidx = pubsub_index:new(node),
N = #pubsub_node{nodeid = oid(Key, Node), id = Nidx,
type = Type, parents = Parents, owners = [OwnerJID],
options = Options},
case set_node(N) of
ok -> {ok, Nidx};
Other -> Other
end;
_ ->
{error, ?ERR_CONFLICT}
end.
options() -> nodetree_tree:options().
get_node(Host, NodeID, _From) -> get_node(Host, NodeID).
-spec(get_node/2 ::
(
Host :: mod_pubsub:hostPubsub(),
NodeID :: mod_pubsub:nodeId())
-> mod_pubsub:pubsubNode()
%%%
| {error, xmlel}
).
get_node(Host, NodeID) ->
case find_node(Host, NodeID) of
false -> {error, ?ERR_ITEM_NOT_FOUND};
Node -> Node
delete_node(Key, Node) ->
case find_node(Key, Node) of
false ->
{error, ?ERR_ITEM_NOT_FOUND};
Record ->
lists:foreach(fun (#pubsub_node{options = Opts} = Child) ->
NewOpts = remove_config_parent(Node, 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, Node)),
pubsub_index:free(node, Record#pubsub_node.id),
mnesia:delete_object(pubsub_node, Record, write),
[Record]
end.
-spec(get_node/1 ::
(
NodeIdx::mod_pubsub:nodeIdx())
-> mod_pubsub:pubsubNode()
| {error, xmlel()}
).
get_node(NodeId) -> nodetree_tree:get_node(NodeId).
options() ->
nodetree_tree:options().
get_node(Host, Node, _From) ->
get_node(Host, Node).
get_node(Host, Node) ->
case find_node(Host, Node) of
false -> {error, ?ERR_ITEM_NOT_FOUND};
Record -> Record
end.
get_node(Node) ->
nodetree_tree:get_node(Node).
get_nodes(Key, From) ->
nodetree_tree:get_nodes(Key, From).
-spec(get_nodes/1 ::
(
Host::mod_pubsub:host())
-> [mod_pubsub:pubsubNode()]
).
get_nodes(Key) -> nodetree_tree:get_nodes(Key).
get_nodes(Key) ->
nodetree_tree:get_nodes(Key).
-spec(get_parentnodes/3 ::
(
Host :: mod_pubsub:hostPubsub(),
NodeID :: mod_pubsub:nodeId(),
_From :: _)
-> [mod_pubsub:pubsubNode()]
%%%
| {error, xmlel()}
).
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
get_parentnodes(Host, Node, _From) ->
case find_node(Host, Node) 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)
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,
get_parentnodes_tree(Host, Node, _From) ->
Pred = fun (NID, #pubsub_node{nodeid = {_, NNode}}) ->
NID == NNode
end,
Tr = fun (#pubsub_node{parents = Parents}) -> Parents
end,
traversal_helper(Pred, Tr, Host, [NodeID]).
end,
traversal_helper(Pred, Tr, Host, [Node]).
get_subnodes(Host, NodeID, _From) ->
get_subnodes(Host, NodeID).
get_subnodes(Host, Node, _From) ->
get_subnodes(Host, Node).
-spec(get_subnodes/2 ::
(
Host :: mod_pubsub:hostPubsub(),
NodeId :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode()]
).
get_subnodes(Host, <<>>) ->
get_subnodes_helper(Host, <<>>);
get_subnodes(Host, NodeID) ->
case find_node(Host, NodeID) of
false -> {error, ?ERR_ITEM_NOT_FOUND};
_ -> get_subnodes_helper(Host, NodeID)
get_subnodes(Host, Node) ->
case find_node(Host, Node) of
false -> {error, ?ERR_ITEM_NOT_FOUND};
_ -> get_subnodes_helper(Host, Node)
end.
-spec(get_subnodes_helper/2 ::
(
Host :: mod_pubsub:hostPubsub(),
NodeID :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode()]
).
get_subnodes_helper(Host, NodeID) ->
Q = qlc:q([Node
|| #pubsub_node{nodeid = {NHost, _},
parents = Parents} =
Node
<- mnesia:table(pubsub_node),
Host == NHost, lists:member(NodeID, Parents)]),
get_subnodes_helper(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, NodeID, From) ->
get_subnodes_tree(Host, Node, From) ->
Pred = fun (NID, #pubsub_node{parents = Parents}) ->
lists:member(NID, Parents)
end,
lists:member(NID, Parents)
end,
Tr = fun (#pubsub_node{nodeid = {_, N}}) -> [N] end,
traversal_helper(Pred, Tr, 1, Host, [NodeID],
[{0, [get_node(Host, NodeID, From)]}]).
traversal_helper(Pred, Tr, 1, Host, [Node],
[{0, [get_node(Host, Node, From)]}]).
%%====================================================================
%% Internal functions
@ -236,17 +160,17 @@ get_subnodes_tree(Host, NodeID, From) ->
oid(Key, Name) -> {Key, Name}.
%% Key = jlib:jid() | host()
%% NodeID = string()
%% Node = string()
-spec(find_node/2 ::
(
Key :: mod_pubsub:hostPubsub(),
NodeID :: mod_pubsub:nodeId())
(
Key :: mod_pubsub:hostPubsub(),
Node :: mod_pubsub:nodeId())
-> mod_pubsub:pubsubNode() | false
).
find_node(Key, NodeID) ->
case mnesia:read(pubsub_node, oid(Key, NodeID), read) of
[] -> false;
[Node] -> Node
).
find_node(Key, Node) ->
case mnesia:read(pubsub_node, oid(Key, Node), read) of
[] -> false;
[Node] -> Node
end.
%% Key = jlib:jid() | host()
@ -254,77 +178,67 @@ find_node(Key, NodeID) ->
%% Options = [{Key = atom(), Value = term()}]
find_opt(Key, Default, Options) ->
case lists:keysearch(Key, 1, Options) of
{value, {Key, Val}} -> Val;
_ -> Default
{value, {Key, Val}} -> Val;
_ -> Default
end.
-spec(traversal_helper/4 ::
(
Pred :: fun(),
Tr :: fun(),
Host :: mod_pubsub:hostPubsub(),
NodeId :: [mod_pubsub:pubsubNode(),...])
-> [{Depth::non_neg_integer(), Nodes::[mod_pubsub:pubsubNode(),...]}]
).
traversal_helper(Pred, Tr, Host, NodeIDs) ->
traversal_helper(Pred, Tr, 0, Host, NodeIDs, []).
(
Pred :: fun(),
Tr :: fun(),
Host :: mod_pubsub:hostPubsub(),
Nodes :: [mod_pubsub:nodeId(),...])
-> [{Depth::non_neg_integer(), Nodes::[mod_pubsub:pubsubNode(),...]}]
).
traversal_helper(Pred, Tr, Host, Nodes) ->
traversal_helper(Pred, Tr, 0, Host, Nodes, []).
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)]),
traversal_helper(Pred, Tr, Depth, Host, Nodes, Acc) ->
Q = qlc:q([N
|| #pubsub_node{nodeid = {NHost, _}} = N
<- mnesia:table(pubsub_node),
Node <- Nodes, Host == NHost, Pred(Node, N)]),
Nodes = qlc:e(Q),
IDs = lists:flatmap(Tr, Nodes),
traversal_helper(Pred, Tr, Depth + 1, Host, IDs,
[{Depth, Nodes} | Acc]).
traversal_helper(Pred, Tr, Depth + 1, Host, IDs, [{Depth, Nodes} | Acc]).
remove_config_parent(NodeID, Options) ->
remove_config_parent(NodeID, Options, []).
remove_config_parent(Node, Options) ->
remove_config_parent(Node, Options, []).
remove_config_parent(_NodeID, [], Acc) ->
remove_config_parent(_Node, [], 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]).
remove_config_parent(Node, [{collection, Parents} | T], Acc) ->
remove_config_parent(Node, T, [{collection, lists:delete(Node, Parents)} | Acc]);
remove_config_parent(Node, [H | T], Acc) ->
remove_config_parent(Node, T, [H | Acc]).
-spec(validate_parentage/3 ::
(
Key :: mod_pubsub:hostPubsub(),
Owners :: [ljid(),...],
Parent_NodeIds :: [mod_pubsub:nodeId()])
(
Key :: mod_pubsub:hostPubsub(),
Owners :: [ljid(),...],
Parent_Nodes :: [mod_pubsub:nodeId()])
-> true
%%%
| {error, xmlel()}
).
validate_parentage(_Key, _Owners, []) -> true;
| {error, xmlel()}
).
validate_parentage(_Key, _Owners, []) ->
true;
validate_parentage(Key, Owners, [[] | T]) ->
validate_parentage(Key, Owners, T);
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_FORBIDDEN};
{_, collection} -> validate_parentage(Key, Owners, T);
{_, _} -> {error, ?ERR_NOT_ALLOWED}
end
end.
%% @spec (Host) -> jid()
%% Host = host()
%% @doc <p>Generate pubsub service JID.</p>
service_jid(Host) ->
case Host of
{U, S, _} -> jlib:make_jid(U, S, <<>>);
_ -> jlib:make_jid(<<>>, Host, <<>>)
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_FORBIDDEN};
{_, collection} -> validate_parentage(Key, Owners, T);
{_, _} -> {error, ?ERR_NOT_ALLOWED}
end
end.

View File

@ -36,287 +36,163 @@
%%% improvements.</p>
-module(nodetree_tree).
-behaviour(gen_pubsub_nodetree).
-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]).
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)}]),
[{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
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.
terminate(_Host, _ServerHost) ->
ok.
options() -> [{virtual_tree, false}].
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).
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
get_node(Host, Node) ->
case catch mnesia:read({pubsub_node, {Host, Node}}) of
[Record] when is_record(Record, pubsub_node) -> Record;
_ -> {error, ?ERR_ITEM_NOT_FOUND}
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
get_node(Nidx) ->
case catch mnesia:index_read(pubsub_node, Nidx, #pubsub_node.id) of
[Record] when is_record(Record, pubsub_node) -> Record;
_ -> {error, ?ERR_ITEM_NOT_FOUND}
end.
get_nodes(Host, _From) -> get_nodes(Host).
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) -> [].
get_parentnodes(_Host, _Node, _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 -> []
get_parentnodes_tree(Host, Node, _From) ->
case catch mnesia:read({pubsub_node, {Host, Node}}) of
[Record] when is_record(Record, pubsub_node) -> [{0, [Record]}];
_ -> []
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).
get_subnodes(Host, Node, _From) ->
get_subnodes(Host, Node).
-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 == []]),
|| #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)]),
|| #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)
get_subnodes_tree(Host, Node) ->
case get_node(Host, Node) of
{error, _} ->
[];
Rec ->
BasePlugin = jlib:binary_to_atom(<<"node_",
(Rec#pubsub_node.type)/binary>>),
BasePath = BasePlugin:node_to_path(Node),
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) ->
create_node(Host, Node, 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}
case catch mnesia:read({pubsub_node, {Host, Node}}) 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 ->
Nidx = pubsub_index:new(node),
mnesia:write(#pubsub_node{nodeid = {Host, Node},
id = Nidx, parents = Parents,
type = Type, owners = [BJID],
options = Options}),
{ok, Nidx};
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),
delete_node(Host, Node) ->
Removed = get_subnodes_tree(Host, Node),
lists:foreach(fun (#pubsub_node{nodeid = {_, SubNode}, id = SubNidx}) ->
pubsub_index:free(node, SubNidx),
mnesia:delete({pubsub_node, {Host, SubNode}})
end,
Removed),
Removed.

View File

@ -36,437 +36,280 @@
%%% improvements.</p>
-module(nodetree_tree_odbc).
-behaviour(gen_pubsub_nodetree).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-include("jlib.hrl").
-define(PUBSUB, mod_pubsub_odbc).
-define(PLUGIN_PREFIX, <<"node_">>).
-define(ODBC_SUFFIX, <<"_odbc">>).
-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]).
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]).
-export([raw_to_node/2]).
%% ================
%% API definition
%% ================
init(_Host, _ServerHost, _Opts) ->
ok.
%% @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>
%% @spec () -> [Option]
%% Option = mod_pubsub:nodetreeOption()
%% @doc Returns the default pubsub node tree options.
%% @spec (Host, Node, From) -> pubsubNode() | {error, Reason}
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
init(_Host, _ServerHost, _Opts) -> ok.
terminate(_Host, _ServerHost) ->
ok.
terminate(_Host, _ServerHost) -> ok.
options() ->
[{odbc, true} | nodetree_tree:options()].
options() -> [{virtual_tree, false}, {odbc, true}].
set_node(Record) when is_record(Record, pubsub_node) ->
{Host, Node} = Record#pubsub_node.nodeid,
Parent = case Record#pubsub_node.parents of
[] -> <<>>;
[First | _] -> First
end,
Type = Record#pubsub_node.type,
H = node_hometree_odbc:encode_host(Host),
N = ejabberd_odbc:escape(Node),
P = ejabberd_odbc:escape(Parent),
Nidx = case nodeidx(Host, Node) of
{result, OldNidx} ->
catch
ejabberd_odbc:sql_query_t([<<"delete from pubsub_node_option where "
"nodeid='">>, OldNidx, <<"';">>]),
catch
ejabberd_odbc:sql_query_t([<<"update pubsub_node set host='">>,
H, <<"' node='">>, N,
<<"' parent='">>, P,
<<"' type='">>, Type,
<<"' where nodeid='">>,
OldNidx, <<"';">>]),
OldNidx;
_ ->
catch
ejabberd_odbc:sql_query_t([<<"insert into pubsub_node(host, node, "
"parent, type) values('">>,
H, <<"', '">>, N, <<"', '">>, P,
<<"', '">>, Type, <<"');">>]),
case nodeidx(Host, Node) of
{result, NewNidx} -> NewNidx;
_ -> none % this should not happen
end
end,
case Nidx of
none ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
_ ->
lists:foreach(fun ({Key, Value}) ->
SKey = iolist_to_binary(atom_to_list(Key)),
SValue = ejabberd_odbc:escape(
list_to_binary(
lists:flatten(io_lib:fwrite("~p", [Value])))),
catch
ejabberd_odbc:sql_query_t([<<"insert into pubsub_node_option(nodeid, "
"name, val) values('">>,
Nidx, <<"', '">>,
SKey, <<"', '">>, SValue, <<"');">>])
end,
Record#pubsub_node.options),
{result, Nidx}
end.
get_node(Host, Node, _From) -> get_node(Host, Node).
get_node(Host, Node, _From) ->
get_node(Host, Node).
-spec(get_node/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> mod_pubsub:pubsubNode()
| {error, _}
).
get_node(Host, Node) ->
H = (?PUBSUB):escape(Host),
N = (?PUBSUB):escape(Node),
H = node_hometree_odbc:encode_host(Host),
N = ejabberd_odbc:escape(Node),
case catch
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>,
H, <<"' and node='">>, N, <<"';">>])
of
{selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
[RItem]} ->
raw_to_node(Host, RItem);
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
_ -> {error, ?ERR_ITEM_NOT_FOUND}
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>,
H, <<"' and node='">>, N, <<"';">>])
of
{selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], [RItem]} ->
raw_to_node(Host, RItem);
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
_ ->
{error, ?ERR_ITEM_NOT_FOUND}
end.
-spec(get_node/1 ::
(
NodeIdx::mod_pubsub:nodeIdx())
-> mod_pubsub:pubsubNode()
| {error, _}
).
get_node(NodeIdx) ->
get_node(Nidx) ->
case catch
ejabberd_odbc:sql_query_t([<<"select host, node, parent, type from "
"pubsub_node where nodeid='">>,
NodeIdx, <<"';">>])
of
{selected,
[<<"host">>, <<"node">>, <<"parent">>, <<"type">>],
[[Host, Node, Parent, Type]]} ->
raw_to_node(Host, [Node, Parent, Type, NodeIdx]);
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
_ -> {error, ?ERR_ITEM_NOT_FOUND}
ejabberd_odbc:sql_query_t([<<"select host, node, parent, type from "
"pubsub_node where nodeid='">>,
Nidx, <<"';">>])
of
{selected,
[<<"host">>, <<"node">>, <<"parent">>, <<"type">>], [[Host, Node, Parent, Type]]} ->
raw_to_node(Host, [Node, Parent, Type, Nidx]);
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
_ ->
{error, ?ERR_ITEM_NOT_FOUND}
end.
%% @spec (Host, From) -> [pubsubNode()] | {error, Reason}
%% Host = mod_pubsub:host() | mod_pubsub:jid()
get_nodes(Host, _From) -> get_nodes(Host).
get_nodes(Host, _From) ->
get_nodes(Host).
-spec(get_nodes/1 ::
(
Host::mod_pubsub:host())
-> [mod_pubsub:pubsubNode()]
).
get_nodes(Host) ->
H = (?PUBSUB):escape(Host),
H = node_hometree_odbc:encode_host(Host),
case catch
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>,
H, <<"';">>])
of
{selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
RItems} ->
lists:map(fun (Item) -> raw_to_node(Host, Item) end,
RItems);
_ -> []
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>, H, <<"';">>])
of
{selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
[raw_to_node(Host, Item) || Item <- RItems];
_ ->
[]
end.
%% @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>
%% @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()
get_parentnodes(_Host, _Node, _From) ->
[].
%% @doc <p>Default node tree does not handle parents, return a list
%% containing just this node.</p>
get_parentnodes(_Host, _Node, _From) -> [].
-spec(get_parentnodes_tree/3 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId(),
From :: jid())
-> [{0, [mod_pubsub:pubsubNode(),...]}]
).
get_parentnodes_tree(Host, Node, From) ->
case get_node(Host, Node, From) of
N when is_record(N, pubsub_node) -> [{0, [N]}];
_Error -> []
{error, _} -> [];
Record -> [{0, [Record]}]
end.
get_subnodes(Host, Node, _From) ->
get_subnodes(Host, Node).
%% @spec (Host, Index) -> [pubsubNode()] | {error, Reason}
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
-spec(get_subnodes/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode()]
).
get_subnodes(Host, Node) ->
H = (?PUBSUB):escape(Host),
N = (?PUBSUB):escape(Node),
H = node_hometree_odbc:encode_host(Host),
N = ejabberd_odbc:escape(Node),
case catch
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>,
H, <<"' and parent='">>, N, <<"';">>])
of
{selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
RItems} ->
lists:map(fun (Item) -> raw_to_node(Host, Item) end,
RItems);
_ -> []
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>,
H, <<"' and parent='">>, N, <<"';">>])
of
{selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
[raw_to_node(Host, Item) || Item <- RItems];
_ ->
[]
end.
get_subnodes_tree(Host, Node, _From) ->
get_subnodes_tree(Host, Node).
%% @spec (Host, Index) -> [pubsubNode()] | {error, Reason}
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
-spec(get_subnodes_tree/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode()]
).
get_subnodes_tree(Host, Node) ->
H = (?PUBSUB):escape(Host),
N = (?PUBSUB):escape(Node),
H = node_hometree_odbc:encode_host(Host),
N = ejabberd_odbc:escape(Node),
case catch
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>,
H, <<"' and node like '">>, N, <<"%';">>])
of
{selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
RItems} ->
lists:map(fun (Item) -> raw_to_node(Host, Item) end,
RItems);
_ -> []
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>,
H, <<"' and node like '">>, N, <<"%';">>])
of
{selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
[raw_to_node(Host, Item) || Item <- RItems];
_ ->
[]
end.
%% @spec (Host, Node, Type, Owner, Options, Parents) -> ok | {error, Reason}
%% Host = mod_pubsub:host() | mod_pubsub:jid()
%% Node = mod_pubsub:pubsubNode()
%% NodeType = mod_pubsub:nodeType()
%% Owner = mod_pubsub:jid()
%% Options = list()
%% Parents = list()
-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, _}
).
create_node(Host, Node, Type, Owner, Options, Parents) ->
BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
case nodeid(Host, Node) of
{error, ?ERR_ITEM_NOT_FOUND} ->
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 nodeid(Host, Parent) of
{result, PNodeId} ->
case nodeowners(PNodeId) of
[{<<>>, Host, <<>>}] -> true;
Owners ->
lists:member(BJID, Owners)
end;
_ -> false
end;
_ -> false
end
end,
case ParentExists of
true ->
case set_node(#pubsub_node{nodeid = {Host, Node},
parents = Parents, type = Type,
options = Options})
case nodeidx(Host, Node) of
{error, ?ERR_ITEM_NOT_FOUND} ->
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 nodeidx(Host, Parent) of
{result, PNode} ->
case nodeowners(PNode) of
[{<<>>, Host, <<>>}] -> true;
Owners -> lists:member(BJID, Owners)
end;
_ ->
false
end;
_ ->
false
end
end,
case ParentExists of
true ->
case set_node(#pubsub_node{nodeid = {Host, Node},
parents = Parents, type = Type,
options = Options})
of
{result, NodeId} -> {ok, NodeId};
Other -> Other
end;
false -> {error, ?ERR_FORBIDDEN}
end;
{result, _} -> {error, ?ERR_CONFLICT};
Error -> Error
{result, Nidx} -> {ok, Nidx};
Other -> Other
end;
false ->
{error, ?ERR_FORBIDDEN}
end;
{result, _} ->
{error, ?ERR_CONFLICT};
Error ->
Error
end.
%% @spec (Host, Node) -> [mod_pubsub:node()]
%% Host = mod_pubsub:host() | mod_pubsub:jid()
%% Node = mod_pubsub:pubsubNode()
-spec(delete_node/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode()]
).
delete_node(Host, Node) ->
H = (?PUBSUB):escape(Host),
N = (?PUBSUB):escape(Node),
H = node_hometree_odbc:encode_host(Host),
N = ejabberd_odbc:escape(Node),
Removed = get_subnodes_tree(Host, Node),
catch
ejabberd_odbc:sql_query_t([<<"delete from pubsub_node where host='">>,
H, <<"' and node like '">>, N, <<"%';">>]),
catch ejabberd_odbc:sql_query_t([<<"delete from pubsub_node where host='">>,
H, <<"' and node like '">>, N, <<"%';">>]),
Removed.
%% helpers
-spec(raw_to_node/2 ::
(
Host :: mod_pubsub:host(),
_ :: {NodeId::mod_pubsub:nodeId(),
Parent::mod_pubsub:nodeId(),
Type::binary(),
NodeIdx::mod_pubsub:nodeIdx()})
-> mod_pubsub:pubsubNode()
).
raw_to_node(Host, [Node, Parent, Type, NodeIdx]) ->
raw_to_node(Host, [Node, Parent, Type, Nidx]) ->
Options = case catch
ejabberd_odbc:sql_query_t([<<"select name,val from pubsub_node_option "
"where nodeid='">>,
NodeIdx, <<"';">>])
of
{selected, [<<"name">>, <<"val">>], ROptions} ->
DbOpts = lists:map(fun ([Key, Value]) ->
RKey =
jlib:binary_to_atom(Key),
Tokens = element(2,
erl_scan:string(
binary_to_list(<<Value/binary, ".">>))),
RValue = element(2,
erl_parse:parse_term(Tokens)),
{RKey, RValue}
end,
ROptions),
Module =
jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
Type/binary,
(?ODBC_SUFFIX)/binary>>),
StdOpts = Module:options(),
lists:foldl(fun ({Key, Value}, Acc) ->
lists:keyreplace(Key, 1, Acc,
{Key, Value})
end,
StdOpts, DbOpts);
_ -> []
end,
Parents = case Parent of
<<>> -> [];
_ -> [Parent]
end,
#pubsub_node{nodeid =
{Host, Node},
parents = Parents,
id = NodeIdx, type = Type, options = Options}.
% @spec (NodeRecord) -> ok | {error, Reason}
%% Record = mod_pubsub:pubsub_node()
-spec(set_node/1 ::
(
Record::mod_pubsub:pubsubNode())
-> {result, NodeIdx::mod_pubsub:nodeIdx()}
%%%
| {error, _}
).
set_node(Record) ->
{Host, Node} = Record#pubsub_node.nodeid,
Parent = case Record#pubsub_node.parents of
[] -> <<>>;
[First | _] -> First
end,
Type = Record#pubsub_node.type,
H = (?PUBSUB):escape(Host),
N = (?PUBSUB):escape(Node),
P = (?PUBSUB):escape(Parent),
NodeIdx = case nodeid(Host, Node) of
{result, OldNodeIdx} ->
catch
ejabberd_odbc:sql_query_t([<<"delete from pubsub_node_option where "
"nodeid='">>,
OldNodeIdx, <<"';">>]),
catch
ejabberd_odbc:sql_query_t([<<"update pubsub_node set host='">>,
H, <<"' node='">>, N,
<<"' parent='">>, P,
<<"' type='">>, Type,
<<"' where nodeid='">>,
OldNodeIdx, <<"';">>]),
OldNodeIdx;
_ ->
catch
ejabberd_odbc:sql_query_t([<<"insert into pubsub_node(host, node, "
"parent, type) values('">>,
H, <<"', '">>, N, <<"', '">>, P,
<<"', '">>, Type, <<"');">>]),
case nodeid(Host, Node) of
{result, NewNodeIdx} -> NewNodeIdx;
_ -> none % this should not happen
end
end,
case NodeIdx of
none -> {error, ?ERR_INTERNAL_SERVER_ERROR};
_ ->
lists:foreach(fun ({Key, Value}) ->
SKey = iolist_to_binary(atom_to_list(Key)),
SValue =
(?PUBSUB):escape(list_to_binary(lists:flatten(io_lib:fwrite("~p",
[Value])))),
catch
ejabberd_odbc:sql_query_t([<<"insert into pubsub_node_option(nodeid, "
"name, val) values('">>,
NodeIdx, <<"', '">>,
SKey, <<"', '">>,
SValue, <<"');">>])
end,
Record#pubsub_node.options),
{result, NodeIdx}
end.
-spec(nodeid/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> {result, NodeIdx::mod_pubsub:nodeIdx()}
%%%
| {error, _}
).
nodeid(Host, NodeId) ->
H = (?PUBSUB):escape(Host),
N = (?PUBSUB):escape(NodeId),
case catch
ejabberd_odbc:sql_query_t([<<"select nodeid from pubsub_node where "
"host='">>,
H, <<"' and node='">>, N, <<"';">>])
of
{selected, [<<"nodeid">>], [[NodeIdx]]} ->
{result, NodeIdx};
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
_ -> {error, ?ERR_ITEM_NOT_FOUND}
end.
-spec(nodeowners/1 ::
(
NodeIdx::mod_pubsub:nodeIdx())
-> Node_Owners::[ljid()]
).
nodeowners(NodeIdx) ->
{result, Res} = node_hometree_odbc:get_node_affiliations(NodeIdx),
lists:foldl(fun ({LJID, owner}, Acc) -> [LJID | Acc];
(_, Acc) -> Acc
ejabberd_odbc:sql_query_t([<<"select name,val from pubsub_node_option "
"where nodeid='">>, Nidx, <<"';">>])
of
{selected, [<<"name">>, <<"val">>], ROptions} ->
DbOpts = lists:map(fun ([Key, Value]) ->
RKey = jlib:binary_to_atom(Key),
Tokens = element(2, erl_scan:string(binary_to_list(<<Value/binary, ".">>))),
RValue = element(2, erl_parse:parse_term(Tokens)),
{RKey, RValue}
end,
ROptions),
Module = jlib:binary_to_atom(<<"node_", Type/binary, "_odbc">>),
StdOpts = Module:options(),
lists:foldl(fun ({Key, Value}, Acc) ->
lists:keyreplace(Key, 1, Acc, {Key, Value})
end,
[], Res).
StdOpts, DbOpts);
_ ->
[]
end,
Parents = case Parent of
<<>> -> [];
_ -> [Parent]
end,
#pubsub_node{nodeid = {Host, Node},
parents = Parents,
id = Nidx, type = Type, options = Options}.
nodeidx(Host, Node) ->
H = node_hometree_odbc:encode_host(Host),
N = ejabberd_odbc:escape(Node),
case catch
ejabberd_odbc:sql_query_t([<<"select nodeid from pubsub_node where "
"host='">>,
H, <<"' and node='">>, N, <<"';">>])
of
{selected, [<<"nodeid">>], [[Nidx]]} ->
{result, Nidx};
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
_ ->
{error, ?ERR_ITEM_NOT_FOUND}
end.
nodeowners(Nidx) ->
{result, Res} = node_hometree_odbc:get_node_affiliations(Nidx),
[LJID || {LJID, Aff} <- Res, Aff =:= owner].

View File

@ -26,143 +26,84 @@
%%% ====================================================================
%%% @doc The module <strong>{@module}</strong> is the PubSub node tree plugin that
%%% allow virtual nodes handling.
%%% allow virtual nodes handling. This prevent storage of nodes.
%%% <p>PubSub node tree plugins are using the {@link gen_nodetree} behaviour.</p>
%%% <p>This plugin development is still a work in progress. Due to optimizations in
%%% mod_pubsub, this plugin can not work anymore without altering functioning.
%%% Please, send us comments, feedback and improvements.</p>
-module(nodetree_virtual).
-behaviour(gen_pubsub_nodetree).
-author('christophe.romain@process-one.net').
-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]).
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
%% ================
init(_Host, _ServerHost, _Opts) ->
ok.
%% @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>
%% @spec () -> [Option]
%% Option = mod_pubsub:nodetreeOption()
%% @doc <p>Returns the default pubsub node tree options.</p>
%% @spec (NodeRecord) -> ok | {error, Reason}
%% NodeRecord = mod_pubsub:pubsub_node()
%% @doc <p>No node record is stored on database. Just do nothing.</p>
%% @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 a node database. Any node is considered
%% as existing. Node record contains default values.</p>
init(_Host, _ServerHost, _Opts) -> ok.
terminate(_Host, _ServerHost) ->
ok.
terminate(_Host, _ServerHost) -> ok.
options() ->
[{virtual_tree, true}].
options() -> [{virtual_tree, true}].
set_node(_Node) ->
ok.
set_node(_NodeRecord) -> ok.
get_node(Host, Node, _From) ->
get_node(Host, Node).
get_node(Host, Node, _From) -> get_node(Host, Node).
get_node(Host, Node) ->
get_node(nodeidx(Host, Node)).
get_node(Host, Node) -> get_node({Host, Node}).
get_node(Nidx) ->
{Host, Node} = nodeid(Nidx),
Record = #pubsub_node{nodeid = Node, id = Nidx},
Module = jlib:binary_to_atom(<<"node_", (Record#pubsub_node.type)/binary>>),
Record#pubsub_node{owners = [{<<"">>, Host, <<"">>}],
options = Module:options()}.
get_node({Host, _} = NodeId) ->
Record = #pubsub_node{nodeid = NodeId, id = NodeId},
Module = jlib:binary_to_atom(<<"node_",
(Record#pubsub_node.type)/binary>>),
Options = Module:options(),
Owners = [{<<"">>, Host, <<"">>}],
Record#pubsub_node{owners = Owners, options = Options}.
get_nodes(Host, _From) ->
get_nodes(Host).
%% @spec (Host, From) -> [pubsubNode()]
%% Host = mod_pubsub:host() | mod_pubsub:jid()
%% From = mod_pubsub:jid()
%% @doc <p>Virtual node tree does not handle a node database. Any node is considered
%% as existing. Nodes list can not be determined.</p>
%% @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>
%% @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>
%% @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_nodes(Host, _From) -> get_nodes(Host).
get_nodes(_Host) ->
[].
get_nodes(_Host) -> [].
get_parentnodes(_Host, _Node, _From) ->
[].
get_parentnodes(_Host, _Node, _From) -> [].
-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 -> []
get_parentnodes_tree(Host, Node, From) ->
case get_node(Host, Node, From) of
Node when is_record(Node, pubsub_node) -> [{0, [Node]}];
_Error -> []
end.
get_subnodes(Host, Node, _From) ->
get_subnodes(Host, Node).
%% @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_subnodes(_Host, _Node) -> [].
get_subnodes(_Host, _Node) ->
[].
get_subnodes_tree(Host, Node, _From) ->
get_subnodes_tree(Host, Node).
%% @spec (Host, Node, Type, Owner, Options, Parents) -> ok
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% Type = mod_pubsub:nodeType()
%% Owner = mod_pubsub:jid()
%% Options = list()
%% @doc <p>No node record is stored on database. Any valid node
%% is considered as already created.</p>
%% <p>default allowed nodes: /home/host/user/any/node/name</p>
%% @spec (Host, Node) -> [mod_pubsub:node()]
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% @doc <p>Virtual node tree does not handle parent/child.
%% node deletion just affects the corresponding node.</p>
get_subnodes_tree(_Host, _Node) -> [].
get_subnodes_tree(_Host, _Node) ->
[].
create_node(Host, Node, _Type, _Owner, _Options,
_Parents) ->
create_node(Host, Node, _Type, _Owner, _Options, _Parents) ->
{error, {virtual, {Host, Node}}}.
delete_node(Host, Node) -> [get_node(Host, Node)].
delete_node(Host, Node) ->
[get_node(Host, Node)].
%% internal helper
nodeidx(Host, Node) -> term_to_binary({Host, Node}).
nodeid(Nidx) -> binary_to_term(Nidx).

View File

@ -26,89 +26,77 @@
-include("pubsub.hrl").
-export([add_subscription/1, read_subscription/1,
delete_subscription/1, update_subscription/1]).
delete_subscription/1, update_subscription/1]).
%% TODO: Those -spec lines produce errors in old Erlang versions.
%% They can be enabled again in ejabberd 3.0 because it uses R12B or higher.
%% -spec read_subscription(SubID :: string()) -> {ok, #pubsub_subscription{}} | notfound.
read_subscription(SubID) ->
case
ejabberd_odbc:sql_query_t([<<"select opt_name, opt_value from pubsub_subscr"
"iption_opt where subid = '">>,
ejabberd_odbc:escape(SubID), <<"'">>])
of
{selected, [<<"opt_name">>, <<"opt_value">>], []} ->
notfound;
{selected, [<<"opt_name">>, <<"opt_value">>],
Options} ->
{ok,
#pubsub_subscription{subid = SubID,
options =
lists:map(fun subscription_opt_from_odbc/1,
Options)}}
ejabberd_odbc:sql_query_t([<<"select opt_name, opt_value from pubsub_subscr"
"iption_opt where subid = '">>,
ejabberd_odbc:escape(SubID), <<"'">>])
of
{selected, [<<"opt_name">>, <<"opt_value">>], []} ->
notfound;
{selected, [<<"opt_name">>, <<"opt_value">>], Options} ->
{ok,
#pubsub_subscription{subid = SubID,
options = lists:map(fun subscription_opt_from_odbc/1, Options)}}
end.
%% -spec delete_subscription(SubID :: string()) -> ok.
delete_subscription(SubID) ->
%% -spec update_subscription(#pubsub_subscription{}) -> ok .
%% -spec add_subscription(#pubsub_subscription{}) -> ok.
%% -------------- Internal utilities -----------------------
%% -spec update_subscription(#pubsub_subscription{}) -> ok .
%% -spec add_subscription(#pubsub_subscription{}) -> ok.
%% -------------- Internal utilities -----------------------
ejabberd_odbc:sql_query_t([<<"delete from pubsub_subscription_opt "
"where subid = '">>,
ejabberd_odbc:escape(SubID), <<"'">>]),
"where subid = '">>,
ejabberd_odbc:escape(SubID), <<"'">>]),
ok.
update_subscription(#pubsub_subscription{subid =
SubId} =
Sub) ->
update_subscription(#pubsub_subscription{subid = SubId} = Sub) ->
delete_subscription(SubId), add_subscription(Sub).
add_subscription(#pubsub_subscription{subid = SubId,
options = Opts}) ->
add_subscription(#pubsub_subscription{subid = SubId, options = Opts}) ->
EscapedSubId = ejabberd_odbc:escape(SubId),
lists:foreach(fun (Opt) ->
{OdbcOptName, OdbcOptValue} =
subscription_opt_to_odbc(Opt),
ejabberd_odbc:sql_query_t([<<"insert into pubsub_subscription_opt(subid, "
"opt_name, opt_value)values ('">>,
EscapedSubId, <<"','">>,
OdbcOptName, <<"','">>,
OdbcOptValue, <<"')">>])
end,
Opts),
{OdbcOptName, OdbcOptValue} = subscription_opt_to_odbc(Opt),
ejabberd_odbc:sql_query_t([<<"insert into pubsub_subscription_opt(subid, "
"opt_name, opt_value)values ('">>,
EscapedSubId, <<"','">>,
OdbcOptName, <<"','">>,
OdbcOptValue, <<"')">>])
end,
Opts),
ok.
subscription_opt_from_odbc({<<"DELIVER">>, Value}) ->
{deliver, odbc_to_boolean(Value)};
subscription_opt_from_odbc({<<"DIGEST">>, Value}) ->
{digest, odbc_to_boolean(Value)};
subscription_opt_from_odbc({<<"DIGEST_FREQUENCY">>,
Value}) ->
subscription_opt_from_odbc({<<"DIGEST_FREQUENCY">>, Value}) ->
{digest_frequency, odbc_to_integer(Value)};
subscription_opt_from_odbc({<<"EXPIRE">>, Value}) ->
{expire, odbc_to_timestamp(Value)};
subscription_opt_from_odbc({<<"INCLUDE_BODY">>,
Value}) ->
subscription_opt_from_odbc({<<"INCLUDE_BODY">>, Value}) ->
{include_body, odbc_to_boolean(Value)};
%%TODO: might be > than 1 show_values value??.
%% need to use compact all in only 1 opt.
subscription_opt_from_odbc({<<"SHOW_VALUES">>,
Value}) ->
subscription_opt_from_odbc({<<"SHOW_VALUES">>, Value}) ->
{show_values, Value};
subscription_opt_from_odbc({<<"SUBSCRIPTION_TYPE">>,
Value}) ->
subscription_opt_from_odbc({<<"SUBSCRIPTION_TYPE">>, Value}) ->
{subscription_type,
case Value of
<<"items">> -> items;
<<"nodes">> -> nodes
end};
subscription_opt_from_odbc({<<"SUBSCRIPTION_DEPTH">>,
Value}) ->
case Value of
<<"items">> -> items;
<<"nodes">> -> nodes
end};
subscription_opt_from_odbc({<<"SUBSCRIPTION_DEPTH">>, Value}) ->
{subscription_depth,
case Value of
<<"all">> -> all;
N -> odbc_to_integer(N)
end}.
case Value of
<<"all">> -> all;
N -> odbc_to_integer(N)
end}.
subscription_opt_to_odbc({deliver, Bool}) ->
{<<"DELIVER">>, boolean_to_odbc(Bool)};
@ -124,19 +112,18 @@ subscription_opt_to_odbc({show_values, Values}) ->
{<<"SHOW_VALUES">>, Values};
subscription_opt_to_odbc({subscription_type, Type}) ->
{<<"SUBSCRIPTION_TYPE">>,
case Type of
items -> <<"items">>;
nodes -> <<"nodes">>
end};
case Type of
items -> <<"items">>;
nodes -> <<"nodes">>
end};
subscription_opt_to_odbc({subscription_depth, Depth}) ->
{<<"SUBSCRIPTION_DEPTH">>,
case Depth of
all -> <<"all">>;
N -> integer_to_odbc(N)
end}.
case Depth of
all -> <<"all">>;
N -> integer_to_odbc(N)
end}.
integer_to_odbc(N) ->
iolist_to_binary(integer_to_list(N)).
integer_to_odbc(N) -> iolist_to_binary(integer_to_list(N)).
boolean_to_odbc(true) -> <<"1">>;
boolean_to_odbc(false) -> <<"0">>.
@ -147,5 +134,4 @@ odbc_to_integer(N) -> jlib:binary_to_integer(N).
odbc_to_boolean(B) -> B == <<"1">>.
odbc_to_timestamp(T) ->
jlib:datetime_string_to_timestamp(T).
odbc_to_timestamp(T) -> jlib:datetime_string_to_timestamp(T).

View File

@ -29,7 +29,6 @@
%% new/1 and free/2 MUST be called inside a transaction bloc
-module(pubsub_index).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
@ -38,30 +37,30 @@
init(_Host, _ServerHost, _Opts) ->
mnesia:create_table(pubsub_index,
[{disc_copies, [node()]},
{attributes, record_info(fields, pubsub_index)}]).
[{disc_copies, [node()]},
{attributes, record_info(fields, pubsub_index)}]).
new(Index) ->
case mnesia:read({pubsub_index, Index}) of
[I] ->
case I#pubsub_index.free of
[] ->
Id = I#pubsub_index.last + 1,
mnesia:write(I#pubsub_index{last = Id}),
Id;
[Id | Free] ->
mnesia:write(I#pubsub_index{free = Free}), Id
end;
_ ->
mnesia:write(#pubsub_index{index = Index, last = 1,
free = []}),
1
[I] ->
case I#pubsub_index.free of
[] ->
Id = I#pubsub_index.last + 1,
mnesia:write(I#pubsub_index{last = Id}),
Id;
[Id | Free] ->
mnesia:write(I#pubsub_index{free = Free}), Id
end;
_ ->
mnesia:write(#pubsub_index{index = Index, last = 1, free = []}),
1
end.
free(Index, Id) ->
case mnesia:read({pubsub_index, Index}) of
[I] ->
Free = I#pubsub_index.free,
mnesia:write(I#pubsub_index{free = [Id | Free]});
_ -> ok
[I] ->
Free = I#pubsub_index.free,
mnesia:write(I#pubsub_index{free = [Id | Free]});
_ ->
ok
end.

422
src/pubsub_migrate.erl Normal file
View File

@ -0,0 +1,422 @@
%%%----------------------------------------------------------------------
%%% File : pubsub_migrate.erl
%%% Author : Christophe Romain <christophe.romain@process-one.net>
%%% Purpose : Migration/Upgrade code put out of mod_pubsub
%%% Created : 26 Jul 2014 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2015 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------
-module(pubsub_migrate).
-include("pubsub.hrl").
-include("logger.hrl").
-export([update_node_database/2, update_state_database/2, update_lastitem_database/2]).
update_item_database_binary() ->
F = fun () ->
case catch mnesia:read({pubsub_last_item, mnesia:first(pubsub_last_item)}) of
[First] when is_list(First#pubsub_last_item.itemid) ->
?INFO_MSG("Binarization of pubsub items table...", []),
lists:foreach(fun (Id) ->
[Node] = mnesia:read({pubsub_last_item, Id}),
ItemId = iolist_to_binary(Node#pubsub_last_item.itemid),
ok = mnesia:delete({pubsub_last_item, Id}),
ok = mnesia:write(Node#pubsub_last_item{itemid=ItemId})
end,
mnesia:all_keys(pubsub_last_item));
_-> no_need
end
end,
case mnesia:transaction(F) of
{aborted, Reason} ->
?ERROR_MSG("Failed to binarize pubsub items table: ~p", [Reason]);
{atomic, no_need} ->
ok;
{atomic, Result} ->
?INFO_MSG("Pubsub items table has been binarized: ~p", [Result])
end.
update_node_database_binary() ->
F = fun () ->
case catch mnesia:read({pubsub_node, mnesia:first(pubsub_node)}) of
[First] when is_list(First#pubsub_node.type) ->
?INFO_MSG("Binarization of pubsub nodes table...", []),
lists:foreach(fun ({H, N}) ->
[Node] = mnesia:read({pubsub_node, {H, N}}),
Type = iolist_to_binary(Node#pubsub_node.type),
BN = case N of
Binary when is_binary(Binary) ->
N;
_ ->
{result, BN1} = mod_pubsub:node_call(H, Type, path_to_node, [N]),
BN1
end,
BP = case [case P of
Binary2 when is_binary(Binary2) -> P;
_ -> element(2, mod_pubsub:node_call(H, Type, path_to_node, [P]))
end
|| P <- Node#pubsub_node.parents] of
[<<>>] -> [];
Parents -> Parents
end,
BH = case H of
{U, S, R} -> {iolist_to_binary(U), iolist_to_binary(S), iolist_to_binary(R)};
String -> iolist_to_binary(String)
end,
Owners = [{iolist_to_binary(U), iolist_to_binary(S), iolist_to_binary(R)} ||
{U, S, R} <- Node#pubsub_node.owners],
ok = mnesia:delete({pubsub_node, {H, N}}),
ok = mnesia:write(Node#pubsub_node{nodeid = {BH, BN},
parents = BP,
type = Type,
owners = Owners});
(_) -> ok
end,
mnesia:all_keys(pubsub_node));
_-> no_need
end
end,
case mnesia:transaction(F) of
{aborted, Reason} ->
?ERROR_MSG("Failed to binarize pubsub node table: ~p", [Reason]);
{atomic, no_need} ->
ok;
{atomic, Result} ->
?INFO_MSG("Pubsub nodes table has been binarized: ~p", [Result])
end.
update_node_database(Host, ServerHost) ->
mnesia:del_table_index(pubsub_node, type),
mnesia:del_table_index(pubsub_node, parentid),
case catch mnesia:table_info(pubsub_node, attributes) of
[host_node, host_parent, info] ->
?INFO_MSG("Upgrading pubsub nodes table...", []),
F = fun () ->
{Result, LastIdx} = lists:foldl(fun ({pubsub_node,
NodeId, ParentId,
{nodeinfo, Items,
Options,
Entities}},
{RecList,
NodeIdx}) ->
ItemsList =
lists:foldl(fun
({item,
IID,
Publisher,
Payload},
Acc) ->
C =
{unknown,
Publisher},
M =
{now(),
Publisher},
mnesia:write(#pubsub_item{itemid
=
{IID,
NodeIdx},
creation
=
C,
modification
=
M,
payload
=
Payload}),
[{Publisher,
IID}
| Acc]
end,
[],
Items),
Owners =
dict:fold(fun
(JID,
{entity,
Aff,
Sub},
Acc) ->
UsrItems =
lists:foldl(fun
({P,
I},
IAcc) ->
case
P
of
JID ->
[I
| IAcc];
_ ->
IAcc
end
end,
[],
ItemsList),
mnesia:write({pubsub_state,
{JID,
NodeIdx},
UsrItems,
Aff,
Sub}),
case
Aff
of
owner ->
[JID
| Acc];
_ ->
Acc
end
end,
[],
Entities),
mnesia:delete({pubsub_node,
NodeId}),
{[#pubsub_node{nodeid
=
NodeId,
id
=
NodeIdx,
parents
=
[element(2,
ParentId)],
owners
=
Owners,
options
=
Options}
| RecList],
NodeIdx + 1}
end,
{[], 1},
mnesia:match_object({pubsub_node,
{Host,
'_'},
'_',
'_'})),
mnesia:write(#pubsub_index{index = node, last = LastIdx,
free = []}),
Result
end,
{atomic, NewRecords} = mnesia:transaction(F),
{atomic, ok} = mnesia:delete_table(pubsub_node),
{atomic, ok} = mnesia:create_table(pubsub_node,
[{disc_copies, [node()]},
{attributes,
record_info(fields,
pubsub_node)}]),
FNew = fun () ->
lists:foreach(fun (Record) -> mnesia:write(Record) end,
NewRecords)
end,
case mnesia:transaction(FNew) of
{atomic, Result} ->
?INFO_MSG("Pubsub nodes table upgraded: ~p",
[Result]);
{aborted, Reason} ->
?ERROR_MSG("Problem upgrading Pubsub nodes table:~n~p",
[Reason])
end;
[nodeid, parentid, type, owners, options] ->
F = fun ({pubsub_node, NodeId, {_, Parent}, Type,
Owners, Options}) ->
#pubsub_node{nodeid = NodeId, id = 0,
parents = [Parent], type = Type,
owners = Owners, options = Options}
end,
mnesia:transform_table(pubsub_node, F,
[nodeid, id, parents, type, owners, options]),
FNew = fun () ->
LastIdx = lists:foldl(fun (#pubsub_node{nodeid =
NodeId} =
PubsubNode,
NodeIdx) ->
mnesia:write(PubsubNode#pubsub_node{id
=
NodeIdx}),
lists:foreach(fun
(#pubsub_state{stateid
=
StateId} =
State) ->
{JID,
_} =
StateId,
mnesia:delete({pubsub_state,
StateId}),
mnesia:write(State#pubsub_state{stateid
=
{JID,
NodeIdx}})
end,
mnesia:match_object(#pubsub_state{stateid
=
{'_',
NodeId},
_
=
'_'})),
lists:foreach(fun
(#pubsub_item{itemid
=
ItemId} =
Item) ->
{IID,
_} =
ItemId,
{M1,
M2} =
Item#pubsub_item.modification,
{C1,
C2} =
Item#pubsub_item.creation,
mnesia:delete({pubsub_item,
ItemId}),
mnesia:write(Item#pubsub_item{itemid
=
{IID,
NodeIdx},
modification
=
{M2,
M1},
creation
=
{C2,
C1}})
end,
mnesia:match_object(#pubsub_item{itemid
=
{'_',
NodeId},
_
=
'_'})),
NodeIdx + 1
end,
1,
mnesia:match_object({pubsub_node,
{Host, '_'},
'_', '_',
'_', '_',
'_'})
++
mnesia:match_object({pubsub_node,
{{'_',
ServerHost,
'_'},
'_'},
'_', '_',
'_', '_',
'_'})),
mnesia:write(#pubsub_index{index = node,
last = LastIdx, free = []})
end,
case mnesia:transaction(FNew) of
{atomic, Result} ->
rename_default_nodeplugin(),
?INFO_MSG("Pubsub nodes table upgraded: ~p",
[Result]);
{aborted, Reason} ->
?ERROR_MSG("Problem upgrading Pubsub nodes table:~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]),
rename_default_nodeplugin();
_ -> ok
end,
update_node_database_binary().
rename_default_nodeplugin() ->
lists:foreach(fun (Node) ->
mnesia:dirty_write(Node#pubsub_node{type =
<<"hometree">>})
end,
mnesia:dirty_match_object(#pubsub_node{type =
<<"default">>,
_ = '_'})).
update_state_database(_Host, _ServerHost) ->
case catch mnesia:table_info(pubsub_state, attributes) of
[stateid, nodeidx, items, affiliation, subscriptions] ->
?INFO_MSG("Upgrading pubsub states table...", []),
F = fun ({pubsub_state, {{U,S,R}, NodeID}, _NodeIdx, Items, Aff, Sub}, Acc) ->
JID = {iolist_to_binary(U), iolist_to_binary(S), iolist_to_binary(R)},
Subs = case Sub of
none ->
[];
[] ->
[];
_ ->
{result, SubID} = pubsub_subscription:subscribe_node(JID, NodeID, []),
[{Sub, SubID}]
end,
NewState = #pubsub_state{stateid = {JID, NodeID},
items = Items,
affiliation = Aff,
subscriptions = Subs},
[NewState | Acc]
end,
{atomic, NewRecs} = mnesia:transaction(fun mnesia:foldl/3,
[F, [], pubsub_state]),
{atomic, ok} = mnesia:delete_table(pubsub_state),
{atomic, ok} = mnesia:create_table(pubsub_state,
[{disc_copies, [node()]},
{attributes, record_info(fields, pubsub_state)}]),
FNew = fun () ->
lists:foreach(fun mnesia:write/1, NewRecs)
end,
case mnesia:transaction(FNew) of
{atomic, Result} ->
?INFO_MSG("Pubsub states table upgraded: ~p",
[Result]);
{aborted, Reason} ->
?ERROR_MSG("Problem upgrading Pubsub states table:~n~p",
[Reason])
end;
_ ->
ok
end.
update_lastitem_database(_Host, _ServerHost) ->
update_item_database_binary().

View File

@ -26,169 +26,111 @@
%% API
-export([init/0, subscribe_node/3, unsubscribe_node/3,
get_subscription/3, set_subscription/4,
get_options_xform/2, parse_options_xform/1]).
get_subscription/3, set_subscription/4,
get_options_xform/2, parse_options_xform/1]).
% Internal function also exported for use in transactional bloc from pubsub plugins
-export([add_subscription/3, delete_subscription/3,
read_subscription/3, write_subscription/4]).
read_subscription/3, write_subscription/4]).
-include("pubsub.hrl").
-include("jlib.hrl").
-define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
-define(PUBSUB_DIGEST, <<"pubsub#digest">>).
-define(PUBSUB_DIGEST_FREQUENCY,
<<"pubsub#digest_frequency">>).
-define(PUBSUB_DIGEST_FREQUENCY, <<"pubsub#digest_frequency">>).
-define(PUBSUB_EXPIRE, <<"pubsub#expire">>).
-define(PUBSUB_INCLUDE_BODY, <<"pubsub#include_body">>).
-define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>).
-define(PUBSUB_SUBSCRIPTION_TYPE,
<<"pubsub#subscription_type">>).
-define(PUBSUB_SUBSCRIPTION_DEPTH,
<<"pubsub#subscription_depth">>).
-define(DELIVER_LABEL,
<<"Whether an entity wants to receive or "
"disable notifications">>).
-define(DIGEST_LABEL,
<<"Whether an entity wants to receive digests "
"(aggregations) of notifications or all "
"notifications individually">>).
-define(DIGEST_FREQUENCY_LABEL,
<<"The minimum number of milliseconds between "
"sending any two notification digests">>).
-define(EXPIRE_LABEL,
<<"The DateTime at which a leased subscription "
"will end or has ended">>).
-define(INCLUDE_BODY_LABEL,
<<"Whether an entity wants to receive an "
"XMPP message body in addition to the "
"payload format">>).
-define(SHOW_VALUES_LABEL,
<<"The presence states for which an entity "
"wants to receive notifications">>).
-define(SUBSCRIPTION_TYPE_LABEL,
<<"Type of notification to receive">>).
-define(SUBSCRIPTION_DEPTH_LABEL,
<<"Depth from subscription for which to "
"receive notifications">>).
-define(SHOW_VALUE_AWAY_LABEL,
<<"XMPP Show Value of Away">>).
-define(SHOW_VALUE_CHAT_LABEL,
<<"XMPP Show Value of Chat">>).
-define(SHOW_VALUE_DND_LABEL,
<<"XMPP Show Value of DND (Do Not Disturb)">>).
-define(SHOW_VALUE_ONLINE_LABEL,
<<"Mere Availability in XMPP (No Show Value)">>).
-define(SHOW_VALUE_XA_LABEL,
<<"XMPP Show Value of XA (Extended Away)">>).
-define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL,
<<"Receive notification of new items only">>).
-define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL,
<<"Receive notification of new nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL,
<<"Receive notification from direct child "
"nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL,
<<"Receive notification from all descendent "
"nodes">>).
-define(PUBSUB_SUBSCRIPTION_TYPE, <<"pubsub#subscription_type">>).
-define(PUBSUB_SUBSCRIPTION_DEPTH, <<"pubsub#subscription_depth">>).
-define(DELIVER_LABEL, <<"Whether an entity wants to receive or disable notifications">>).
-define(DIGEST_LABEL, <<"Whether an entity wants to receive digests "
"(aggregations) of notifications or all notifications individually">>).
-define(DIGEST_FREQUENCY_LABEL, <<"The minimum number of milliseconds between "
"sending any two notification digests">>).
-define(EXPIRE_LABEL, <<"The DateTime at which a leased subscription will end or has ended">>).
-define(INCLUDE_BODY_LABEL, <<"Whether an entity wants to receive an "
"XMPP message body in addition to the payload format">>).
-define(SHOW_VALUES_LABEL, <<"The presence states for which an entity wants to receive notifications">>).
-define(SUBSCRIPTION_TYPE_LABEL, <<"Type of notification to receive">>).
-define(SUBSCRIPTION_DEPTH_LABEL, <<"Depth from subscription for which to receive notifications">>).
-define(SHOW_VALUE_AWAY_LABEL, <<"XMPP Show Value of Away">>).
-define(SHOW_VALUE_CHAT_LABEL, <<"XMPP Show Value of Chat">>).
-define(SHOW_VALUE_DND_LABEL, <<"XMPP Show Value of DND (Do Not Disturb)">>).
-define(SHOW_VALUE_ONLINE_LABEL, <<"Mere Availability in XMPP (No Show Value)">>).
-define(SHOW_VALUE_XA_LABEL, <<"XMPP Show Value of XA (Extended Away)">>).
-define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL, <<"Receive notification of new items only">>).
-define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL, <<"Receive notification of new nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL, <<"Receive notification from direct child nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL, <<"Receive notification from all descendent nodes">>).
%%====================================================================
%% API
%%====================================================================
init() -> ok = create_table().
subscribe_node(JID, NodeID, Options) ->
case catch mnesia:sync_dirty(fun add_subscription/3,
[JID, NodeID, Options])
of
{'EXIT', {aborted, Error}} -> Error;
{error, Error} -> {error, Error};
Result -> {result, Result}
subscribe_node(JID, NodeId, Options) ->
case catch mnesia:sync_dirty(fun add_subscription/3, [JID, NodeId, Options])
of
{'EXIT', {aborted, Error}} -> Error;
{error, Error} -> {error, Error};
Result -> {result, Result}
end.
unsubscribe_node(JID, NodeID, SubID) ->
case catch mnesia:sync_dirty(fun delete_subscription/3,
[JID, NodeID, SubID])
of
{'EXIT', {aborted, Error}} -> Error;
{error, Error} -> {error, Error};
Result -> {result, Result}
unsubscribe_node(JID, NodeId, SubID) ->
case catch mnesia:sync_dirty(fun delete_subscription/3, [JID, NodeId, SubID])
of
{'EXIT', {aborted, Error}} -> Error;
{error, Error} -> {error, Error};
Result -> {result, Result}
end.
get_subscription(JID, NodeID, SubID) ->
case catch mnesia:sync_dirty(fun read_subscription/3,
[JID, NodeID, SubID])
of
{'EXIT', {aborted, Error}} -> Error;
{error, Error} -> {error, Error};
Result -> {result, Result}
get_subscription(JID, NodeId, SubID) ->
case catch mnesia:sync_dirty(fun read_subscription/3, [JID, NodeId, SubID])
of
{'EXIT', {aborted, Error}} -> Error;
{error, Error} -> {error, Error};
Result -> {result, Result}
end.
set_subscription(JID, NodeID, SubID, Options) ->
case catch mnesia:sync_dirty(fun write_subscription/4,
[JID, NodeID, SubID, Options])
of
{'EXIT', {aborted, Error}} -> Error;
{error, Error} -> {error, Error};
Result -> {result, Result}
set_subscription(JID, NodeId, SubID, Options) ->
case catch mnesia:sync_dirty(fun write_subscription/4, [JID, NodeId, SubID, Options])
of
{'EXIT', {aborted, Error}} -> Error;
{error, Error} -> {error, Error};
Result -> {result, Result}
end.
get_options_xform(Lang, Options) ->
Keys = [deliver, show_values, subscription_type,
subscription_depth],
XFields = [get_option_xfield(Lang, Key, Options)
|| Key <- Keys],
Keys = [deliver, show_values, subscription_type, subscription_depth],
XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys],
{result,
#xmlel{name = <<"x">>,
#xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_XDATA}],
children =
[#xmlel{name = <<"field">>,
attrs =
[{<<"var">>, <<"FORM_TYPE">>},
{<<"type">>, <<"hidden">>}],
children =
[#xmlel{name = <<"value">>, attrs = [],
children =
[{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
++ XFields}}.
[#xmlel{name = <<"field">>,
attrs =
[{<<"var">>, <<"FORM_TYPE">>},
{<<"type">>, <<"hidden">>}],
children =
[#xmlel{name = <<"value">>, attrs = [],
children =
[{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
++ XFields}}.
parse_options_xform(XFields) ->
case xml:remove_cdata(XFields) of
[#xmlel{name = <<"x">>} = XEl] ->
case jlib:parse_xdata_submit(XEl) of
XData when is_list(XData) ->
Opts = set_xoption(XData, []),
{result, Opts};
Other -> Other
end;
_ -> {result, []}
[#xmlel{name = <<"x">>} = XEl] ->
case jlib:parse_xdata_submit(XEl) of
XData when is_list(XData) ->
Opts = set_xoption(XData, []),
{result, Opts};
Other -> Other
end;
_ -> {result, []}
end.
%%====================================================================
@ -196,69 +138,67 @@ parse_options_xform(XFields) ->
%%====================================================================
create_table() ->
case mnesia:create_table(pubsub_subscription,
[{disc_copies, [node()]},
{attributes,
record_info(fields, pubsub_subscription)},
{type, set}])
of
{atomic, ok} -> ok;
{aborted, {already_exists, _}} -> ok;
Other -> Other
[{disc_copies, [node()]},
{attributes,
record_info(fields, pubsub_subscription)},
{type, set}])
of
{atomic, ok} -> ok;
{aborted, {already_exists, _}} -> ok;
Other -> Other
end.
-spec(add_subscription/3 ::
(
_JID :: ljid(),
_NodeID :: mod_pubsub:nodeIdx(),
Options :: [] | mod_pubsub:subOptions())
(
_JID :: ljid(),
_NodeId :: mod_pubsub:nodeIdx(),
Options :: [] | mod_pubsub:subOptions())
-> SubId :: mod_pubsub:subId()
).
).
add_subscription(_JID, _NodeID, []) -> make_subid();
add_subscription(_JID, _NodeID, Options) ->
add_subscription(_JID, _NodeId, []) -> make_subid();
add_subscription(_JID, _NodeId, Options) ->
SubID = make_subid(),
mnesia:write(#pubsub_subscription{subid = SubID,
options = Options}),
mnesia:write(#pubsub_subscription{subid = SubID, options = Options}),
SubID.
-spec(delete_subscription/3 ::
(
_JID :: _,
_NodeID :: _,
SubId :: mod_pubsub:subId())
(
_JID :: _,
_NodeId :: _,
SubId :: mod_pubsub:subId())
-> ok
).
).
delete_subscription(_JID, _NodeID, SubID) ->
delete_subscription(_JID, _NodeId, SubID) ->
mnesia:delete({pubsub_subscription, SubID}).
-spec(read_subscription/3 ::
(
_JID :: ljid(),
_NodeID :: _,
SubID :: mod_pubsub:subId())
(
_JID :: ljid(),
_NodeId :: _,
SubID :: mod_pubsub:subId())
-> mod_pubsub:pubsubSubscription()
| {error, notfound}
).
| {error, notfound}
).
read_subscription(_JID, _NodeID, SubID) ->
read_subscription(_JID, _NodeId, SubID) ->
case mnesia:read({pubsub_subscription, SubID}) of
[Sub] -> Sub;
_ -> {error, notfound}
[Sub] -> Sub;
_ -> {error, notfound}
end.
-spec(write_subscription/4 ::
(
_JID :: ljid(),
_NodeID :: _,
SubID :: mod_pubsub:subId(),
Options :: mod_pubsub:subOptions())
(
_JID :: ljid(),
_NodeId :: _,
SubID :: mod_pubsub:subId(),
Options :: mod_pubsub:subOptions())
-> ok
).
).
write_subscription(_JID, _NodeID, SubID, Options) ->
mnesia:write(#pubsub_subscription{subid = SubID,
options = Options}).
write_subscription(_JID, _NodeId, SubID, Options) ->
mnesia:write(#pubsub_subscription{subid = SubID, options = Options}).
-spec(make_subid/0 :: () -> SubId::mod_pubsub:subId()).
make_subid() ->
@ -274,43 +214,42 @@ make_subid() ->
set_xoption([], Opts) -> Opts;
set_xoption([{Var, Value} | T], Opts) ->
NewOpts = case var_xfield(Var) of
{error, _} -> Opts;
Key ->
Val = val_xfield(Key, Value),
lists:keystore(Key, 1, Opts, {Key, Val})
end,
{error, _} -> Opts;
Key ->
Val = val_xfield(Key, Value),
lists:keystore(Key, 1, Opts, {Key, Val})
end,
set_xoption(T, NewOpts).
%% Return the options list's key for an XForm var.
%% Convert Values for option list's Key.
var_xfield(?PUBSUB_DELIVER) -> deliver;
var_xfield(?PUBSUB_DIGEST) -> digest;
var_xfield(?PUBSUB_DIGEST_FREQUENCY) ->
digest_frequency;
var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> digest_frequency;
var_xfield(?PUBSUB_EXPIRE) -> expire;
var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
var_xfield(?PUBSUB_SHOW_VALUES) -> show_values;
var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) ->
subscription_type;
var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) ->
subscription_depth;
var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> subscription_type;
var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) -> subscription_depth;
var_xfield(_) -> {error, badarg}.
val_xfield(deliver, [Val]) -> xopt_to_bool(Val);
%val_xfield(digest, [Val]) -> xopt_to_bool(Val);
%val_xfield(digest_frequency, [Val]) ->
% jlib:binary_to_integer(Val);
%val_xfield(expire, [Val]) ->
% jlib:datetime_string_to_timestamp(Val);
%val_xfield(include_body, [Val]) -> xopt_to_bool(Val);
val_xfield(digest, [Val]) -> xopt_to_bool(Val);
val_xfield(digest_frequency, [Val]) ->
case catch jlib:binary_to_integer(Val) of
N when is_integer(N) -> N;
_ -> {error, ?ERR_NOT_ACCEPTABLE}
end;
val_xfield(expire, [Val]) -> jlib:datetime_string_to_timestamp(Val);
val_xfield(include_body, [Val]) -> xopt_to_bool(Val);
val_xfield(show_values, Vals) -> Vals;
val_xfield(subscription_type, [<<"items">>]) -> items;
val_xfield(subscription_type, [<<"nodes">>]) -> nodes;
val_xfield(subscription_depth, [<<"all">>]) -> all;
val_xfield(subscription_depth, [Depth]) ->
case catch jlib:binary_to_integer(Depth) of
N when is_integer(N) -> N;
_ -> {error, ?ERR_NOT_ACCEPTABLE}
N when is_integer(N) -> N;
_ -> {error, ?ERR_NOT_ACCEPTABLE}
end.
%% Convert XForm booleans to Erlang booleans.
@ -321,31 +260,30 @@ xopt_to_bool(<<"true">>) -> true;
xopt_to_bool(_) -> {error, ?ERR_NOT_ACCEPTABLE}.
-spec(get_option_xfield/3 ::
(
Lang :: binary(),
Key :: atom(),
Options :: mod_pubsub:subOptions())
(
Lang :: binary(),
Key :: atom(),
Options :: mod_pubsub:subOptions())
-> xmlel()
).
).
%% Return a field for an XForm for Key, with data filled in, if
%% applicable, from Options.
get_option_xfield(Lang, Key, Options) ->
Var = xfield_var(Key),
Label = xfield_label(Key),
{Type, OptEls} = type_and_options(xfield_type(Key),
Lang),
{Type, OptEls} = type_and_options(xfield_type(Key), Lang),
Vals = case lists:keysearch(Key, 1, Options) of
{value, {_, Val}} ->
[tr_xfield_values(Vals)
|| Vals <- xfield_val(Key, Val)];
false -> []
end,
{value, {_, Val}} ->
[tr_xfield_values(Vals)
|| Vals <- xfield_val(Key, Val)];
false -> []
end,
#xmlel{name = <<"field">>,
attrs =
[{<<"var">>, Var}, {<<"type">>, Type},
{<<"label">>, translate:translate(Lang, Label)}],
children = OptEls ++ Vals}.
attrs =
[{<<"var">>, Var}, {<<"type">>, Type},
{<<"label">>, translate:translate(Lang, Label)}],
children = OptEls ++ Vals}.
type_and_options({Type, Options}, Lang) ->
{Type, [tr_xfield_options(O, Lang) || O <- Options]};
@ -353,42 +291,26 @@ type_and_options(Type, _Lang) -> {Type, []}.
tr_xfield_options({Value, Label}, Lang) ->
#xmlel{name = <<"option">>,
attrs =
[{<<"label">>, translate:translate(Lang, Label)}],
children =
[#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Value}]}]}.
attrs =
[{<<"label">>, translate:translate(Lang, Label)}],
children =
[#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Value}]}]}.
tr_xfield_values(Value) ->
%% Return the XForm variable name for a subscription option key.
%% Return the XForm variable type for a subscription option key.
%% Return the XForm variable name for a subscription option key.
%% Return the XForm variable type for a subscription option key.
#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Value}]}.
-spec(xfield_var/1 ::
(
Var :: 'deliver'
% | 'digest'
% | 'digest_frequency'
% | 'expire'
% | 'include_body'
| 'show_values'
| 'subscription_type'
| 'subscription_depth')
-> binary()
).
children = [{xmlcdata, Value}]}.
xfield_var(deliver) -> ?PUBSUB_DELIVER;
%xfield_var(digest) -> ?PUBSUB_DIGEST;
%xfield_var(digest_frequency) ->
% ?PUBSUB_DIGEST_FREQUENCY;
%xfield_var(digest_frequency) -> ?PUBSUB_DIGEST_FREQUENCY;
%xfield_var(expire) -> ?PUBSUB_EXPIRE;
%xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY;
xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES;
xfield_var(subscription_type) ->
?PUBSUB_SUBSCRIPTION_TYPE;
xfield_var(subscription_depth) ->
?PUBSUB_SUBSCRIPTION_DEPTH.
xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
xfield_type(deliver) -> <<"boolean">>;
%xfield_type(digest) -> <<"boolean">>;
@ -397,52 +319,31 @@ xfield_type(deliver) -> <<"boolean">>;
%xfield_type(include_body) -> <<"boolean">>;
xfield_type(show_values) ->
{<<"list-multi">>,
[{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
{<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
{<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
{<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
{<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
[{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
{<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
{<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
{<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
{<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
xfield_type(subscription_type) ->
{<<"list-single">>,
[{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
{<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
[{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
{<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
xfield_type(subscription_depth) ->
{<<"list-single">>,
[{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
{<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
[{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
{<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
%% Return the XForm variable label for a subscription option key.
xfield_label(deliver) -> ?DELIVER_LABEL;
%xfield_label(digest) -> ?DIGEST_LABEL;
%xfield_label(digest_frequency) ->
% ?DIGEST_FREQUENCY_LABEL;
%xfield_label(digest_frequency) -> ?DIGEST_FREQUENCY_LABEL;
%xfield_label(expire) -> ?EXPIRE_LABEL;
%xfield_label(include_body) -> ?INCLUDE_BODY_LABEL;
xfield_label(show_values) -> ?SHOW_VALUES_LABEL;
%% Return the XForm value for a subscription option key.
%% Convert erlang booleans to XForms.
xfield_label(subscription_type) ->
?SUBSCRIPTION_TYPE_LABEL;
xfield_label(subscription_depth) ->
?SUBSCRIPTION_DEPTH_LABEL.
-spec(xfield_val/2 ::
(
Field :: 'deliver'
% | 'digest'
% | 'digest_frequency'
% | 'expire'
% | 'include_body'
| 'show_values'
| 'subscription_type'
| 'subscription_depth',
Val :: boolean()
| binary()
| integer()
| [binary()])
% | erlang:timestamp())
-> [binary()]
).
xfield_label(subscription_type) -> ?SUBSCRIPTION_TYPE_LABEL;
xfield_label(subscription_depth) -> ?SUBSCRIPTION_DEPTH_LABEL.
xfield_val(deliver, Val) -> [bool_to_xopt(Val)];
%xfield_val(digest, Val) -> [bool_to_xopt(Val)];
@ -450,7 +351,7 @@ xfield_val(deliver, Val) -> [bool_to_xopt(Val)];
% [iolist_to_binary(integer_to_list(Val))];
%xfield_val(expire, Val) ->
% [jlib:now_to_utc_string(Val)];
%%xfield_val(include_body, Val) -> [bool_to_xopt(Val)];
%xfield_val(include_body, Val) -> [bool_to_xopt(Val)];
xfield_val(show_values, Val) -> Val;
xfield_val(subscription_type, items) -> [<<"items">>];
xfield_val(subscription_type, nodes) -> [<<"nodes">>];

View File

@ -22,98 +22,45 @@
%%% ====================================================================
-module(pubsub_subscription_odbc).
-author("pablo.polvorin@process-one.net").
%% API
-export([init/0, subscribe_node/3, unsubscribe_node/3,
get_subscription/3, set_subscription/4,
get_options_xform/2, parse_options_xform/1]).
get_subscription/3, set_subscription/4,
get_options_xform/2, parse_options_xform/1]).
-include("pubsub.hrl").
-include("jlib.hrl").
-define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
-define(PUBSUB_DIGEST, <<"pubsub#digest">>).
-define(PUBSUB_DIGEST_FREQUENCY,
<<"pubsub#digest_frequency">>).
-define(PUBSUB_DIGEST_FREQUENCY, <<"pubsub#digest_frequency">>).
-define(PUBSUB_EXPIRE, <<"pubsub#expire">>).
-define(PUBSUB_INCLUDE_BODY, <<"pubsub#include_body">>).
-define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>).
-define(PUBSUB_SUBSCRIPTION_TYPE,
<<"pubsub#subscription_type">>).
-define(PUBSUB_SUBSCRIPTION_DEPTH,
<<"pubsub#subscription_depth">>).
-define(DELIVER_LABEL,
<<"Whether an entity wants to receive or "
"disable notifications">>).
-define(DIGEST_LABEL,
<<"Whether an entity wants to receive digests "
"(aggregations) of notifications or all "
"notifications individually">>).
-define(DIGEST_FREQUENCY_LABEL,
<<"The minimum number of milliseconds between "
"sending any two notification digests">>).
-define(EXPIRE_LABEL,
<<"The DateTime at which a leased subscription "
"will end or has ended">>).
-define(INCLUDE_BODY_LABEL,
<<"Whether an entity wants to receive an "
"XMPP message body in addition to the "
"payload format">>).
-define(SHOW_VALUES_LABEL,
<<"The presence states for which an entity "
"wants to receive notifications">>).
-define(SUBSCRIPTION_TYPE_LABEL,
<<"Type of notification to receive">>).
-define(SUBSCRIPTION_DEPTH_LABEL,
<<"Depth from subscription for which to "
"receive notifications">>).
-define(SHOW_VALUE_AWAY_LABEL,
<<"XMPP Show Value of Away">>).
-define(SHOW_VALUE_CHAT_LABEL,
<<"XMPP Show Value of Chat">>).
-define(SHOW_VALUE_DND_LABEL,
<<"XMPP Show Value of DND (Do Not Disturb)">>).
-define(SHOW_VALUE_ONLINE_LABEL,
<<"Mere Availability in XMPP (No Show Value)">>).
-define(SHOW_VALUE_XA_LABEL,
<<"XMPP Show Value of XA (Extended Away)">>).
-define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL,
<<"Receive notification of new items only">>).
-define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL,
<<"Receive notification of new nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL,
<<"Receive notification from direct child "
"nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL,
<<"Receive notification from all descendent "
"nodes">>).
-define(PUBSUB_SUBSCRIPTION_TYPE, <<"pubsub#subscription_type">>).
-define(PUBSUB_SUBSCRIPTION_DEPTH, <<"pubsub#subscription_depth">>).
-define(DELIVER_LABEL, <<"Whether an entity wants to receive or disable notifications">>).
-define(DIGEST_LABEL, <<"Whether an entity wants to receive digests "
"(aggregations) of notifications or all notifications individually">>).
-define(DIGEST_FREQUENCY_LABEL, <<"The minimum number of milliseconds between "
"sending any two notification digests">>).
-define(EXPIRE_LABEL, <<"The DateTime at which a leased subscription will end or has ended">>).
-define(INCLUDE_BODY_LABEL, <<"Whether an entity wants to receive an "
"XMPP message body in addition to the payload format">>).
-define(SHOW_VALUES_LABEL, <<"The presence states for which an entity wants to receive notifications">>).
-define(SUBSCRIPTION_TYPE_LABEL, <<"Type of notification to receive">>).
-define(SUBSCRIPTION_DEPTH_LABEL, <<"Depth from subscription for which to receive notifications">>).
-define(SHOW_VALUE_AWAY_LABEL, <<"XMPP Show Value of Away">>).
-define(SHOW_VALUE_CHAT_LABEL, <<"XMPP Show Value of Chat">>).
-define(SHOW_VALUE_DND_LABEL, <<"XMPP Show Value of DND (Do Not Disturb)">>).
-define(SHOW_VALUE_ONLINE_LABEL, <<"Mere Availability in XMPP (No Show Value)">>).
-define(SHOW_VALUE_XA_LABEL, <<"XMPP Show Value of XA (Extended Away)">>).
-define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL, <<"Receive notification of new items only">>).
-define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL, <<"Receive notification of new nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL, <<"Receive notification from direct child nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL, <<"Receive notification from all descendent nodes">>).
-define(DB_MOD, pubsub_db_odbc).
%%====================================================================
@ -123,99 +70,92 @@
init() -> ok = create_table().
-spec(subscribe_node/3 ::
(
_JID :: _,
_NodeID :: _,
Options :: mod_pubsub:subOptions())
(
_JID :: _,
_NodeId :: _,
Options :: [] | mod_pubsub:subOptions())
-> {result, mod_pubsub:subId()}
).
subscribe_node(_JID, _NodeID, Options) ->
).
subscribe_node(_JID, _NodeId, Options) ->
SubID = make_subid(),
(?DB_MOD):add_subscription(#pubsub_subscription{subid =
SubID,
options = Options}),
(?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID, options = Options}),
{result, SubID}.
-spec(unsubscribe_node/3 ::
(
_JID :: _,
_NodeID :: _,
SubID :: mod_pubsub:subId())
(
_JID :: _,
_NodeId :: _,
SubID :: mod_pubsub:subId())
-> {result, mod_pubsub:subscription()}
| {error, notfound}
).
unsubscribe_node(_JID, _NodeID, SubID) ->
| {error, notfound}
).
unsubscribe_node(_JID, _NodeId, SubID) ->
case (?DB_MOD):read_subscription(SubID) of
{ok, Sub} ->
(?DB_MOD):delete_subscription(SubID), {result, Sub};
notfound -> {error, notfound}
{ok, Sub} -> (?DB_MOD):delete_subscription(SubID), {result, Sub};
notfound -> {error, notfound}
end.
-spec(get_subscription/3 ::
(
_JID :: _,
_NodeID :: _,
SubId :: mod_pubsub:subId())
(
_JID :: _,
_NodeId :: _,
SubId :: mod_pubsub:subId())
-> {result, mod_pubsub:subscription()}
| {error, notfound}
).
get_subscription(_JID, _NodeID, SubID) ->
| {error, notfound}
).
get_subscription(_JID, _NodeId, SubID) ->
case (?DB_MOD):read_subscription(SubID) of
{ok, Sub} -> {result, Sub};
notfound -> {error, notfound}
{ok, Sub} -> {result, Sub};
notfound -> {error, notfound}
end.
-spec(set_subscription/4 ::
(
_JID :: _,
_NodeID :: _,
SubId :: mod_pubsub:subId(),
Options :: mod_pubsub:subOptions())
(
_JID :: _,
_NodeId :: _,
SubId :: mod_pubsub:subId(),
Options :: mod_pubsub:subOptions())
-> {result, ok}
).
set_subscription(_JID, _NodeID, SubID, Options) ->
).
set_subscription(_JID, _NodeId, SubID, Options) ->
case (?DB_MOD):read_subscription(SubID) of
{ok, _} ->
(?DB_MOD):update_subscription(#pubsub_subscription{subid
= SubID,
options =
Options}),
{result, ok};
notfound ->
(?DB_MOD):add_subscription(#pubsub_subscription{subid =
SubID,
options = Options}),
{result, ok}
{ok, _} ->
(?DB_MOD):update_subscription(#pubsub_subscription{subid = SubID,
options = Options}),
{result, ok};
notfound ->
(?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID,
options = Options}),
{result, ok}
end.
get_options_xform(Lang, Options) ->
Keys = [deliver, show_values, subscription_type, subscription_depth],
XFields = [get_option_xfield(Lang, Key, Options)
|| Key <- Keys],
XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys],
{result,
#xmlel{name = <<"x">>,
#xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_XDATA}],
children =
[#xmlel{name = <<"field">>,
attrs =
[{<<"var">>, <<"FORM_TYPE">>},
{<<"type">>, <<"hidden">>}],
children =
[#xmlel{name = <<"value">>, attrs = [],
children =
[{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
++ XFields}}.
[#xmlel{name = <<"field">>,
attrs =
[{<<"var">>, <<"FORM_TYPE">>},
{<<"type">>, <<"hidden">>}],
children =
[#xmlel{name = <<"value">>, attrs = [],
children =
[{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
++ XFields}}.
parse_options_xform(XFields) ->
case xml:remove_cdata(XFields) of
[#xmlel{name = <<"x">>} = XEl] ->
case jlib:parse_xdata_submit(XEl) of
XData when is_list(XData) ->
Opts = set_xoption(XData, []),
{result, Opts};
Other -> Other
end;
_ -> {result, []}
[#xmlel{name = <<"x">>} = XEl] ->
case jlib:parse_xdata_submit(XEl) of
XData when is_list(XData) ->
Opts = set_xoption(XData, []),
{result, Opts};
Other -> Other
end;
_ -> {result, []}
end.
%%====================================================================
@ -237,43 +177,42 @@ make_subid() ->
set_xoption([], Opts) -> Opts;
set_xoption([{Var, Value} | T], Opts) ->
NewOpts = case var_xfield(Var) of
{error, _} -> Opts;
Key ->
Val = val_xfield(Key, Value),
lists:keystore(Key, 1, Opts, {Key, Val})
end,
{error, _} -> Opts;
Key ->
Val = val_xfield(Key, Value),
lists:keystore(Key, 1, Opts, {Key, Val})
end,
set_xoption(T, NewOpts).
%% Return the options list's key for an XForm var.
%% Convert Values for option list's Key.
var_xfield(?PUBSUB_DELIVER) -> deliver;
%var_xfield(?PUBSUB_DIGEST) -> digest;
%var_xfield(?PUBSUB_DIGEST_FREQUENCY) ->
% digest_frequency;
%var_xfield(?PUBSUB_EXPIRE) -> expire;
%var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
var_xfield(?PUBSUB_DIGEST) -> digest;
var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> digest_frequency;
var_xfield(?PUBSUB_EXPIRE) -> expire;
var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
var_xfield(?PUBSUB_SHOW_VALUES) -> show_values;
var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) ->
subscription_type;
var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) ->
subscription_depth;
var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> subscription_type;
var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) -> subscription_depth;
var_xfield(_) -> {error, badarg}.
val_xfield(deliver, [Val]) -> xopt_to_bool(Val);
%val_xfield(digest, [Val]) -> xopt_to_bool(Val);
%val_xfield(digest_frequency, [Val]) ->
% jlib:binary_to_integer(Val);
%val_xfield(expire, [Val]) ->
% jlib:datetime_string_to_timestamp(Val);
%val_xfield(include_body, [Val]) -> xopt_to_bool(Val);
val_xfield(digest, [Val]) -> xopt_to_bool(Val);
val_xfield(digest_frequency, [Val]) ->
case catch jlib:binary_to_integer(Val) of
N when is_integer(N) -> N;
_ -> {error, ?ERR_NOT_ACCEPTABLE}
end;
val_xfield(expire, [Val]) -> jlib:datetime_string_to_timestamp(Val);
val_xfield(include_body, [Val]) -> xopt_to_bool(Val);
val_xfield(show_values, Vals) -> Vals;
val_xfield(subscription_type, [<<"items">>]) -> items;
val_xfield(subscription_type, [<<"nodes">>]) -> nodes;
val_xfield(subscription_depth, [<<"all">>]) -> all;
val_xfield(subscription_depth, [Depth]) ->
case catch jlib:binary_to_integer(Depth) of
N when is_integer(N) -> N;
_ -> {error, ?ERR_NOT_ACCEPTABLE}
N when is_integer(N) -> N;
_ -> {error, ?ERR_NOT_ACCEPTABLE}
end.
%% Convert XForm booleans to Erlang booleans.
@ -288,19 +227,18 @@ xopt_to_bool(_) -> {error, ?ERR_NOT_ACCEPTABLE}.
get_option_xfield(Lang, Key, Options) ->
Var = xfield_var(Key),
Label = xfield_label(Key),
{Type, OptEls} = type_and_options(xfield_type(Key),
Lang),
{Type, OptEls} = type_and_options(xfield_type(Key), Lang),
Vals = case lists:keysearch(Key, 1, Options) of
{value, {_, Val}} ->
[tr_xfield_values(Vals)
|| Vals <- xfield_val(Key, Val)];
false -> []
end,
{value, {_, Val}} ->
[tr_xfield_values(Vals)
|| Vals <- xfield_val(Key, Val)];
false -> []
end,
#xmlel{name = <<"field">>,
attrs =
[{<<"var">>, Var}, {<<"type">>, Type},
{<<"label">>, translate:translate(Lang, Label)}],
children = OptEls ++ Vals}.
attrs =
[{<<"var">>, Var}, {<<"type">>, Type},
{<<"label">>, translate:translate(Lang, Label)}],
children = OptEls ++ Vals}.
type_and_options({Type, Options}, Lang) ->
{Type, [tr_xfield_options(O, Lang) || O <- Options]};
@ -308,29 +246,26 @@ type_and_options(Type, _Lang) -> {Type, []}.
tr_xfield_options({Value, Label}, Lang) ->
#xmlel{name = <<"option">>,
attrs =
[{<<"label">>, translate:translate(Lang, Label)}],
children =
[#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Value}]}]}.
attrs =
[{<<"label">>, translate:translate(Lang, Label)}],
children =
[#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Value}]}]}.
tr_xfield_values(Value) ->
%% Return the XForm variable name for a subscription option key.
%% Return the XForm variable type for a subscription option key.
%% Return the XForm variable name for a subscription option key.
%% Return the XForm variable type for a subscription option key.
#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Value}]}.
children = [{xmlcdata, Value}]}.
xfield_var(deliver) -> ?PUBSUB_DELIVER;
%xfield_var(digest) -> ?PUBSUB_DIGEST;
%xfield_var(digest_frequency) ->
% ?PUBSUB_DIGEST_FREQUENCY;
%xfield_var(digest_frequency) -> ?PUBSUB_DIGEST_FREQUENCY;
%xfield_var(expire) -> ?PUBSUB_EXPIRE;
%xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY;
xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES;
xfield_var(subscription_type) ->
?PUBSUB_SUBSCRIPTION_TYPE;
xfield_var(subscription_depth) ->
?PUBSUB_SUBSCRIPTION_DEPTH.
xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
xfield_type(deliver) -> <<"boolean">>;
%xfield_type(digest) -> <<"boolean">>;
@ -339,34 +274,31 @@ xfield_type(deliver) -> <<"boolean">>;
%xfield_type(include_body) -> <<"boolean">>;
xfield_type(show_values) ->
{<<"list-multi">>,
[{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
{<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
{<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
{<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
{<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
[{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
{<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
{<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
{<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
{<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
xfield_type(subscription_type) ->
{<<"list-single">>,
[{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
{<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
[{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
{<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
xfield_type(subscription_depth) ->
{<<"list-single">>,
[{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
{<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
[{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
{<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
%% Return the XForm variable label for a subscription option key.
xfield_label(deliver) -> ?DELIVER_LABEL;
%xfield_label(digest) -> ?DIGEST_LABEL;
%xfield_label(digest_frequency) ->
% ?DIGEST_FREQUENCY_LABEL;
%xfield_label(digest_frequency) -> ?DIGEST_FREQUENCY_LABEL;
%xfield_label(expire) -> ?EXPIRE_LABEL;
%xfield_label(include_body) -> ?INCLUDE_BODY_LABEL;
xfield_label(show_values) -> ?SHOW_VALUES_LABEL;
%% Return the XForm value for a subscription option key.
%% Convert erlang booleans to XForms.
xfield_label(subscription_type) ->
?SUBSCRIPTION_TYPE_LABEL;
xfield_label(subscription_depth) ->
?SUBSCRIPTION_DEPTH_LABEL.
xfield_label(subscription_type) -> ?SUBSCRIPTION_TYPE_LABEL;
xfield_label(subscription_depth) -> ?SUBSCRIPTION_DEPTH_LABEL.
xfield_val(deliver, Val) -> [bool_to_xopt(Val)];
%xfield_val(digest, Val) -> [bool_to_xopt(Val)];

View File

@ -813,7 +813,7 @@ pubsub(Config) ->
node = Node,
jid = my_jid(Config)}}]}),
?recv2(
#message{sub_els = [#pubsub_event{}, #delay{}, #legacy_delay{}]},
#message{sub_els = [#pubsub_event{}, #delay{}]},
#iq{type = result, id = I1}),
%% Get subscriptions
true = lists:member(?PUBSUB("retrieve-subscriptions"), Features),
@ -860,8 +860,7 @@ pubsub(Config) ->
#message{sub_els = [#pubsub_event{
items = [#pubsub_event_items{
node = Node,
retract = [ItemID]}]},
#shim{headers = [{<<"Collection">>, Node}]}]}),
retract = [ItemID]}]}]}),
%% Unsubscribe from node "presence"
#iq{type = result, sub_els = []} =
send_recv(Config,

View File

@ -94,7 +94,8 @@
{mod_offline, [{db_type, odbc}]},
{mod_privacy, [{db_type, odbc}]},
{mod_private, [{db_type, odbc}]},
{mod_pubsub_odbc, [{access_createnode, pubsub_createnode},
{mod_pubsub, [{db_type, odbc},
{access_createnode, pubsub_createnode},
{ignore_pep_from_offline, true},
{last_item_cache, false},
{plugins, ["flat", "hometree", "pep"]}]},
@ -114,7 +115,8 @@
{mod_offline, [{db_type, odbc}]},
{mod_privacy, [{db_type, odbc}]},
{mod_private, [{db_type, odbc}]},
{mod_pubsub_odbc, [{access_createnode, pubsub_createnode},
{mod_pubsub, [{db_type, odbc},
{access_createnode, pubsub_createnode},
{ignore_pep_from_offline, true},
{last_item_cache, false},
{plugins, ["flat", "hometree", "pep"]}]},
@ -133,7 +135,8 @@
{mod_offline, [{db_type, odbc}]},
{mod_privacy, [{db_type, odbc}]},
{mod_private, [{db_type, odbc}]},
{mod_pubsub_odbc, [{access_createnode, pubsub_createnode},
{mod_pubsub, [{db_type, odbc},
{access_createnode, pubsub_createnode},
{ignore_pep_from_offline, true},
{last_item_cache, false},
{plugins, ["flat", "hometree", "pep"]}]},

View File

@ -26,7 +26,8 @@ host_config:
db_type: odbc
mod_private:
db_type: odbc
mod_pubsub_odbc:
mod_pubsub:
db_type: odbc
access_createnode: pubsub_createnode
ignore_pep_from_offline: true
last_item_cache: false
@ -76,7 +77,8 @@ Welcome to this XMPP server."
db_type: odbc
mod_private:
db_type: odbc
mod_pubsub_odbc:
mod_pubsub:
db_type: odbc
access_createnode: pubsub_createnode
ignore_pep_from_offline: true
last_item_cache: false
@ -132,7 +134,8 @@ Welcome to this XMPP server."
db_type: odbc
mod_private:
db_type: odbc
mod_pubsub_odbc:
mod_pubsub:
db_type: odbc
access_createnode: pubsub_createnode
ignore_pep_from_offline: true
last_item_cache: false