mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
Improve pubsub code
This commit is contained in:
parent
45eb49125b
commit
5ec972b00f
@ -23,27 +23,27 @@
|
|||||||
'presence-subscription-required' | 'subid-required' |
|
'presence-subscription-required' | 'subid-required' |
|
||||||
'too-many-subscriptions' | 'unsupported' |
|
'too-many-subscriptions' | 'unsupported' |
|
||||||
'unsupported-access-model'.
|
'unsupported-access-model'.
|
||||||
-type ps_error_feature() :: 'access-authorize' | 'access-open' |
|
-type ps_feature() :: 'access-authorize' | 'access-open' |
|
||||||
'access-presence' | 'access-roster' |
|
'access-presence' | 'access-roster' |
|
||||||
'access-whitelist' | 'auto-create' |
|
'access-whitelist' | 'auto-create' |
|
||||||
'auto-subscribe' | 'collections' | 'config-node' |
|
'auto-subscribe' | 'collections' | 'config-node' |
|
||||||
'create-and-configure' | 'create-nodes' |
|
'create-and-configure' | 'create-nodes' |
|
||||||
'delete-items' | 'delete-nodes' |
|
'delete-items' | 'delete-nodes' |
|
||||||
'filtered-notifications' | 'get-pending' |
|
'filtered-notifications' | 'get-pending' |
|
||||||
'instant-nodes' | 'item-ids' | 'last-published' |
|
'instant-nodes' | 'item-ids' | 'last-published' |
|
||||||
'leased-subscription' | 'manage-subscriptions' |
|
'leased-subscription' | 'manage-subscriptions' |
|
||||||
'member-affiliation' | 'meta-data' |
|
'member-affiliation' | 'meta-data' |
|
||||||
'modify-affiliations' | 'multi-collection' |
|
'modify-affiliations' | 'multi-collection' |
|
||||||
'multi-subscribe' | 'outcast-affiliation' |
|
'multi-subscribe' | 'outcast-affiliation' |
|
||||||
'persistent-items' | 'presence-notifications' |
|
'persistent-items' | 'presence-notifications' |
|
||||||
'presence-subscribe' | 'publish' |
|
'presence-subscribe' | 'publish' |
|
||||||
'publish-options' | 'publish-only-affiliation' |
|
'publish-options' | 'publish-only-affiliation' |
|
||||||
'publisher-affiliation' | 'purge-nodes' |
|
'publisher-affiliation' | 'purge-nodes' |
|
||||||
'retract-items' | 'retrieve-affiliations' |
|
'retract-items' | 'retrieve-affiliations' |
|
||||||
'retrieve-default' | 'retrieve-items' |
|
'retrieve-default' | 'retrieve-items' |
|
||||||
'retrieve-subscriptions' | 'subscribe' |
|
'retrieve-subscriptions' | 'subscribe' |
|
||||||
'subscription-options' | 'subscription-notifications'.
|
'subscription-options' | 'subscription-notifications'.
|
||||||
-record(ps_error, {type :: ps_error_type(), feature :: ps_error_feature()}).
|
-record(ps_error, {type :: ps_error_type(), feature :: ps_feature()}).
|
||||||
-type ps_error() :: #ps_error{}.
|
-type ps_error() :: #ps_error{}.
|
||||||
|
|
||||||
-record(chatstate, {type :: active | composing | gone | inactive | paused}).
|
-record(chatstate, {type :: active | composing | gone | inactive | paused}).
|
||||||
|
149
src/adhoc.erl
149
src/adhoc.erl
@ -1,149 +0,0 @@
|
|||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%% File : adhoc.erl
|
|
||||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
|
||||||
%%% Purpose : Provide helper functions for ad-hoc commands (XEP-0050)
|
|
||||||
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
|
||||||
%%%
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2016 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.,
|
|
||||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(adhoc).
|
|
||||||
|
|
||||||
-author('henoch@dtek.chalmers.se').
|
|
||||||
|
|
||||||
-export([
|
|
||||||
parse_request/1,
|
|
||||||
produce_response/2,
|
|
||||||
produce_response/1
|
|
||||||
]).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
|
||||||
-include("logger.hrl").
|
|
||||||
-include("jlib.hrl").
|
|
||||||
-include("adhoc.hrl").
|
|
||||||
|
|
||||||
%% Parse an ad-hoc request. Return either an adhoc_request record or
|
|
||||||
%% an {error, ErrorType} tuple.
|
|
||||||
%%
|
|
||||||
-spec parse_request(IQ :: iq_request()) -> adhoc_response() | {error, _}.
|
|
||||||
|
|
||||||
parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) ->
|
|
||||||
?DEBUG("entering parse_request...", []),
|
|
||||||
Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
|
|
||||||
SessionID = fxml:get_tag_attr_s(<<"sessionid">>, SubEl),
|
|
||||||
Action = fxml:get_tag_attr_s(<<"action">>, SubEl),
|
|
||||||
XData = find_xdata_el(SubEl),
|
|
||||||
#xmlel{children = AllEls} = SubEl,
|
|
||||||
Others = case XData of
|
|
||||||
false -> AllEls;
|
|
||||||
_ -> lists:delete(XData, AllEls)
|
|
||||||
end,
|
|
||||||
#adhoc_request{
|
|
||||||
lang = Lang,
|
|
||||||
node = Node,
|
|
||||||
sessionid = SessionID,
|
|
||||||
action = Action,
|
|
||||||
xdata = XData,
|
|
||||||
others = Others
|
|
||||||
};
|
|
||||||
parse_request(#iq{lang = Lang}) ->
|
|
||||||
Text = <<"Failed to parse ad-hoc command request">>,
|
|
||||||
{error, ?ERRT_BAD_REQUEST(Lang, Text)}.
|
|
||||||
|
|
||||||
%% Borrowed from mod_vcard.erl
|
|
||||||
find_xdata_el(#xmlel{children = SubEls}) ->
|
|
||||||
find_xdata_el1(SubEls).
|
|
||||||
|
|
||||||
find_xdata_el1([]) -> false;
|
|
||||||
find_xdata_el1([El | Els]) when is_record(El, xmlel) ->
|
|
||||||
case fxml:get_tag_attr_s(<<"xmlns">>, El) of
|
|
||||||
?NS_XDATA -> El;
|
|
||||||
_ -> find_xdata_el1(Els)
|
|
||||||
end;
|
|
||||||
find_xdata_el1([_ | Els]) -> find_xdata_el1(Els).
|
|
||||||
|
|
||||||
%% Produce a <command/> node to use as response from an adhoc_response
|
|
||||||
%% record, filling in values for language, node and session id from
|
|
||||||
%% the request.
|
|
||||||
%%
|
|
||||||
-spec produce_response(Adhoc_Request :: adhoc_request(),
|
|
||||||
Adhoc_Response :: adhoc_response()) ->
|
|
||||||
Xmlel::xmlel().
|
|
||||||
|
|
||||||
%% Produce a <command/> node to use as response from an adhoc_response
|
|
||||||
%% record.
|
|
||||||
produce_response(#adhoc_request{lang = Lang, node = Node, sessionid = SessionID},
|
|
||||||
Adhoc_Response) ->
|
|
||||||
produce_response(Adhoc_Response#adhoc_response{
|
|
||||||
lang = Lang, node = Node, sessionid = SessionID
|
|
||||||
}).
|
|
||||||
|
|
||||||
%%
|
|
||||||
-spec produce_response(Adhoc_Response::adhoc_response()) -> Xmlel::xmlel().
|
|
||||||
|
|
||||||
produce_response(
|
|
||||||
#adhoc_response{
|
|
||||||
%lang = _Lang,
|
|
||||||
node = Node,
|
|
||||||
sessionid = ProvidedSessionID,
|
|
||||||
status = Status,
|
|
||||||
defaultaction = DefaultAction,
|
|
||||||
actions = Actions,
|
|
||||||
notes = Notes,
|
|
||||||
elements = Elements
|
|
||||||
}) ->
|
|
||||||
SessionID = if is_binary(ProvidedSessionID),
|
|
||||||
ProvidedSessionID /= <<"">> -> ProvidedSessionID;
|
|
||||||
true -> jlib:now_to_utc_string(p1_time_compat:timestamp())
|
|
||||||
end,
|
|
||||||
case Actions of
|
|
||||||
[] ->
|
|
||||||
ActionsEls = [];
|
|
||||||
_ ->
|
|
||||||
case DefaultAction of
|
|
||||||
<<"">> -> ActionsElAttrs = [];
|
|
||||||
_ -> ActionsElAttrs = [{<<"execute">>, DefaultAction}]
|
|
||||||
end,
|
|
||||||
ActionsEls = [
|
|
||||||
#xmlel{
|
|
||||||
name = <<"actions">>,
|
|
||||||
attrs = ActionsElAttrs,
|
|
||||||
children = [
|
|
||||||
#xmlel{name = Action, attrs = [], children = []}
|
|
||||||
|| Action <- Actions]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
end,
|
|
||||||
NotesEls = lists:map(fun({Type, Text}) ->
|
|
||||||
#xmlel{
|
|
||||||
name = <<"note">>,
|
|
||||||
attrs = [{<<"type">>, Type}],
|
|
||||||
children = [{xmlcdata, Text}]
|
|
||||||
}
|
|
||||||
end, Notes),
|
|
||||||
#xmlel{
|
|
||||||
name = <<"command">>,
|
|
||||||
attrs = [
|
|
||||||
{<<"xmlns">>, ?NS_COMMANDS},
|
|
||||||
{<<"sessionid">>, SessionID},
|
|
||||||
{<<"node">>, Node},
|
|
||||||
{<<"status">>, iolist_to_binary(atom_to_list(Status))}
|
|
||||||
],
|
|
||||||
children = ActionsEls ++ NotesEls ++ Elements
|
|
||||||
}.
|
|
@ -41,8 +41,6 @@
|
|||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
%%-include("adhoc.hrl").
|
|
||||||
%%-include("jlib.hrl").
|
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
|
|
||||||
@ -1199,11 +1197,14 @@ iq_get_vcard(Lang) ->
|
|||||||
iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang,
|
iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang,
|
||||||
sub_els = [SubEl]}) ->
|
sub_els = [SubEl]}) ->
|
||||||
case {IQType, SubEl} of
|
case {IQType, SubEl} of
|
||||||
{set, #pubsub{create = Node, configure = {_, XData},
|
{set, #pubsub{create = Node, configure = Configure,
|
||||||
_ = undefined}} when is_binary(Node) ->
|
_ = undefined}} when is_binary(Node) ->
|
||||||
ServerHost = serverhost(Host),
|
ServerHost = serverhost(Host),
|
||||||
Plugins = config(ServerHost, plugins),
|
Plugins = config(ServerHost, plugins),
|
||||||
Config = get_xdata_fields(XData),
|
Config = case Configure of
|
||||||
|
{_, XData} -> get_xdata_fields(XData);
|
||||||
|
undefined -> []
|
||||||
|
end,
|
||||||
Type = hd(Plugins),
|
Type = hd(Plugins),
|
||||||
create_node(Host, ServerHost, Node, From, Type, Access, Config);
|
create_node(Host, ServerHost, Node, From, Type, Access, Config);
|
||||||
{set, #pubsub{publish = #ps_publish{node = Node, items = Items},
|
{set, #pubsub{publish = #ps_publish{node = Node, items = Items},
|
||||||
@ -1223,7 +1224,12 @@ iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang,
|
|||||||
_ = undefined}} ->
|
_ = undefined}} ->
|
||||||
case Items of
|
case Items of
|
||||||
[#ps_item{id = ItemId}] ->
|
[#ps_item{id = ItemId}] ->
|
||||||
delete_item(Host, Node, From, ItemId, Notify);
|
if ItemId /= <<>> ->
|
||||||
|
delete_item(Host, Node, From, ItemId, Notify);
|
||||||
|
true ->
|
||||||
|
{error, extended_error(xmpp:err_bad_request(),
|
||||||
|
err_item_required())}
|
||||||
|
end;
|
||||||
[] ->
|
[] ->
|
||||||
{error, extended_error(xmpp:err_bad_request(), err_item_required())};
|
{error, extended_error(xmpp:err_bad_request(), err_item_required())};
|
||||||
_ ->
|
_ ->
|
||||||
@ -1259,11 +1265,14 @@ iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang,
|
|||||||
jid = JID, xdata = XData},
|
jid = JID, xdata = XData},
|
||||||
_ = undefined}} ->
|
_ = undefined}} ->
|
||||||
set_options(Host, Node, JID, SubId, get_xdata_fields(XData));
|
set_options(Host, Node, JID, SubId, get_xdata_fields(XData));
|
||||||
|
{set, #pubsub{}} ->
|
||||||
|
{error, xmpp:err_bad_request()};
|
||||||
_ ->
|
_ ->
|
||||||
{error, xmpp:err_feature_not_implemented()}
|
{error, xmpp:err_feature_not_implemented()}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec iq_pubsub_owner(binary() | ljid(), iq()) -> {result, pubsub()} | {error, error()}.
|
-spec iq_pubsub_owner(binary() | ljid(), iq()) -> {result, pubsub_owner() | undefined} |
|
||||||
|
{error, error()}.
|
||||||
iq_pubsub_owner(Host, #iq{type = IQType, from = From,
|
iq_pubsub_owner(Host, #iq{type = IQType, from = From,
|
||||||
lang = Lang, sub_els = [SubEl]}) ->
|
lang = Lang, sub_els = [SubEl]}) ->
|
||||||
case {IQType, SubEl} of
|
case {IQType, SubEl} of
|
||||||
@ -1275,7 +1284,7 @@ iq_pubsub_owner(Host, #iq{type = IQType, from = From,
|
|||||||
undefined ->
|
undefined ->
|
||||||
{error, xmpp:err_bad_request(<<"No data form found">>, Lang)};
|
{error, xmpp:err_bad_request(<<"No data form found">>, Lang)};
|
||||||
#xdata{type = cancel} ->
|
#xdata{type = cancel} ->
|
||||||
{result, #pubsub{}};
|
{result, #pubsub_owner{}};
|
||||||
#xdata{type = submit} ->
|
#xdata{type = submit} ->
|
||||||
Config = get_xdata_fields(XData),
|
Config = get_xdata_fields(XData),
|
||||||
set_configure(Host, Node, From, Config, Lang);
|
set_configure(Host, Node, From, Config, Lang);
|
||||||
@ -1684,7 +1693,7 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
|
|||||||
%%<li>The node is the root collection node, which cannot be deleted.</li>
|
%%<li>The node is the root collection node, which cannot be deleted.</li>
|
||||||
%%<li>The specified node does not exist.</li>
|
%%<li>The specified node does not exist.</li>
|
||||||
%%</ul>
|
%%</ul>
|
||||||
-spec delete_node(host(), binary(), jid()) -> {result, pubsub()} | {error, error()}.
|
-spec delete_node(host(), binary(), jid()) -> {result, pubsub_owner()} | {error, error()}.
|
||||||
delete_node(_Host, <<>>, _Owner) ->
|
delete_node(_Host, <<>>, _Owner) ->
|
||||||
{error, xmpp:err_not_allowed(<<"No node specified">>, ?MYLANG)};
|
{error, xmpp:err_not_allowed(<<"No node specified">>, ?MYLANG)};
|
||||||
delete_node(Host, Node, Owner) ->
|
delete_node(Host, Node, Owner) ->
|
||||||
@ -1701,7 +1710,7 @@ delete_node(Host, Node, Owner) ->
|
|||||||
{error, xmpp:err_forbidden(<<"Owner privileges required">>, ?MYLANG)}
|
{error, xmpp:err_forbidden(<<"Owner privileges required">>, ?MYLANG)}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
Reply = [],
|
Reply = undefined,
|
||||||
ServerHost = serverhost(Host),
|
ServerHost = serverhost(Host),
|
||||||
case transaction(Host, Node, Action, transaction) of
|
case transaction(Host, Node, Action, transaction) of
|
||||||
{result, {_, {SubsByDepth, {Result, broadcast, Removed}}}} ->
|
{result, {_, {SubsByDepth, {Result, broadcast, Removed}}}} ->
|
||||||
@ -2566,8 +2575,8 @@ get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
|
|||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_subscriptions(binary(), binary(), jid()) -> {result, pubsub()} |
|
-spec get_subscriptions(host(), binary(), jid()) -> {result, pubsub_owner()} |
|
||||||
{error, error()}.
|
{error, error()}.
|
||||||
get_subscriptions(Host, Node, JID) ->
|
get_subscriptions(Host, Node, JID) ->
|
||||||
Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
|
Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
|
||||||
Features = plugin_features(Host, Type),
|
Features = plugin_features(Host, Type),
|
||||||
@ -2595,7 +2604,7 @@ get_subscriptions(Host, Node, JID) ->
|
|||||||
({AJID, Sub, SubId}) ->
|
({AJID, Sub, SubId}) ->
|
||||||
[#ps_subscription{jid = AJID, type = Sub, subid = SubId}]
|
[#ps_subscription{jid = AJID, type = Sub, subid = SubId}]
|
||||||
end, Subs),
|
end, Subs),
|
||||||
{result, #pubsub{subscriptions = {Node, Entities}}};
|
{result, #pubsub_owner{subscriptions = {Node, Entities}}};
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
@ -2623,6 +2632,8 @@ get_subscriptions_for_send_last(Host, PType, sql, JID, LJID, BJID) ->
|
|||||||
get_subscriptions_for_send_last(_Host, _PType, _, _JID, _LJID, _BJID) ->
|
get_subscriptions_for_send_last(_Host, _PType, _, _JID, _LJID, _BJID) ->
|
||||||
[].
|
[].
|
||||||
|
|
||||||
|
-spec set_subscriptions(host(), binary(), jid(), [ps_subscription()]) ->
|
||||||
|
{result, undefined} | {error, error()}.
|
||||||
set_subscriptions(Host, Node, From, Entities) ->
|
set_subscriptions(Host, Node, From, Entities) ->
|
||||||
Owner = jid:tolower(jid:remove_resource(From)),
|
Owner = jid:tolower(jid:remove_resource(From)),
|
||||||
Notify = fun(#ps_subscription{jid = JID, type = Sub}) ->
|
Notify = fun(#ps_subscription{jid = JID, type = Sub}) ->
|
||||||
@ -3152,7 +3163,7 @@ user_resource(_, _, Resource) ->
|
|||||||
|
|
||||||
%%%%%%% Configuration handling
|
%%%%%%% Configuration handling
|
||||||
-spec get_configure(host(), binary(), binary(), jid(),
|
-spec get_configure(host(), binary(), binary(), jid(),
|
||||||
binary()) -> {error, error()} | {result, pubsub()}.
|
binary()) -> {error, error()} | {result, pubsub_owner()}.
|
||||||
get_configure(Host, ServerHost, Node, From, Lang) ->
|
get_configure(Host, ServerHost, Node, From, Lang) ->
|
||||||
Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) ->
|
Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) ->
|
||||||
case node_call(Host, Type, get_affiliation, [Nidx, From]) of
|
case node_call(Host, Type, get_affiliation, [Nidx, From]) of
|
||||||
@ -3171,7 +3182,7 @@ get_configure(Host, ServerHost, Node, From, Lang) ->
|
|||||||
Other -> Other
|
Other -> Other
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_default(host(), binary(), jid(), binary()) -> {result, pubsub()}.
|
-spec get_default(host(), binary(), jid(), binary()) -> {result, pubsub_owner()}.
|
||||||
get_default(Host, Node, _From, Lang) ->
|
get_default(Host, Node, _From, Lang) ->
|
||||||
Type = select_type(Host, Host, Node),
|
Type = select_type(Host, Host, Node),
|
||||||
Options = node_options(Host, Type),
|
Options = node_options(Host, Type),
|
||||||
@ -3371,6 +3382,8 @@ get_configure_xfields(_Type, Options, Lang, Groups) ->
|
|||||||
%%</ul>
|
%%</ul>
|
||||||
-spec set_configure(host(), binary(), jid(), [{binary(), [binary()]}],
|
-spec set_configure(host(), binary(), jid(), [{binary(), [binary()]}],
|
||||||
binary()) -> {result, undefined} | {error, error()}.
|
binary()) -> {result, undefined} | {error, error()}.
|
||||||
|
set_configure(_Host, <<>>, _From, _Config, _Lang) ->
|
||||||
|
{error, extended_error(xmpp:err_bad_request(), err_nodeid_required())};
|
||||||
set_configure(Host, Node, From, Config, Lang) ->
|
set_configure(Host, Node, From, Config, Lang) ->
|
||||||
Action =
|
Action =
|
||||||
fun(#pubsub_node{options = Options, type = Type, id = Nidx} = N) ->
|
fun(#pubsub_node{options = Options, type = Type, id = Nidx} = N) ->
|
||||||
@ -3664,26 +3677,28 @@ select_type(ServerHost, Host, Node, Type) ->
|
|||||||
select_type(ServerHost, Host, Node) ->
|
select_type(ServerHost, Host, Node) ->
|
||||||
select_type(ServerHost, Host, Node, hd(plugins(Host))).
|
select_type(ServerHost, Host, Node, hd(plugins(Host))).
|
||||||
|
|
||||||
|
-spec feature(binary()) -> binary().
|
||||||
feature(<<"rsm">>) -> ?NS_RSM;
|
feature(<<"rsm">>) -> ?NS_RSM;
|
||||||
feature(Feature) -> <<(?NS_PUBSUB)/binary, "#", Feature/binary>>.
|
feature(Feature) -> <<(?NS_PUBSUB)/binary, "#", Feature/binary>>.
|
||||||
|
|
||||||
|
-spec features() -> [binary()].
|
||||||
features() ->
|
features() ->
|
||||||
[% see plugin "access-authorize", % OPTIONAL
|
[% see plugin "access-authorize", % OPTIONAL
|
||||||
<<"access-open">>, % OPTIONAL this relates to access_model option in node_hometree
|
<<"access-open">>, % OPTIONAL this relates to access_model option in node_hometree
|
||||||
<<"access-presence">>, % OPTIONAL this relates to access_model option in node_pep
|
<<"access-presence">>, % OPTIONAL this relates to access_model option in node_pep
|
||||||
<<"access-whitelist">>, % OPTIONAL
|
<<"access-whitelist">>, % OPTIONAL
|
||||||
<<"collections">>, % RECOMMENDED
|
<<"collections">>, % RECOMMENDED
|
||||||
<<"config-node">>, % RECOMMENDED
|
<<"config-node">>, % RECOMMENDED
|
||||||
<<"create-and-configure">>, % RECOMMENDED
|
<<"create-and-configure">>, % RECOMMENDED
|
||||||
<<"item-ids">>, % RECOMMENDED
|
<<"item-ids">>, % RECOMMENDED
|
||||||
<<"last-published">>, % RECOMMENDED
|
<<"last-published">>, % RECOMMENDED
|
||||||
<<"member-affiliation">>, % RECOMMENDED
|
<<"member-affiliation">>, % RECOMMENDED
|
||||||
<<"presence-notifications">>, % OPTIONAL
|
<<"presence-notifications">>, % OPTIONAL
|
||||||
<<"presence-subscribe">>, % RECOMMENDED
|
<<"presence-subscribe">>, % RECOMMENDED
|
||||||
<<"publisher-affiliation">>, % RECOMMENDED
|
<<"publisher-affiliation">>, % RECOMMENDED
|
||||||
<<"publish-only-affiliation">>, % OPTIONAL
|
<<"publish-only-affiliation">>, % OPTIONAL
|
||||||
<<"retrieve-default">>,
|
<<"retrieve-default">>,
|
||||||
<<"shim">>]. % RECOMMENDED
|
<<"shim">>]. % RECOMMENDED
|
||||||
|
|
||||||
% see plugin "retrieve-items", % RECOMMENDED
|
% see plugin "retrieve-items", % RECOMMENDED
|
||||||
% see plugin "retrieve-subscriptions", % RECOMMENDED
|
% see plugin "retrieve-subscriptions", % RECOMMENDED
|
||||||
@ -3920,7 +3935,7 @@ err_subid_required() ->
|
|||||||
err_too_many_subscriptions() ->
|
err_too_many_subscriptions() ->
|
||||||
#ps_error{type = 'too-many-subscriptions'}.
|
#ps_error{type = 'too-many-subscriptions'}.
|
||||||
|
|
||||||
-spec err_unsupported(ps_error_feature()) -> ps_error().
|
-spec err_unsupported(ps_feature()) -> ps_error().
|
||||||
err_unsupported(Feature) ->
|
err_unsupported(Feature) ->
|
||||||
#ps_error{type = 'unsupported', feature = Feature}.
|
#ps_error{type = 'unsupported', feature = Feature}.
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
-author('bjc@kublai.com').
|
-author('bjc@kublai.com').
|
||||||
|
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
-export([init/3, terminate/2, options/0, features/0,
|
-export([init/3, terminate/2, options/0, features/0,
|
||||||
create_node_permission/6, create_node/2, delete_node/1,
|
create_node_permission/6, create_node/2, delete_node/1,
|
||||||
@ -78,8 +78,9 @@ publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) ->
|
|||||||
case find_opt(node_type, Options) of
|
case find_opt(node_type, Options) of
|
||||||
collection ->
|
collection ->
|
||||||
Txt = <<"Publishing items to collection node is not allowed">>,
|
Txt = <<"Publishing items to collection node is not allowed">>,
|
||||||
{error,
|
{error, mod_pubsub:extended_error(
|
||||||
?ERR_EXTENDED(?ERRT_NOT_ALLOWED(?MYLANG, Txt), <<"publish">>)};
|
xmpp:err_not_allowed(Txt, ?MYLANG),
|
||||||
|
mod_pubsub:err_unsupported('publish'))};
|
||||||
_ ->
|
_ ->
|
||||||
node_hometree:publish_item(Nidx, Publisher, Model,
|
node_hometree:publish_item(Nidx, Publisher, Model,
|
||||||
MaxItems, ItemId, Payload, PubOpts)
|
MaxItems, ItemId, Payload, PubOpts)
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
-author('christophe.romain@process-one.net').
|
-author('christophe.romain@process-one.net').
|
||||||
|
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
-export([init/3, terminate/2, options/0, features/0,
|
-export([init/3, terminate/2, options/0, features/0,
|
||||||
create_node_permission/6, create_node/2, delete_node/1,
|
create_node_permission/6, create_node/2, delete_node/1,
|
||||||
@ -94,10 +94,12 @@ delete_node(Nodes) ->
|
|||||||
|
|
||||||
subscribe_node(_Nidx, _Sender, _Subscriber, _AccessModel, _SendLast, _PresenceSubscription,
|
subscribe_node(_Nidx, _Sender, _Subscriber, _AccessModel, _SendLast, _PresenceSubscription,
|
||||||
_RosterGroup, _Options) ->
|
_RosterGroup, _Options) ->
|
||||||
{error, ?ERR_FORBIDDEN}.
|
{error, mod_pubsub:extended_error(xmpp:err_feature_not_implemented(),
|
||||||
|
mod_pubsub:err_unsupported('subscribe'))}.
|
||||||
|
|
||||||
unsubscribe_node(_Nidx, _Sender, _Subscriber, _SubId) ->
|
unsubscribe_node(_Nidx, _Sender, _Subscriber, _SubId) ->
|
||||||
{error, ?ERR_FORBIDDEN}.
|
{error, mod_pubsub:extended_error(xmpp:err_feature_not_implemented(),
|
||||||
|
mod_pubsub:err_unsupported('subscribe'))}.
|
||||||
|
|
||||||
publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload,
|
publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload,
|
||||||
PubOpts) ->
|
PubOpts) ->
|
||||||
@ -118,10 +120,12 @@ remove_extra_items(_Nidx, _MaxItems, ItemIds) ->
|
|||||||
{result, {ItemIds, []}}.
|
{result, {ItemIds, []}}.
|
||||||
|
|
||||||
delete_item(_Nidx, _Publisher, _PublishModel, _ItemId) ->
|
delete_item(_Nidx, _Publisher, _PublishModel, _ItemId) ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND}.
|
{error, mod_pubsub:extended_error(xmpp:err_feature_not_implemented(),
|
||||||
|
mod_pubsub:err_unsupported('delete-items'))}.
|
||||||
|
|
||||||
purge_node(_Nidx, _Owner) ->
|
purge_node(_Nidx, _Owner) ->
|
||||||
{error, ?ERR_FORBIDDEN}.
|
{error, mod_pubsub:extended_error(xmpp:err_feature_not_implemented(),
|
||||||
|
mod_pubsub:err_unsupported('purge-nodes'))}.
|
||||||
|
|
||||||
get_entity_affiliations(_Host, _Owner) ->
|
get_entity_affiliations(_Host, _Owner) ->
|
||||||
{result, []}.
|
{result, []}.
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
-author('christophe.romain@process-one.net').
|
-author('christophe.romain@process-one.net').
|
||||||
|
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
-export([init/3, terminate/2, options/0, features/0,
|
-export([init/3, terminate/2, options/0, features/0,
|
||||||
create_node_permission/6, create_node/2, delete_node/1,
|
create_node_permission/6, create_node/2, delete_node/1,
|
||||||
@ -107,8 +107,8 @@ features() ->
|
|||||||
<<"retrieve-items">>,
|
<<"retrieve-items">>,
|
||||||
<<"retrieve-subscriptions">>,
|
<<"retrieve-subscriptions">>,
|
||||||
<<"subscribe">>,
|
<<"subscribe">>,
|
||||||
|
%%<<"subscription-options">>,
|
||||||
<<"subscription-notifications">>].
|
<<"subscription-notifications">>].
|
||||||
%%<<"subscription-options">>
|
|
||||||
|
|
||||||
%% @doc Checks if the current user has the permission to create the requested node
|
%% @doc Checks if the current user has the permission to create the requested node
|
||||||
%% <p>In flat node, any unused node name is allowed. The access parameter is also
|
%% <p>In flat node, any unused node name is allowed. The access parameter is also
|
||||||
@ -196,27 +196,27 @@ subscribe_node(Nidx, Sender, Subscriber, AccessModel,
|
|||||||
Owner = Affiliation == owner,
|
Owner = Affiliation == owner,
|
||||||
if not Authorized ->
|
if not Authorized ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"invalid-jid">>)};
|
mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_invalid_jid())};
|
||||||
(Affiliation == outcast) or (Affiliation == publish_only) ->
|
(Affiliation == outcast) or (Affiliation == publish_only) ->
|
||||||
{error, ?ERR_FORBIDDEN};
|
{error, xmpp:err_forbidden()};
|
||||||
PendingSubscription ->
|
PendingSubscription ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"pending-subscription">>)};
|
mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_pending_subscription())};
|
||||||
(AccessModel == presence) and (not PresenceSubscription) and (not Owner) ->
|
(AccessModel == presence) and (not PresenceSubscription) and (not Owner) ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)};
|
mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_presence_subscription_required())};
|
||||||
(AccessModel == roster) and (not RosterGroup) and (not Owner) ->
|
(AccessModel == roster) and (not RosterGroup) and (not Owner) ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)};
|
mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_not_in_roster_group())};
|
||||||
(AccessModel == whitelist) and (not Whitelisted) and (not Owner) ->
|
(AccessModel == whitelist) and (not Whitelisted) and (not Owner) ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
|
mod_pubsub:extended_error((xmpp:err_not_allowed()), mod_pubsub:err_closed_node())};
|
||||||
%%MustPay ->
|
%%MustPay ->
|
||||||
%% % Payment is required for a subscription
|
%% % Payment is required for a subscription
|
||||||
%% {error, ?ERR_PAYMENT_REQUIRED};
|
%% {error, ?ERR_PAYMENT_REQUIRED};
|
||||||
%%ForbiddenAnonymous ->
|
%%ForbiddenAnonymous ->
|
||||||
%% % Requesting entity is anonymous
|
%% % Requesting entity is anonymous
|
||||||
%% {error, ?ERR_FORBIDDEN};
|
%% {error, xmpp:err_forbidden()};
|
||||||
true ->
|
true ->
|
||||||
%%SubId = pubsub_subscription:add_subscription(Subscriber, Nidx, Options),
|
%%SubId = pubsub_subscription:add_subscription(Subscriber, Nidx, Options),
|
||||||
{NewSub, SubId} = case Subscriptions of
|
{NewSub, SubId} = case Subscriptions of
|
||||||
@ -265,17 +265,17 @@ unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
|
|||||||
if
|
if
|
||||||
%% Requesting entity is prohibited from unsubscribing entity
|
%% Requesting entity is prohibited from unsubscribing entity
|
||||||
not Authorized ->
|
not Authorized ->
|
||||||
{error, ?ERR_FORBIDDEN};
|
{error, xmpp:err_forbidden()};
|
||||||
%% Entity did not specify SubId
|
%% Entity did not specify SubId
|
||||||
%%SubId == "", ?? ->
|
%%SubId == "", ?? ->
|
||||||
%% {error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
|
%% {error, mod_pubsub:extended_error(xmpp:err_bad_request(), "subid-required")};
|
||||||
%% Invalid subscription identifier
|
%% Invalid subscription identifier
|
||||||
%%InvalidSubId ->
|
%%InvalidSubId ->
|
||||||
%% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
|
%% {error, mod_pubsub:extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
|
||||||
%% Requesting entity is not a subscriber
|
%% Requesting entity is not a subscriber
|
||||||
Subscriptions == [] ->
|
Subscriptions == [] ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL), <<"not-subscribed">>)};
|
mod_pubsub:extended_error(xmpp:err_unexpected_request(), mod_pubsub:err_not_subscribed())};
|
||||||
%% Subid supplied, so use that.
|
%% Subid supplied, so use that.
|
||||||
SubIdExists ->
|
SubIdExists ->
|
||||||
Sub = first_in_list(fun
|
Sub = first_in_list(fun
|
||||||
@ -289,7 +289,7 @@ unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
|
|||||||
{result, default};
|
{result, default};
|
||||||
false ->
|
false ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL), <<"not-subscribed">>)}
|
mod_pubsub:extended_error(xmpp:err_unexpected_request(), mod_pubsub:err_not_subscribed())}
|
||||||
end;
|
end;
|
||||||
%% Asking to remove all subscriptions to the given node
|
%% Asking to remove all subscriptions to the given node
|
||||||
SubId == all ->
|
SubId == all ->
|
||||||
@ -302,7 +302,7 @@ unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
|
|||||||
%% No subid and more than one possible subscription match.
|
%% No subid and more than one possible subscription match.
|
||||||
true ->
|
true ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"subid-required">>)}
|
mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_subid_required())}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
delete_subscriptions(SubKey, Nidx, Subscriptions, SubState) ->
|
delete_subscriptions(SubKey, Nidx, Subscriptions, SubState) ->
|
||||||
@ -366,7 +366,7 @@ publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload,
|
|||||||
or (Affiliation == publisher)
|
or (Affiliation == publisher)
|
||||||
or (Affiliation == publish_only))
|
or (Affiliation == publish_only))
|
||||||
or (Subscribed == true)) ->
|
or (Subscribed == true)) ->
|
||||||
{error, ?ERR_FORBIDDEN};
|
{error, xmpp:err_forbidden()};
|
||||||
true ->
|
true ->
|
||||||
if MaxItems > 0 ->
|
if MaxItems > 0 ->
|
||||||
Now = p1_time_compat:timestamp(),
|
Now = p1_time_compat:timestamp(),
|
||||||
@ -425,7 +425,7 @@ delete_item(Nidx, Publisher, PublishModel, ItemId) ->
|
|||||||
_ -> false
|
_ -> false
|
||||||
end,
|
end,
|
||||||
if not Allowed ->
|
if not Allowed ->
|
||||||
{error, ?ERR_FORBIDDEN};
|
{error, xmpp:err_forbidden()};
|
||||||
true ->
|
true ->
|
||||||
case lists:member(ItemId, Items) of
|
case lists:member(ItemId, Items) of
|
||||||
true ->
|
true ->
|
||||||
@ -450,9 +450,9 @@ delete_item(Nidx, Publisher, PublishModel, ItemId) ->
|
|||||||
(_, Res) ->
|
(_, Res) ->
|
||||||
Res
|
Res
|
||||||
end,
|
end,
|
||||||
{error, ?ERR_ITEM_NOT_FOUND}, States);
|
{error, xmpp:err_item_not_found()}, States);
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND}
|
{error, xmpp:err_item_not_found()}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
@ -474,7 +474,7 @@ purge_node(Nidx, Owner) ->
|
|||||||
States),
|
States),
|
||||||
{result, {default, broadcast}};
|
{result, {default, broadcast}};
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_FORBIDDEN}
|
{error, xmpp:err_forbidden()}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc <p>Return the current affiliations for the given user</p>
|
%% @doc <p>Return the current affiliations for the given user</p>
|
||||||
@ -581,7 +581,7 @@ set_subscriptions(Nidx, Owner, Subscription, SubId) ->
|
|||||||
case Subscription of
|
case Subscription of
|
||||||
none ->
|
none ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"not-subscribed">>)};
|
mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_not_subscribed())};
|
||||||
_ ->
|
_ ->
|
||||||
new_subscription(Nidx, Owner, Subscription, SubState)
|
new_subscription(Nidx, Owner, Subscription, SubState)
|
||||||
end;
|
end;
|
||||||
@ -592,7 +592,7 @@ set_subscriptions(Nidx, Owner, Subscription, SubId) ->
|
|||||||
end;
|
end;
|
||||||
{<<>>, [_ | _]} ->
|
{<<>>, [_ | _]} ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"subid-required">>)};
|
mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_subid_required())};
|
||||||
_ ->
|
_ ->
|
||||||
case Subscription of
|
case Subscription of
|
||||||
none -> unsub_with_subid(Nidx, SubId, SubState);
|
none -> unsub_with_subid(Nidx, SubId, SubState);
|
||||||
@ -721,23 +721,23 @@ get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId, RSM
|
|||||||
can_fetch_item(Affiliation, FullSubscriptions),
|
can_fetch_item(Affiliation, FullSubscriptions),
|
||||||
if %%SubId == "", ?? ->
|
if %%SubId == "", ?? ->
|
||||||
%% Entity has multiple subscriptions to the node but does not specify a subscription ID
|
%% Entity has multiple subscriptions to the node but does not specify a subscription ID
|
||||||
%{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
|
%{error, mod_pubsub:extended_error(xmpp:err_bad_request(), "subid-required")};
|
||||||
%%InvalidSubId ->
|
%%InvalidSubId ->
|
||||||
%% Entity is subscribed but specifies an invalid subscription ID
|
%% Entity is subscribed but specifies an invalid subscription ID
|
||||||
%{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
|
%{error, mod_pubsub:extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
|
||||||
(Affiliation == outcast) or (Affiliation == publish_only) ->
|
(Affiliation == outcast) or (Affiliation == publish_only) ->
|
||||||
{error, ?ERR_FORBIDDEN};
|
{error, xmpp:err_forbidden()};
|
||||||
(AccessModel == presence) and not PresenceSubscription ->
|
(AccessModel == presence) and not PresenceSubscription ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)};
|
mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_presence_subscription_required())};
|
||||||
(AccessModel == roster) and not RosterGroup ->
|
(AccessModel == roster) and not RosterGroup ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)};
|
mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_not_in_roster_group())};
|
||||||
(AccessModel == whitelist) and not Whitelisted ->
|
(AccessModel == whitelist) and not Whitelisted ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
|
mod_pubsub:extended_error((xmpp:err_not_allowed()), mod_pubsub:err_closed_node())};
|
||||||
(AccessModel == authorize) and not Whitelisted ->
|
(AccessModel == authorize) and not Whitelisted ->
|
||||||
{error, ?ERR_FORBIDDEN};
|
{error, xmpp:err_forbidden()};
|
||||||
%%MustPay ->
|
%%MustPay ->
|
||||||
%% % Payment is required for a subscription
|
%% % Payment is required for a subscription
|
||||||
%% {error, ?ERR_PAYMENT_REQUIRED};
|
%% {error, ?ERR_PAYMENT_REQUIRED};
|
||||||
@ -750,7 +750,7 @@ get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId, RSM
|
|||||||
get_item(Nidx, ItemId) ->
|
get_item(Nidx, ItemId) ->
|
||||||
case mnesia:read({pubsub_item, {ItemId, Nidx}}) of
|
case mnesia:read({pubsub_item, {ItemId, Nidx}}) of
|
||||||
[Item] when is_record(Item, pubsub_item) -> {result, Item};
|
[Item] when is_record(Item, pubsub_item) -> {result, Item};
|
||||||
_ -> {error, ?ERR_ITEM_NOT_FOUND}
|
_ -> {error, xmpp:err_item_not_found()}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
|
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
|
||||||
@ -762,23 +762,23 @@ get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _Sub
|
|||||||
Whitelisted = can_fetch_item(Affiliation, Subscriptions),
|
Whitelisted = can_fetch_item(Affiliation, Subscriptions),
|
||||||
if %%SubId == "", ?? ->
|
if %%SubId == "", ?? ->
|
||||||
%% Entity has multiple subscriptions to the node but does not specify a subscription ID
|
%% Entity has multiple subscriptions to the node but does not specify a subscription ID
|
||||||
%{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
|
%{error, mod_pubsub:extended_error(xmpp:err_bad_request(), "subid-required")};
|
||||||
%%InvalidSubId ->
|
%%InvalidSubId ->
|
||||||
%% Entity is subscribed but specifies an invalid subscription ID
|
%% Entity is subscribed but specifies an invalid subscription ID
|
||||||
%{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
|
%{error, mod_pubsub:extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
|
||||||
(Affiliation == outcast) or (Affiliation == publish_only) ->
|
(Affiliation == outcast) or (Affiliation == publish_only) ->
|
||||||
{error, ?ERR_FORBIDDEN};
|
{error, xmpp:err_forbidden()};
|
||||||
(AccessModel == presence) and not PresenceSubscription ->
|
(AccessModel == presence) and not PresenceSubscription ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)};
|
mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_presence_subscription_required())};
|
||||||
(AccessModel == roster) and not RosterGroup ->
|
(AccessModel == roster) and not RosterGroup ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)};
|
mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_not_in_roster_group())};
|
||||||
(AccessModel == whitelist) and not Whitelisted ->
|
(AccessModel == whitelist) and not Whitelisted ->
|
||||||
{error,
|
{error,
|
||||||
?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
|
mod_pubsub:extended_error((xmpp:err_not_allowed()), mod_pubsub:err_closed_node())};
|
||||||
(AccessModel == authorize) and not Whitelisted ->
|
(AccessModel == authorize) and not Whitelisted ->
|
||||||
{error, ?ERR_FORBIDDEN};
|
{error, xmpp:err_forbidden()};
|
||||||
%%MustPay ->
|
%%MustPay ->
|
||||||
%% % Payment is required for a subscription
|
%% % Payment is required for a subscription
|
||||||
%% {error, ?ERR_PAYMENT_REQUIRED};
|
%% {error, ?ERR_PAYMENT_REQUIRED};
|
||||||
|
@ -677,7 +677,7 @@ get_items(Nidx, _From, #rsm_set{max = Max, index = IncIndex,
|
|||||||
"where exists ( select count(*) as count1 "
|
"where exists ( select count(*) as count1 "
|
||||||
"from pubsub_item where nodeid='">>, SNidx,
|
"from pubsub_item where nodeid='">>, SNidx,
|
||||||
<<"' and modification > pi.modification having count1 = ">>,
|
<<"' and modification > pi.modification having count1 = ">>,
|
||||||
IncIndex, <<" );">>]) of
|
integer_to_binary(IncIndex), <<" );">>]) of
|
||||||
{selected, [_], [[O]]} ->
|
{selected, [_], [[O]]} ->
|
||||||
[<<"modification">>, <<"'", O/binary, "'">>];
|
[<<"modification">>, <<"'", O/binary, "'">>];
|
||||||
_ ->
|
_ ->
|
||||||
@ -699,7 +699,7 @@ get_items(Nidx, _From, #rsm_set{max = Max, index = IncIndex,
|
|||||||
end,
|
end,
|
||||||
Query = fun(mssql, _) ->
|
Query = fun(mssql, _) ->
|
||||||
ejabberd_sql:sql_query_t(
|
ejabberd_sql:sql_query_t(
|
||||||
[<<"select top ">>, Max,
|
[<<"select top ">>, integer_to_binary(Max),
|
||||||
<<" itemid, publisher, creation, modification, payload "
|
<<" itemid, publisher, creation, modification, payload "
|
||||||
"from pubsub_item where nodeid='">>, SNidx,
|
"from pubsub_item where nodeid='">>, SNidx,
|
||||||
<<"' and ">>, AttrName, <<" ">>, Way, <<" ">>, Id, <<" order by ">>,
|
<<"' and ">>, AttrName, <<" ">>, Way, <<" ">>, Id, <<" order by ">>,
|
||||||
@ -709,7 +709,8 @@ get_items(Nidx, _From, #rsm_set{max = Max, index = IncIndex,
|
|||||||
[<<"select itemid, publisher, creation, modification, payload "
|
[<<"select itemid, publisher, creation, modification, payload "
|
||||||
"from pubsub_item where nodeid='">>, SNidx,
|
"from pubsub_item where nodeid='">>, SNidx,
|
||||||
<<"' and ">>, AttrName, <<" ">>, Way, <<" ">>, Id, <<" order by ">>,
|
<<"' and ">>, AttrName, <<" ">>, Way, <<" ">>, Id, <<" order by ">>,
|
||||||
AttrName, <<" ">>, Order, <<" limit ">>, Max, <<" ;">>])
|
AttrName, <<" ">>, Order, <<" limit ">>,
|
||||||
|
integer_to_binary(Max), <<" ;">>])
|
||||||
end,
|
end,
|
||||||
case ejabberd_sql:sql_query_t(Query) of
|
case ejabberd_sql:sql_query_t(Query) of
|
||||||
{selected, [<<"itemid">>, <<"publisher">>, <<"creation">>,
|
{selected, [<<"itemid">>, <<"publisher">>, <<"creation">>,
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
-include_lib("stdlib/include/qlc.hrl").
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
|
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
-export([init/3, terminate/2, options/0, set_node/1,
|
-export([init/3, terminate/2, options/0, set_node/1,
|
||||||
get_node/3, get_node/2, get_node/1, get_nodes/2,
|
get_node/3, get_node/2, get_node/1, get_nodes/2,
|
||||||
@ -69,13 +69,13 @@ create_node(Key, Node, Type, Owner, Options, Parents) ->
|
|||||||
Other -> Other
|
Other -> Other
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERRT_CONFLICT(?MYLANG, <<"Node already exists">>)}
|
{error, xmpp:err_conflict(<<"Node already exists">>, ?MYLANG)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
delete_node(Key, Node) ->
|
delete_node(Key, Node) ->
|
||||||
case find_node(Key, Node) of
|
case find_node(Key, Node) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)};
|
{error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)};
|
||||||
Record ->
|
Record ->
|
||||||
lists:foreach(fun (#pubsub_node{options = Opts} = Child) ->
|
lists:foreach(fun (#pubsub_node{options = Opts} = Child) ->
|
||||||
NewOpts = remove_config_parent(Node, Opts),
|
NewOpts = remove_config_parent(Node, Opts),
|
||||||
@ -99,7 +99,7 @@ get_node(Host, Node, _From) ->
|
|||||||
|
|
||||||
get_node(Host, Node) ->
|
get_node(Host, Node) ->
|
||||||
case find_node(Host, Node) of
|
case find_node(Host, Node) of
|
||||||
false -> {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)};
|
false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)};
|
||||||
Record -> Record
|
Record -> Record
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ get_nodes(Key) ->
|
|||||||
get_parentnodes(Host, Node, _From) ->
|
get_parentnodes(Host, Node, _From) ->
|
||||||
case find_node(Host, Node) of
|
case find_node(Host, Node) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)};
|
{error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)};
|
||||||
#pubsub_node{parents = Parents} ->
|
#pubsub_node{parents = Parents} ->
|
||||||
Q = qlc:q([N
|
Q = qlc:q([N
|
||||||
|| #pubsub_node{nodeid = {NHost, NNode}} = N
|
|| #pubsub_node{nodeid = {NHost, NNode}} = N
|
||||||
@ -139,7 +139,7 @@ get_subnodes(Host, <<>>) ->
|
|||||||
get_subnodes_helper(Host, <<>>);
|
get_subnodes_helper(Host, <<>>);
|
||||||
get_subnodes(Host, Node) ->
|
get_subnodes(Host, Node) ->
|
||||||
case find_node(Host, Node) of
|
case find_node(Host, Node) of
|
||||||
false -> {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)};
|
false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)};
|
||||||
_ -> get_subnodes_helper(Host, Node)
|
_ -> get_subnodes_helper(Host, Node)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -226,13 +226,13 @@ validate_parentage(Key, Owners, [<<>> | T]) ->
|
|||||||
validate_parentage(Key, Owners, [ParentID | T]) ->
|
validate_parentage(Key, Owners, [ParentID | T]) ->
|
||||||
case find_node(Key, ParentID) of
|
case find_node(Key, ParentID) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)};
|
{error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)};
|
||||||
#pubsub_node{owners = POwners, options = POptions} ->
|
#pubsub_node{owners = POwners, options = POptions} ->
|
||||||
NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions),
|
NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions),
|
||||||
MutualOwners = [O || O <- Owners, PO <- POwners, O == PO],
|
MutualOwners = [O || O <- Owners, PO <- POwners, O == PO],
|
||||||
case {MutualOwners, NodeType} of
|
case {MutualOwners, NodeType} of
|
||||||
{[], _} -> {error, ?ERR_FORBIDDEN};
|
{[], _} -> {error, xmpp:err_forbidden()};
|
||||||
{_, collection} -> validate_parentage(Key, Owners, T);
|
{_, collection} -> validate_parentage(Key, Owners, T);
|
||||||
{_, _} -> {error, ?ERR_NOT_ALLOWED}
|
{_, _} -> {error, xmpp:err_not_allowed()}
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
-include_lib("stdlib/include/qlc.hrl").
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
|
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
-export([init/3, terminate/2, options/0, set_node/1,
|
-export([init/3, terminate/2, options/0, set_node/1,
|
||||||
get_node/3, get_node/2, get_node/1, get_nodes/2,
|
get_node/3, get_node/2, get_node/1, get_nodes/2,
|
||||||
@ -76,13 +76,13 @@ get_node(Host, Node, _From) ->
|
|||||||
get_node(Host, Node) ->
|
get_node(Host, Node) ->
|
||||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||||
[Record] when is_record(Record, pubsub_node) -> Record;
|
[Record] when is_record(Record, pubsub_node) -> Record;
|
||||||
_ -> {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)}
|
_ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_node(Nidx) ->
|
get_node(Nidx) ->
|
||||||
case mnesia:index_read(pubsub_node, Nidx, #pubsub_node.id) of
|
case mnesia:index_read(pubsub_node, Nidx, #pubsub_node.id) of
|
||||||
[Record] when is_record(Record, pubsub_node) -> Record;
|
[Record] when is_record(Record, pubsub_node) -> Record;
|
||||||
_ -> {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)}
|
_ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_nodes(Host, _From) ->
|
get_nodes(Host, _From) ->
|
||||||
@ -180,10 +180,10 @@ create_node(Host, Node, Type, Owner, Options, Parents) ->
|
|||||||
options = Options}),
|
options = Options}),
|
||||||
{ok, Nidx};
|
{ok, Nidx};
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_FORBIDDEN}
|
{error, xmpp:err_forbidden()}
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERRT_CONFLICT(?MYLANG, <<"Node already exists">>)}
|
{error, xmpp:err_conflict(<<"Node already exists">>, ?MYLANG)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
delete_node(Host, Node) ->
|
delete_node(Host, Node) ->
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||||
|
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
-include("ejabberd_sql_pt.hrl").
|
-include("ejabberd_sql_pt.hrl").
|
||||||
|
|
||||||
-export([init/3, terminate/2, options/0, set_node/1,
|
-export([init/3, terminate/2, options/0, set_node/1,
|
||||||
@ -97,7 +97,7 @@ set_node(Record) when is_record(Record, pubsub_node) ->
|
|||||||
case Nidx of
|
case Nidx of
|
||||||
none ->
|
none ->
|
||||||
Txt = <<"Node index not found">>,
|
Txt = <<"Node index not found">>,
|
||||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(?MYLANG, Txt)};
|
{error, xmpp:err_internal_server_error(Txt, ?MYLANG)};
|
||||||
_ ->
|
_ ->
|
||||||
lists:foreach(fun ({Key, Value}) ->
|
lists:foreach(fun ({Key, Value}) ->
|
||||||
SKey = iolist_to_binary(atom_to_list(Key)),
|
SKey = iolist_to_binary(atom_to_list(Key)),
|
||||||
@ -125,9 +125,9 @@ get_node(Host, Node) ->
|
|||||||
{selected, [RItem]} ->
|
{selected, [RItem]} ->
|
||||||
raw_to_node(Host, RItem);
|
raw_to_node(Host, RItem);
|
||||||
{'EXIT', _Reason} ->
|
{'EXIT', _Reason} ->
|
||||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(?MYLANG, <<"Database failure">>)};
|
{error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)};
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)}
|
{error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_node(Nidx) ->
|
get_node(Nidx) ->
|
||||||
@ -139,9 +139,9 @@ get_node(Nidx) ->
|
|||||||
{selected, [{Host, Node, Parent, Type}]} ->
|
{selected, [{Host, Node, Parent, Type}]} ->
|
||||||
raw_to_node(Host, {Node, Parent, Type, Nidx});
|
raw_to_node(Host, {Node, Parent, Type, Nidx});
|
||||||
{'EXIT', _Reason} ->
|
{'EXIT', _Reason} ->
|
||||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(?MYLANG, <<"Database failure">>)};
|
{error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)};
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)}
|
{error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_nodes(Host, _From) ->
|
get_nodes(Host, _From) ->
|
||||||
@ -249,12 +249,12 @@ create_node(Host, Node, Type, Owner, Options, Parents) ->
|
|||||||
Other -> Other
|
Other -> Other
|
||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_FORBIDDEN}
|
{error, xmpp:err_forbidden()}
|
||||||
end;
|
end;
|
||||||
{result, _} ->
|
{result, _} ->
|
||||||
{error, ?ERRT_CONFLICT(?MYLANG, <<"Node already exists">>)};
|
{error, xmpp:err_conflict(<<"Node already exists">>, ?MYLANG)};
|
||||||
{error, db_fail} ->
|
{error, db_fail} ->
|
||||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(?MYLANG, <<"Database failure">>)}
|
{error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
delete_node(Host, Node) ->
|
delete_node(Host, Node) ->
|
||||||
|
@ -75,27 +75,27 @@ add_subscription(#pubsub_subscription{subid = SubId, options = Opts}) ->
|
|||||||
Opts),
|
Opts),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
subscription_opt_from_sql({<<"DELIVER">>, Value}) ->
|
subscription_opt_from_sql([<<"DELIVER">>, Value]) ->
|
||||||
{deliver, sql_to_boolean(Value)};
|
{deliver, sql_to_boolean(Value)};
|
||||||
subscription_opt_from_sql({<<"DIGEST">>, Value}) ->
|
subscription_opt_from_sql([<<"DIGEST">>, Value]) ->
|
||||||
{digest, sql_to_boolean(Value)};
|
{digest, sql_to_boolean(Value)};
|
||||||
subscription_opt_from_sql({<<"DIGEST_FREQUENCY">>, Value}) ->
|
subscription_opt_from_sql([<<"DIGEST_FREQUENCY">>, Value]) ->
|
||||||
{digest_frequency, sql_to_integer(Value)};
|
{digest_frequency, sql_to_integer(Value)};
|
||||||
subscription_opt_from_sql({<<"EXPIRE">>, Value}) ->
|
subscription_opt_from_sql([<<"EXPIRE">>, Value]) ->
|
||||||
{expire, sql_to_timestamp(Value)};
|
{expire, sql_to_timestamp(Value)};
|
||||||
subscription_opt_from_sql({<<"INCLUDE_BODY">>, Value}) ->
|
subscription_opt_from_sql([<<"INCLUDE_BODY">>, Value]) ->
|
||||||
{include_body, sql_to_boolean(Value)};
|
{include_body, sql_to_boolean(Value)};
|
||||||
%%TODO: might be > than 1 show_values value??.
|
%%TODO: might be > than 1 show_values value??.
|
||||||
%% need to use compact all in only 1 opt.
|
%% need to use compact all in only 1 opt.
|
||||||
subscription_opt_from_sql({<<"SHOW_VALUES">>, Value}) ->
|
subscription_opt_from_sql([<<"SHOW_VALUES">>, Value]) ->
|
||||||
{show_values, Value};
|
{show_values, Value};
|
||||||
subscription_opt_from_sql({<<"SUBSCRIPTION_TYPE">>, Value}) ->
|
subscription_opt_from_sql([<<"SUBSCRIPTION_TYPE">>, Value]) ->
|
||||||
{subscription_type,
|
{subscription_type,
|
||||||
case Value of
|
case Value of
|
||||||
<<"items">> -> items;
|
<<"items">> -> items;
|
||||||
<<"nodes">> -> nodes
|
<<"nodes">> -> nodes
|
||||||
end};
|
end};
|
||||||
subscription_opt_from_sql({<<"SUBSCRIPTION_DEPTH">>, Value}) ->
|
subscription_opt_from_sql([<<"SUBSCRIPTION_DEPTH">>, Value]) ->
|
||||||
{subscription_depth,
|
{subscription_depth,
|
||||||
case Value of
|
case Value of
|
||||||
<<"all">> -> all;
|
<<"all">> -> all;
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
|
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
-define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
|
-define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
|
||||||
-define(PUBSUB_DIGEST, <<"pubsub#digest">>).
|
-define(PUBSUB_DIGEST, <<"pubsub#digest">>).
|
||||||
@ -112,30 +112,15 @@ get_options_xform(Lang, Options) ->
|
|||||||
Keys = [deliver, show_values, subscription_type, subscription_depth],
|
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,
|
{result,
|
||||||
#xmlel{name = <<"x">>,
|
#xdata{type = form,
|
||||||
attrs = [{<<"xmlns">>, ?NS_XDATA}],
|
fields = [#xdata_field{type = hidden,
|
||||||
children =
|
var = <<"FORM_TYPE">>,
|
||||||
[#xmlel{name = <<"field">>,
|
values = [?NS_PUBSUB_SUB_OPTIONS]}|
|
||||||
attrs =
|
XFields]}}.
|
||||||
[{<<"var">>, <<"FORM_TYPE">>},
|
|
||||||
{<<"type">>, <<"hidden">>}],
|
|
||||||
children =
|
|
||||||
[#xmlel{name = <<"value">>, attrs = [],
|
|
||||||
children =
|
|
||||||
[{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
|
|
||||||
++ XFields}}.
|
|
||||||
|
|
||||||
parse_options_xform(XFields) ->
|
parse_options_xform(XFields) ->
|
||||||
case fxml:remove_cdata(XFields) of
|
Opts = set_xoption(XFields, []),
|
||||||
[#xmlel{name = <<"x">>} = XEl] ->
|
{result, Opts}.
|
||||||
case jlib:parse_xdata_submit(XEl) of
|
|
||||||
XData when is_list(XData) ->
|
|
||||||
Opts = set_xoption(XData, []),
|
|
||||||
{result, Opts};
|
|
||||||
Other -> Other
|
|
||||||
end;
|
|
||||||
_ -> {result, []}
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
@ -223,9 +208,17 @@ val_xfield(digest_frequency = Opt, [Val]) ->
|
|||||||
_ ->
|
_ ->
|
||||||
Txt = <<"Value of '~s' should be integer">>,
|
Txt = <<"Value of '~s' should be integer">>,
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
||||||
{error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)}
|
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}
|
||||||
|
end;
|
||||||
|
val_xfield(expire = Opt, [Val]) ->
|
||||||
|
case jlib:datetime_string_to_timestamp(Val) of
|
||||||
|
undefined ->
|
||||||
|
Txt = <<"Value of '~s' should be datetime string">>,
|
||||||
|
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
||||||
|
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)};
|
||||||
|
Timestamp ->
|
||||||
|
Timestamp
|
||||||
end;
|
end;
|
||||||
val_xfield(expire, [Val]) -> jlib:datetime_string_to_timestamp(Val);
|
|
||||||
val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val);
|
val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val);
|
||||||
val_xfield(show_values, Vals) -> Vals;
|
val_xfield(show_values, Vals) -> Vals;
|
||||||
val_xfield(subscription_type, [<<"items">>]) -> items;
|
val_xfield(subscription_type, [<<"items">>]) -> items;
|
||||||
@ -237,7 +230,7 @@ val_xfield(subscription_depth = Opt, [Depth]) ->
|
|||||||
_ ->
|
_ ->
|
||||||
Txt = <<"Value of '~s' should be integer">>,
|
Txt = <<"Value of '~s' should be integer">>,
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
||||||
{error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)}
|
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Convert XForm booleans to Erlang booleans.
|
%% Convert XForm booleans to Erlang booleans.
|
||||||
@ -248,10 +241,7 @@ xopt_to_bool(_, <<"true">>) -> true;
|
|||||||
xopt_to_bool(Option, _) ->
|
xopt_to_bool(Option, _) ->
|
||||||
Txt = <<"Value of '~s' should be boolean">>,
|
Txt = <<"Value of '~s' should be boolean">>,
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Option])),
|
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Option])),
|
||||||
{error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)}.
|
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}.
|
||||||
|
|
||||||
-spec get_option_xfield(Lang :: binary(), Key :: atom(),
|
|
||||||
Options :: mod_pubsub:subOptions()) -> xmlel().
|
|
||||||
|
|
||||||
%% Return a field for an XForm for Key, with data filled in, if
|
%% Return a field for an XForm for Key, with data filled in, if
|
||||||
%% applicable, from Options.
|
%% applicable, from Options.
|
||||||
@ -261,33 +251,22 @@ get_option_xfield(Lang, Key, Options) ->
|
|||||||
{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
|
Vals = case lists:keysearch(Key, 1, Options) of
|
||||||
{value, {_, Val}} ->
|
{value, {_, Val}} ->
|
||||||
[tr_xfield_values(Vals)
|
[xfield_val(Key, Val)];
|
||||||
|| Vals <- xfield_val(Key, Val)];
|
false ->
|
||||||
false -> []
|
[]
|
||||||
end,
|
end,
|
||||||
#xmlel{name = <<"field">>,
|
#xdata_field{type = Type, var = Var,
|
||||||
attrs =
|
label = translate:translate(Lang, Label),
|
||||||
[{<<"var">>, Var}, {<<"type">>, Type},
|
values = Vals,
|
||||||
{<<"label">>, translate:translate(Lang, Label)}],
|
options = OptEls}.
|
||||||
children = OptEls ++ Vals}.
|
|
||||||
|
|
||||||
type_and_options({Type, Options}, Lang) ->
|
type_and_options({Type, Options}, Lang) ->
|
||||||
{Type, [tr_xfield_options(O, Lang) || O <- Options]};
|
{Type, [tr_xfield_options(O, Lang) || O <- Options]};
|
||||||
type_and_options(Type, _Lang) -> {Type, []}.
|
type_and_options(Type, _Lang) -> {Type, []}.
|
||||||
|
|
||||||
tr_xfield_options({Value, Label}, Lang) ->
|
tr_xfield_options({Value, Label}, Lang) ->
|
||||||
#xmlel{name = <<"option">>,
|
#xdata_option{label = translate:translate(Lang, Label),
|
||||||
attrs =
|
value = Value}.
|
||||||
[{<<"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.
|
|
||||||
#xmlel{name = <<"value">>, attrs = [],
|
|
||||||
children = [{xmlcdata, Value}]}.
|
|
||||||
|
|
||||||
xfield_var(deliver) -> ?PUBSUB_DELIVER;
|
xfield_var(deliver) -> ?PUBSUB_DELIVER;
|
||||||
%xfield_var(digest) -> ?PUBSUB_DIGEST;
|
%xfield_var(digest) -> ?PUBSUB_DIGEST;
|
||||||
@ -298,24 +277,24 @@ xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES;
|
|||||||
xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
|
xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
|
||||||
xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
|
xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
|
||||||
|
|
||||||
xfield_type(deliver) -> <<"boolean">>;
|
xfield_type(deliver) -> boolean;
|
||||||
%xfield_type(digest) -> <<"boolean">>;
|
%xfield_type(digest) -> boolean;
|
||||||
%xfield_type(digest_frequency) -> <<"text-single">>;
|
%xfield_type(digest_frequency) -> 'text-single';
|
||||||
%xfield_type(expire) -> <<"text-single">>;
|
%xfield_type(expire) -> 'text-single';
|
||||||
%xfield_type(include_body) -> <<"boolean">>;
|
%xfield_type(include_body) -> boolean;
|
||||||
xfield_type(show_values) ->
|
xfield_type(show_values) ->
|
||||||
{<<"list-multi">>,
|
{'list-multi',
|
||||||
[{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
|
[{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
|
||||||
{<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
|
{<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
|
||||||
{<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
|
{<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
|
||||||
{<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
|
{<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
|
||||||
{<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
|
{<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
|
||||||
xfield_type(subscription_type) ->
|
xfield_type(subscription_type) ->
|
||||||
{<<"list-single">>,
|
{'list-single',
|
||||||
[{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
|
[{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
|
||||||
{<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
|
{<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
|
||||||
xfield_type(subscription_depth) ->
|
xfield_type(subscription_depth) ->
|
||||||
{<<"list-single">>,
|
{'list-single',
|
||||||
[{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
|
[{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
|
||||||
{<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
|
{<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
|
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
-define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
|
-define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
|
||||||
-define(PUBSUB_DIGEST, <<"pubsub#digest">>).
|
-define(PUBSUB_DIGEST, <<"pubsub#digest">>).
|
||||||
@ -117,30 +117,15 @@ get_options_xform(Lang, Options) ->
|
|||||||
Keys = [deliver, show_values, subscription_type, subscription_depth],
|
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,
|
{result,
|
||||||
#xmlel{name = <<"x">>,
|
#xdata{type = form,
|
||||||
attrs = [{<<"xmlns">>, ?NS_XDATA}],
|
fields = [#xdata_field{type = hidden,
|
||||||
children =
|
var = <<"FORM_TYPE">>,
|
||||||
[#xmlel{name = <<"field">>,
|
values = [?NS_PUBSUB_SUB_OPTIONS]}|
|
||||||
attrs =
|
XFields]}}.
|
||||||
[{<<"var">>, <<"FORM_TYPE">>},
|
|
||||||
{<<"type">>, <<"hidden">>}],
|
|
||||||
children =
|
|
||||||
[#xmlel{name = <<"value">>, attrs = [],
|
|
||||||
children =
|
|
||||||
[{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
|
|
||||||
++ XFields}}.
|
|
||||||
|
|
||||||
parse_options_xform(XFields) ->
|
parse_options_xform(XFields) ->
|
||||||
case fxml:remove_cdata(XFields) of
|
Opts = set_xoption(XFields, []),
|
||||||
[#xmlel{name = <<"x">>} = XEl] ->
|
{result, Opts}.
|
||||||
case jlib:parse_xdata_submit(XEl) of
|
|
||||||
XData when is_list(XData) ->
|
|
||||||
Opts = set_xoption(XData, []),
|
|
||||||
{result, Opts};
|
|
||||||
Other -> Other
|
|
||||||
end;
|
|
||||||
_ -> {result, []}
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
@ -188,9 +173,17 @@ val_xfield(digest_frequency = Opt, [Val]) ->
|
|||||||
_ ->
|
_ ->
|
||||||
Txt = <<"Value of '~s' should be integer">>,
|
Txt = <<"Value of '~s' should be integer">>,
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
||||||
{error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)}
|
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}
|
||||||
|
end;
|
||||||
|
val_xfield(expire = Opt, [Val]) ->
|
||||||
|
case jlib:datetime_string_to_timestamp(Val) of
|
||||||
|
undefined ->
|
||||||
|
Txt = <<"Value of '~s' should be datetime string">>,
|
||||||
|
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
||||||
|
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)};
|
||||||
|
Timestamp ->
|
||||||
|
Timestamp
|
||||||
end;
|
end;
|
||||||
val_xfield(expire, [Val]) -> jlib:datetime_string_to_timestamp(Val);
|
|
||||||
val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val);
|
val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val);
|
||||||
val_xfield(show_values, Vals) -> Vals;
|
val_xfield(show_values, Vals) -> Vals;
|
||||||
val_xfield(subscription_type, [<<"items">>]) -> items;
|
val_xfield(subscription_type, [<<"items">>]) -> items;
|
||||||
@ -202,7 +195,7 @@ val_xfield(subscription_depth = Opt, [Depth]) ->
|
|||||||
_ ->
|
_ ->
|
||||||
Txt = <<"Value of '~s' should be integer">>,
|
Txt = <<"Value of '~s' should be integer">>,
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
||||||
{error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)}
|
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Convert XForm booleans to Erlang booleans.
|
%% Convert XForm booleans to Erlang booleans.
|
||||||
@ -213,7 +206,7 @@ xopt_to_bool(_, <<"true">>) -> true;
|
|||||||
xopt_to_bool(Option, _) ->
|
xopt_to_bool(Option, _) ->
|
||||||
Txt = <<"Value of '~s' should be boolean">>,
|
Txt = <<"Value of '~s' should be boolean">>,
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Option])),
|
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Option])),
|
||||||
{error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)}.
|
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}.
|
||||||
|
|
||||||
%% Return a field for an XForm for Key, with data filled in, if
|
%% Return a field for an XForm for Key, with data filled in, if
|
||||||
%% applicable, from Options.
|
%% applicable, from Options.
|
||||||
@ -222,34 +215,23 @@ get_option_xfield(Lang, Key, Options) ->
|
|||||||
Label = xfield_label(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
|
Vals = case lists:keysearch(Key, 1, Options) of
|
||||||
{value, {_, Val}} ->
|
{value, {_, Val}} ->
|
||||||
[tr_xfield_values(Vals)
|
[xfield_val(Key, Val)];
|
||||||
|| Vals <- xfield_val(Key, Val)];
|
false ->
|
||||||
false -> []
|
[]
|
||||||
end,
|
end,
|
||||||
#xmlel{name = <<"field">>,
|
#xdata_field{type = Type, var = Var,
|
||||||
attrs =
|
label = translate:translate(Lang, Label),
|
||||||
[{<<"var">>, Var}, {<<"type">>, Type},
|
values = Vals,
|
||||||
{<<"label">>, translate:translate(Lang, Label)}],
|
options = OptEls}.
|
||||||
children = OptEls ++ Vals}.
|
|
||||||
|
|
||||||
type_and_options({Type, Options}, Lang) ->
|
type_and_options({Type, Options}, Lang) ->
|
||||||
{Type, [tr_xfield_options(O, Lang) || O <- Options]};
|
{Type, [tr_xfield_options(O, Lang) || O <- Options]};
|
||||||
type_and_options(Type, _Lang) -> {Type, []}.
|
type_and_options(Type, _Lang) -> {Type, []}.
|
||||||
|
|
||||||
tr_xfield_options({Value, Label}, Lang) ->
|
tr_xfield_options({Value, Label}, Lang) ->
|
||||||
#xmlel{name = <<"option">>,
|
#xdata_option{label = translate:translate(Lang, Label),
|
||||||
attrs =
|
value = Value}.
|
||||||
[{<<"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.
|
|
||||||
#xmlel{name = <<"value">>, attrs = [],
|
|
||||||
children = [{xmlcdata, Value}]}.
|
|
||||||
|
|
||||||
xfield_var(deliver) -> ?PUBSUB_DELIVER;
|
xfield_var(deliver) -> ?PUBSUB_DELIVER;
|
||||||
%xfield_var(digest) -> ?PUBSUB_DIGEST;
|
%xfield_var(digest) -> ?PUBSUB_DIGEST;
|
||||||
@ -260,26 +242,26 @@ xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES;
|
|||||||
xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
|
xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
|
||||||
xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
|
xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
|
||||||
|
|
||||||
xfield_type(deliver) -> <<"boolean">>;
|
xfield_type(deliver) -> boolean;
|
||||||
%xfield_type(digest) -> <<"boolean">>;
|
%xfield_type(digest) -> boolean;
|
||||||
%xfield_type(digest_frequency) -> <<"text-single">>;
|
%xfield_type(digest_frequency) -> 'text-single';
|
||||||
%xfield_type(expire) -> <<"text-single">>;
|
%xfield_type(expire) -> 'text-single';
|
||||||
%xfield_type(include_body) -> <<"boolean">>;
|
%xfield_type(include_body) -> boolean;
|
||||||
xfield_type(show_values) ->
|
xfield_type(show_values) ->
|
||||||
{<<"list-multi">>,
|
{'list-multi',
|
||||||
[{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
|
[{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
|
||||||
{<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
|
{<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
|
||||||
{<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
|
{<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
|
||||||
{<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
|
{<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
|
||||||
{<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
|
{<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
|
||||||
xfield_type(subscription_type) ->
|
xfield_type(subscription_type) ->
|
||||||
{<<"list-single">>,
|
{'list-single',
|
||||||
[{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
|
[{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
|
||||||
{<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
|
{<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
|
||||||
xfield_type(subscription_depth) ->
|
xfield_type(subscription_depth) ->
|
||||||
{<<"list-single">>,
|
{'list-single',
|
||||||
[{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
|
[{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
|
||||||
{<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
|
{<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
|
||||||
|
|
||||||
%% Return the XForm variable label for a subscription option key.
|
%% Return the XForm variable label for a subscription option key.
|
||||||
xfield_label(deliver) -> ?DELIVER_LABEL;
|
xfield_label(deliver) -> ?DELIVER_LABEL;
|
||||||
|
@ -1961,27 +1961,27 @@
|
|||||||
'presence-subscription-required' | 'subid-required' |
|
'presence-subscription-required' | 'subid-required' |
|
||||||
'too-many-subscriptions' | 'unsupported' |
|
'too-many-subscriptions' | 'unsupported' |
|
||||||
'unsupported-access-model'.
|
'unsupported-access-model'.
|
||||||
-type ps_error_feature() :: 'access-authorize' | 'access-open' |
|
-type ps_feature() :: 'access-authorize' | 'access-open' |
|
||||||
'access-presence' | 'access-roster' |
|
'access-presence' | 'access-roster' |
|
||||||
'access-whitelist' | 'auto-create' |
|
'access-whitelist' | 'auto-create' |
|
||||||
'auto-subscribe' | 'collections' | 'config-node' |
|
'auto-subscribe' | 'collections' | 'config-node' |
|
||||||
'create-and-configure' | 'create-nodes' |
|
'create-and-configure' | 'create-nodes' |
|
||||||
'delete-items' | 'delete-nodes' |
|
'delete-items' | 'delete-nodes' |
|
||||||
'filtered-notifications' | 'get-pending' |
|
'filtered-notifications' | 'get-pending' |
|
||||||
'instant-nodes' | 'item-ids' | 'last-published' |
|
'instant-nodes' | 'item-ids' | 'last-published' |
|
||||||
'leased-subscription' | 'manage-subscriptions' |
|
'leased-subscription' | 'manage-subscriptions' |
|
||||||
'member-affiliation' | 'meta-data' |
|
'member-affiliation' | 'meta-data' |
|
||||||
'modify-affiliations' | 'multi-collection' |
|
'modify-affiliations' | 'multi-collection' |
|
||||||
'multi-subscribe' | 'outcast-affiliation' |
|
'multi-subscribe' | 'outcast-affiliation' |
|
||||||
'persistent-items' | 'presence-notifications' |
|
'persistent-items' | 'presence-notifications' |
|
||||||
'presence-subscribe' | 'publish' |
|
'presence-subscribe' | 'publish' |
|
||||||
'publish-options' | 'publish-only-affiliation' |
|
'publish-options' | 'publish-only-affiliation' |
|
||||||
'publisher-affiliation' | 'purge-nodes' |
|
'publisher-affiliation' | 'purge-nodes' |
|
||||||
'retract-items' | 'retrieve-affiliations' |
|
'retract-items' | 'retrieve-affiliations' |
|
||||||
'retrieve-default' | 'retrieve-items' |
|
'retrieve-default' | 'retrieve-items' |
|
||||||
'retrieve-subscriptions' | 'subscribe' |
|
'retrieve-subscriptions' | 'subscribe' |
|
||||||
'subscription-options' | 'subscription-notifications'.
|
'subscription-options' | 'subscription-notifications'.
|
||||||
-record(ps_error, {type :: ps_error_type(), feature :: ps_error_feature()}).
|
-record(ps_error, {type :: ps_error_type(), feature :: ps_feature()}).
|
||||||
-type ps_error() :: #ps_error{}.
|
-type ps_error() :: #ps_error{}.
|
||||||
|
|
||||||
-xml(pubsub_error_closed_node,
|
-xml(pubsub_error_closed_node,
|
||||||
|
Loading…
Reference in New Issue
Block a user