>,
+ Fields =
+ [stringxfield(<<"Room title">>,
<<"muc#roomconfig_roomname">>,
Config#config.title, Lang),
stringxfield(<<"Room description">>,
@@ -3420,50 +3409,17 @@ get_config(Lang, StateData, From) ->
InstructionsTxt = translate:translate(
Lang, <<"You need an x:data capable client to configure room">>),
{result, [#xmlel{name = <<"instructions">>, children = [#xmlcdata{content = InstructionsTxt}]},
- #xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA},
- {<<"type">>, <<"form">>}],
- children = Res}],
+ mongoose_data_forms:form(#{title => Title, ns => ?NS_MUC_CONFIG, fields => Fields})],
StateData}.
--spec getmemberlist_field(Lang :: ejabberd:lang()) -> exml:element().
+-spec getmemberlist_field(Lang :: ejabberd:lang()) -> mongoose_data_forms:field().
getmemberlist_field(Lang) ->
LabelTxt = translate:translate(
Lang, <<"Roles and affiliations that may retrieve member list">>),
- OptModerator = #xmlel{name = <<"option">>,
- attrs = [{<<"label">>, translate:translate(Lang, <<"moderator">>)}],
- children = [
- #xmlel{name = <<"value">>,
- children = [#xmlcdata{content = <<"moderator">>}]}
- ]},
- OptParticipant = #xmlel{name = <<"option">>,
- attrs = [{<<"label">>, translate:translate(Lang, <<"participant">>)}],
- children = [
- #xmlel{name = <<"value">>,
- children = [#xmlcdata{content = <<"participant">>}]}
- ]},
- OptVisitor = #xmlel{name = <<"option">>,
- attrs = [{<<"label">>, translate:translate(Lang, <<"visitor">>)}],
- children = [
- #xmlel{name = <<"value">>,
- children = [#xmlcdata{content = <<"visitor">>}]}
- ]},
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"list-multi">>},
- {<<"label">>, LabelTxt},
- {<<"var">>, <<"muc#roomconfig_getmemberlist">>}],
- children = [
- #xmlel{name = <<"value">>,
- children = [#xmlcdata{content = <<"moderator">>}]},
- #xmlel{name = <<"value">>,
- children = [#xmlcdata{content = <<"participant">>}]},
- #xmlel{name = <<"value">>,
- children = [#xmlcdata{content = <<"visitor">>}]},
- OptModerator,
- OptParticipant,
- OptVisitor
- ]
- }.
+ Values = [<<"moderator">>, <<"participant">>, <<"visitor">>],
+ Options = [{translate:translate(Lang, Opt), Opt} || Opt <- Values],
+ #{type => <<"list-multi">>, label => LabelTxt,
+ var => <<"muc#roomconfig_getmemberlist">>, values => Values, options => Options}.
maxusers_field(Lang, StateData) ->
ServiceMaxUsers = get_service_max_users(StateData),
@@ -3474,76 +3430,45 @@ maxusers_field(Lang, StateData) ->
{N, integer_to_binary(N)};
_ -> {0, <<"none">>}
end,
-
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"list-single">>},
- {<<"label">>, translate:translate(Lang, <<"Maximum Number of Occupants">>)},
- {<<"var">>, <<"muc#roomconfig_maxusers">>}],
- children = [#xmlel{name = <<"value">>,
- children = [#xmlcdata{content = MaxUsersRoomString}]}] ++
- if
- is_integer(ServiceMaxUsers) -> [];
- true ->
- [#xmlel{name = <<"option">>,
- attrs = [{<<"label">>, translate:translate(Lang, <<"No limit">>)}],
- children = [#xmlel{name = <<"value">>,
- children = [#xmlcdata{content = <<"none">>}]}]}]
- end ++
- [#xmlel{name = <<"option">>,
- attrs = [{<<"label">>, integer_to_binary(N)}],
- children = [#xmlel{name = <<"value">>,
- children = [#xmlcdata{content = integer_to_binary(N)}]}]} ||
+ LabelTxt = translate:translate(Lang, <<"Maximum Number of Occupants">>),
+ Options = if
+ is_integer(ServiceMaxUsers) -> [];
+ true -> {translate:translate(Lang, <<"No limit">>), <<"none">>}
+ end ++
+ [integer_to_binary(N) ||
N <- lists:usort([ServiceMaxUsers, DefaultRoomMaxUsers, MaxUsersRoomInteger |
- ?MAX_USERS_DEFAULT_LIST]), N =< ServiceMaxUsers]}.
+ ?MAX_USERS_DEFAULT_LIST]), N =< ServiceMaxUsers],
+ #{type => <<"list-single">>, label => LabelTxt,
+ var => <<"muc#roomconfig_maxusers">>, values => [MaxUsersRoomString], options => Options}.
--spec whois_field(Lang :: ejabberd:lang(), Config :: config()) -> exml:element().
+-spec whois_field(Lang :: ejabberd:lang(), Config :: config()) -> mongoose_data_forms:field().
whois_field(Lang, Config) ->
- OptModerators = #xmlel{name = <<"option">>,
- attrs = [{<<"label">>,
- translate:translate(Lang, <<"moderators only">>)}],
- children = [#xmlel{name = <<"value">>,
- children = [#xmlcdata{content = <<"moderators">>}]}]},
- OptAnyone = #xmlel{name = <<"option">>,
- attrs = [{<<"label">>, translate:translate(Lang, <<"anyone">>)}],
- children = [#xmlel{name = <<"value">>,
- children = [#xmlcdata{content = <<"anyone">>}]}]},
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"list-single">>},
- {<<"label">>, translate:translate(Lang, <<"Present real Jabber IDs to">>)},
- {<<"var">>, <<"muc#roomconfig_whois">>}],
- children = [#xmlel{name = <<"value">>,
- children = [#xmlcdata{content = if Config#config.anonymous ->
- <<"moderators">>;
- true ->
- <<"anyone">>
- end}]},
- OptModerators,
- OptAnyone]}.
-
--spec set_config(exml:element(), state()) -> any().
-set_config(XEl, StateData) ->
- XData = jlib:parse_xdata_submit(XEl),
- case XData of
- invalid ->
- {error, mongoose_xmpp_errors:bad_request()};
- _ ->
- case set_xoption(XData, StateData#state.config) of
- #config{} = Config ->
- Res = change_config(Config, StateData),
- {result, _, NSD} = Res,
- PrevLogging = (StateData#state.config)#config.logging,
- NewLogging = Config#config.logging,
- PrevAnon = (StateData#state.config)#config.anonymous,
- NewAnon = Config#config.anonymous,
- Type = notify_config_change_and_get_type(PrevLogging, NewLogging,
- PrevAnon, NewAnon, StateData),
+ Value = if Config#config.anonymous -> <<"moderators">>;
+ true -> <<"anyone">>
+ end,
+ Options = [{translate:translate(Lang, <<"moderators only">>), <<"moderators">>},
+ {translate:translate(Lang, <<"anyone">>), <<"anyone">>}],
+ #{type => <<"list-single">>, label => translate:translate(Lang, <<"moderators only">>),
+ var => <<"muc#roomconfig_whois">>, values => [Value], options => Options}.
+
+-spec set_config([{binary(), [binary()]}], state()) -> any().
+set_config(XData, StateData) ->
+ case set_xoption(XData, StateData#state.config) of
+ #config{} = Config ->
+ Res = change_config(Config, StateData),
+ {result, _, NSD} = Res,
+ PrevLogging = (StateData#state.config)#config.logging,
+ NewLogging = Config#config.logging,
+ PrevAnon = (StateData#state.config)#config.anonymous,
+ NewAnon = Config#config.anonymous,
+ Type = notify_config_change_and_get_type(PrevLogging, NewLogging,
+ PrevAnon, NewAnon, StateData),
Users = [{U#user.jid, U#user.nick, U#user.role} ||
- {_, U} <- maps:to_list(StateData#state.users)],
- add_to_log(Type, Users, NSD),
- Res;
+ {_, U} <- maps:to_list(StateData#state.users)],
+ add_to_log(Type, Users, NSD),
+ Res;
Err ->
- Err
- end
+ Err
end.
-spec notify_config_change_and_get_type(PrevLogging :: boolean(), NewLogging :: boolean(),
@@ -3651,9 +3576,6 @@ set_xoption([{<<"muc#roomconfig_getmemberlist">>, Val} | Opts], Config) ->
end;
set_xoption([{<<"muc#roomconfig_enablelogging">>, [Val]} | Opts], Config) ->
?SET_BOOL_XOPT(logging, Val);
-set_xoption([{<<"FORM_TYPE">>, _} | Opts], Config) ->
- %% Ignore our FORM_TYPE
- set_xoption(Opts, Config);
set_xoption([_ | _Opts], _Config) ->
{error, mongoose_xmpp_errors:bad_request()}.
@@ -3986,42 +3908,32 @@ disco_item(User=#user{nick=Nick}, RoomJID) ->
| {role, BRole :: binary(), RoomNick :: mod_muc:nick()}
| {error, any()}
| ok.
-check_voice_approval(From, [#xmlel{name = <<"x">>,
- children = Items}], _Lang, StateData) ->
- BRole = get_field(<<"muc#role">>, Items),
- case Items of
- [_Form, _Role] ->
- case catch binary_to_role(BRole) of
- {'EXIT', _} -> {error, mongoose_xmpp_errors:bad_request()};
- _ -> {form, BRole}
+check_voice_approval(From, [XEl], Lang, StateData) ->
+ case mongoose_data_forms:parse_form(XEl) of
+ #{kvs := #{<<"muc#role">> := [BRole]} = KVs} ->
+ case {get_role(From, StateData) =:= moderator,
+ maps:find(<<"muc#request_allow">>, KVs),
+ maps:find(<<"muc#roomnick">>, KVs)} of
+ {_, error, error} ->
+ case catch binary_to_role(BRole) of
+ {'EXIT', _} -> {error, mongoose_xmpp_errors:bad_request()};
+ _ -> {form, BRole}
+ end;
+ {false, _, _} ->
+ {error, mongoose_xmpp_errors:not_allowed()};
+ {true, {ok, [<<"true">>]}, error} ->
+ {error, mongoose_xmpp_errors:bad_request()};
+ {true, {ok, [<<"true">>]}, {ok, [RoomNick]}} ->
+ {role, BRole, RoomNick};
+ {true, _, _} ->
+ ok
end;
+ {error, Msg} ->
+ {error, mongoose_xmpp_errors:bad_request(Lang, Msg)};
_ ->
- case {get_role(From, StateData),
- get_field(<<"muc#request_allow">>, Items),
- get_field(<<"muc#roomnick">>, Items)} of
- {moderator, <<"true">>, false} -> {error, mongoose_xmpp_errors:bad_request()};
- {moderator, <<"true">>, RoomNick} -> {role, BRole, RoomNick};
- {moderator, _, _} -> ok;
- _ -> {error, mongoose_xmpp_errors:not_allowed()}
- end
+ {error, mongoose_xmpp_errors:bad_request(Lang, <<"MUC Role was not provided">>)}
end.
-
--spec get_field(binary(), [jlib:xmlcdata() | exml:element()]) -> any().
-get_field(Var, [#xmlel{name = <<"field">>, attrs = Attrs} = Item|Items])
- when is_binary(Var) ->
- case xml:get_attr(<<"var">>, Attrs) of
- {value, Var} ->
- case xml:get_path_s(Item, [{elem, <<"value">>}, cdata]) of
- <<>> -> get_field(Var, Items);
- Value -> Value
- end;
- _ ->
- get_field(Var, Items)
- end;
-get_field(_Var, []) ->
- false.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Invitation support
@@ -4421,7 +4333,7 @@ route_voice_approval({error, ErrType}, From, Packet, _Lang, StateData) ->
StateData;
route_voice_approval({form, RoleName}, From, _Packet, _Lang, StateData) ->
{Nick, _} = get_participant_data(From, StateData),
- ApprovalForm = jlib:make_voice_approval_form(From, Nick, RoleName),
+ ApprovalForm = make_voice_approval_form(From, Nick, RoleName),
F = fun({_, Info}) ->
ejabberd_router:route(StateData#state.jid, Info#user.jid,
ApprovalForm)
@@ -4636,18 +4548,30 @@ route_nick_iq(#routed_nick_iq{packet = Packet, lang = Lang, nick = ToNick,
decode_reason(Elem) ->
xml:get_path_s(Elem, [{elem, <<"reason">>}, cdata]).
-
--spec xfield(binary(), any(), binary(), binary(), ejabberd:lang()) -> exml:element().
+-spec make_voice_approval_form(From :: jid:simple_jid() | jid:jid(),
+ Nick :: binary(), Role :: binary()) -> exml:element().
+make_voice_approval_form(From, Nick, Role) ->
+ Title = <<"Voice request">>,
+ Instructions = <<"To approve this request"
+ " for voice, select the "Grant voice to this person?" checkbox"
+ " and click OK. To skip this request, click the cancel button.">>,
+ Fields = [#{var => <<"muc#role">>, type => <<"text-single">>,
+ label => <<"Request role">>, values => [Role]},
+ #{var => <<"muc#jid">>, type => <<"jid-single">>,
+ label => <<"User ID">>, values => [jid:to_binary(From)]},
+ #{var => <<"muc#roomnick">>, type => <<"text-single">>,
+ label => <<"Room Nickname">>, values => [Nick]},
+ #{var => <<"muc#request_allow">>, type => <<"boolean">>,
+ label => <<"Grant voice to this person?">>, values => [<<"false">>]}],
+ Form = mongoose_data_forms:form(#{title => Title, instructions => Instructions,
+ ns => ?NS_MUC_REQUEST, fields => Fields}),
+ #xmlel{name = <<"message">>, children = [Form]}.
+
+-spec xfield(binary(), any(), binary(), binary(), ejabberd:lang()) -> mongoose_data_forms:field().
xfield(Type, Label, Var, Val, Lang) ->
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, Type},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children = [#xmlel{name = <<"value">>,
- children = [#xmlcdata{content = Val}]}]}.
-
+ #{type => Type, label => translate:translate(Lang, Label), var => Var, values => [Val]}.
--spec boolxfield(any(), binary(), any(), ejabberd:lang()) -> exml:element().
+-spec boolxfield(any(), binary(), any(), ejabberd:lang()) -> mongoose_data_forms:field().
boolxfield(Label, Var, Val, Lang) ->
xfield(<<"boolean">>, Label, Var,
case Val of
diff --git a/src/mongoose_disco.erl b/src/mongoose_disco.erl
index 00516289ab..3884873e8b 100644
--- a/src/mongoose_disco.erl
+++ b/src/mongoose_disco.erl
@@ -217,26 +217,4 @@ identity_to_xml(Identity) ->
-spec info_to_xml(info()) -> exml:element().
info_to_xml(#{xmlns := NS, fields := Fields}) ->
- #xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"result">>}],
- children = [form_type_field_xml(NS) |
- [info_field_to_xml(Field) || Field <- Fields]]}.
-
--spec info_field_to_xml(info_field()) -> exml:element().
-info_field_to_xml(InfoField) ->
- {Values, Attrs} = maps:take(values, InfoField),
- #xmlel{name = <<"field">>,
- attrs = lists:map(fun({Key, Value}) -> {atom_to_binary(Key, utf8), Value} end,
- maps:to_list(Attrs)),
- children = values_to_xml(Values)}.
-
--spec values_to_xml([binary()]) -> [exml:element()].
-values_to_xml(Values) ->
- [#xmlel{name = <<"value">>, children = [#xmlcdata{content = Value}]} || Value <- Values].
-
--spec form_type_field_xml(binary()) -> exml:element().
-form_type_field_xml(NS) ->
- #xmlel{name = <<"field">>,
- attrs = [{<<"var">>, <<"FORM_TYPE">>}, {<<"type">>, <<"hidden">>}],
- children = [#xmlel{name = <<"value">>,
- children = [#xmlcdata{content = NS}]}]}.
+ mongoose_data_forms:form(#{type => <<"result">>, ns => NS, fields => Fields}).
diff --git a/src/muc_light/mod_muc_light_codec_legacy.erl b/src/muc_light/mod_muc_light_codec_legacy.erl
index 156f363f02..d60b6dc827 100644
--- a/src/muc_light/mod_muc_light_codec_legacy.erl
+++ b/src/muc_light/mod_muc_light_codec_legacy.erl
@@ -138,14 +138,12 @@ decode_iq(_From, #iq{ xmlns = ?NS_MUC_OWNER, type = get, sub_el = _QueryEl, id =
decode_iq(From, IQ = #iq{ xmlns = ?NS_MUC_OWNER, type = set, sub_el = QueryEl, id = ID }) ->
case exml_query:subelement(QueryEl, <<"destroy">>) of
undefined ->
- try parse_config(exml_query:paths(QueryEl, [{element, <<"x">>},
- {element, <<"field">>}])) of
+ case parse_config_form(QueryEl) of
{ok, RawConfig} ->
- {ok, {set, #config{ id = ID, raw_config = RawConfig }}}
- catch Class:Reason:Stacktrace ->
+ {ok, {set, #config{ id = ID, raw_config = RawConfig }}};
+ {error, Reason} ->
?LOG_WARNING(#{what => muc_parse_config_failed,
- from_jid => jid:to_binary(From), iq => IQ,
- class => Class, reason => Reason, stacktrace => Stacktrace}),
+ from_jid => jid:to_binary(From), iq => IQ, reason => Reason}),
{error, bad_request}
end;
_ ->
@@ -191,19 +189,12 @@ decode_iq(_From, #iq{} = IQ) ->
%% ------------------ Parsers ------------------
--spec parse_config(Els :: [jlib:xmlch()]) -> {ok, mod_muc_light_room_config:binary_kv()}.
-parse_config(Els) ->
- parse_config(Els, []).
-
--spec parse_config(Els :: [jlib:xmlch()], ConfigAcc :: mod_muc_light_room_config:binary_kv()) ->
- {ok, mod_muc_light_room_config:binary_kv()}.
-parse_config([], ConfigAcc) ->
- {ok, ConfigAcc};
-parse_config([Field | REls], ConfigAcc) ->
- case {exml_query:attr(Field, <<"var">>),
- exml_query:path(Field, [{element, <<"value">>}, cdata])} of
- {<<"FORM_TYPE">>, _} -> parse_config(REls, ConfigAcc);
- ConfigKV -> parse_config(REls, [ConfigKV | ConfigAcc])
+parse_config_form(QueryEl) ->
+ case mongoose_data_forms:find_and_parse_form(QueryEl) of
+ #{kvs := KVs} ->
+ {ok, [{K, V} || {K, [V]} <- maps:to_list(KVs)]};
+ {error, Msg} ->
+ {error, Msg}
end.
-spec parse_aff_users(Els :: [jlib:xmlch()]) -> {ok, aff_users()}.
@@ -267,15 +258,11 @@ encode_meta({get, #disco_items{ rooms = Rooms, id = ID, rsm = RSMOut }},
|| {{RoomU, RoomS}, RoomName, _RoomVersion} <- Rooms ],
{iq_reply, ?NS_DISCO_ITEMS, jlib:rsm_encode(RSMOut) ++ DiscoEls, ID};
encode_meta({get, #config{} = Config}, _RoomJID, _SenderJID, _HandleFun, _Acc) ->
- ConfigEls = [ jlib:form_field({K, <<"text-single">>, V, K})
- || {K, V} <- Config#config.raw_config ],
- XEl = #xmlel{ name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
- children = [
- kv_to_el(<<"title">>, <<"Configuration form for the room">>),
- jlib:form_field({<<"FORM_TYPE">>, <<"hidden">>,
- <<"http://jabber.org/protocol/muc#roomconfig">>})
- | ConfigEls] },
+ Fields = [#{var => K, type => <<"text-single">>, values => [V]}
+ || {K, V} <- Config#config.raw_config],
+ XEl = mongoose_data_forms:form(#{title => <<"Configuration form for the room">>,
+ ns => <<"http://jabber.org/protocol/muc#roomconfig">>,
+ fields => Fields}),
{iq_reply, ?NS_MUC_OWNER, [XEl], Config#config.id};
encode_meta({get, #affiliations{} = Affs}, _RoomJID, _SenderJID, _HandleFun, _Acc) ->
AffEls = [ aff_user_to_item(AffUser) || AffUser <- Affs#affiliations.aff_users ],
@@ -386,10 +373,6 @@ blocking_to_el({What, Action, {WhoU, WhoS}}, Service) ->
{<<"order">>, <<"1">>}
] }.
--spec kv_to_el(binary(), binary()) -> exml:element().
-kv_to_el(Key, Value) ->
- #xmlel{ name = Key, children = [#xmlcdata{ content = Value }] }.
-
-spec envelope(XMLNS :: binary(), Children :: [jlib:xmlch()]) -> [jlib:xmlch()].
envelope(XMLNS, Children) ->
[ #xmlel{ name = <<"x">>, attrs = [{<<"xmlns">>, XMLNS}], children = Children } ].
diff --git a/src/pubsub/mod_pubsub.erl b/src/pubsub/mod_pubsub.erl
index f2b2c83739..11ce6d3f57 100644
--- a/src/pubsub/mod_pubsub.erl
+++ b/src/pubsub/mod_pubsub.erl
@@ -788,10 +788,12 @@ handle_pep_authorization_response(_, <<"error">>, From, To, Acc, Packet) ->
handle_pep_authorization_response(<<"message">>, _, From, To, Acc, Packet)
when From#jid.luser == To#jid.luser, From#jid.lserver == To#jid.lserver ->
case find_authorization_response(Packet) of
- none -> {From, To, Acc, Packet};
- invalid -> {From, To, Acc, Packet};
- XFields ->
- handle_authorization_response(Acc, jid:to_lower(To), From, To, Packet, XFields),
+ none ->
+ {From, To, Acc, Packet};
+ invalid ->
+ {From, To, Acc, Packet};
+ KVs ->
+ handle_authorization_response(Acc, jid:to_lower(To), From, To, Packet, KVs),
drop
end;
handle_pep_authorization_response(_, _, From, To, Acc, Packet) ->
@@ -1464,8 +1466,10 @@ iq_pubsub_set_retract(Host, Node, From,
iq_pubsub_set_subscribe(Host, Node, From, #{query_el := QueryEl,
action_el := #xmlel{attrs = SubscribeAttrs}}) ->
- ConfigXForm = exml_query:path(QueryEl, [{element, <<"options">>},
- {element_with_ns, <<"x">>, ?NS_XDATA}]),
+ ConfigXForm = case exml_query:subelement(QueryEl, <<"options">>) of
+ undefined -> undefined;
+ Options -> mongoose_data_forms:find_form(Options)
+ end,
JID = xml:get_attr_s(<<"jid">>, SubscribeAttrs),
subscribe_node(Host, Node, From, JID, ConfigXForm).
@@ -1504,7 +1508,7 @@ iq_pubsub_get_options(Host, Node, Lang, #{action_el := #xmlel{attrs = GetOptions
get_options(Host, Node, JID, SubId, Lang).
iq_pubsub_set_options(Host, Node, #{action_el := #xmlel{attrs = SetOptionsAttrs} = ActionEl}) ->
- XForm = exml_query:subelement_with_name_and_ns(ActionEl, <<"x">>, ?NS_XDATA),
+ XForm = mongoose_data_forms:find_form(ActionEl),
SubId = xml:get_attr_s(<<"subid">>, SetOptionsAttrs),
JID = xml:get_attr_s(<<"jid">>, SetOptionsAttrs),
set_options(Host, Node, JID, SubId, XForm).
@@ -1616,19 +1620,11 @@ send_pending_node_form(Request, Host, Owner, Plugins) ->
[] ->
{error, mongoose_xmpp_errors:feature_not_implemented()};
Ps ->
- XOpts = [#xmlel{name = <<"option">>, attrs = [],
- children = [#xmlel{name = <<"value">>,
- attrs = [],
- children = [{xmlcdata, Node}]}]}
- || Node <- get_pending_nodes(Host, Owner, Ps)],
- XForm = #xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA},
- {<<"type">>, <<"form">>}],
- children = [#xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"list-single">>},
- {<<"var">>, <<"pubsub#node">>}],
- children = lists:usort(XOpts)}]},
- adhoc:produce_response(Request, executing, <<"execute">>, [XForm])
+ Options = get_pending_nodes(Host, Owner, Ps),
+ Field = #{type => <<"list-single">>, var => <<"pubsub#node">>,
+ options => lists:usort(Options)},
+ Form = mongoose_data_forms:form(#{fields => [Field]}),
+ adhoc:produce_response(Request, executing, <<"execute">>, [Form])
end.
get_pending_nodes(Host, Owner, Plugins) ->
@@ -1650,19 +1646,18 @@ get_pending_nodes(Host, Owner, Plugins) ->
Err -> Err
end.
-adhoc_get_pending_parse_options(Host, #xmlel{name = <<"x">>} = XEl) ->
- case jlib:parse_xdata_submit(XEl) of
- invalid ->
- {error, mongoose_xmpp_errors:bad_request()};
- XData2 ->
- case set_xoption(Host, XData2, []) of
+adhoc_get_pending_parse_options(Host, XEl) ->
+ case mongoose_data_forms:parse_form(XEl) of
+ #{type := <<"submit">>, kvs := KVs} ->
+ case set_xoption(Host, maps:to_list(KVs), []) of
NewOpts when is_list(NewOpts) -> {result, NewOpts};
Err -> Err
- end
- end;
-adhoc_get_pending_parse_options(_Host, XData) ->
- ?LOG_INFO(#{what => pubsub_bad_xform, exml_packet => XData}),
- {error, mongoose_xmpp_errors:bad_request()}.
+ end;
+ {error, Msg} ->
+ {error, mongoose_xmpp_errors:bad_request(<<"en">>, Msg)};
+ _ ->
+ {error, mongoose_xmpp_errors:bad_request(<<"en">>, <<"Invalid form type">>)}
+ end.
%% @doc Send a subscription approval form to Owner for all pending
%% subscriptions on Host and Node.
@@ -1700,88 +1695,42 @@ get_node_subscriptions_transaction(Owner, #pubsub_node{id = Nidx, type = Type})
send_authorization_request(#pubsub_node{nodeid = {Host, Node}, owners = Owners},
Subscriber) ->
Lang = <<"en">>,
- FormChildren = [#xmlel{name = <<"title">>, attrs = [],
- children =
- [#xmlcdata{content =
- translate:translate(Lang, <<"PubSub subscriber request">>)}]},
- #xmlel{name = <<"instructions">>,
- attrs = [],
- children =
- [#xmlcdata{content = translate:translate(
- Lang, <<"Choose whether to approve this entity's "
- "subscription.">>)}]},
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"var">>, <<"FORM_TYPE">>},
- {<<"type">>, <<"hidden">>}],
- children =
- [#xmlel{name = <<"value">>,
- attrs = [],
- children = [#xmlcdata{content = ?NS_PUBSUB_SUB_AUTH}]}]},
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"var">>, <<"pubsub#node">>},
- {<<"type">>,
- <<"text-single">>},
- {<<"label">>, translate:translate(Lang, <<"Node ID">>)}],
- children = [#xmlel{name = <<"value">>,
- attrs = [],
- children = [#xmlcdata{content = Node}]}]},
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"var">>,
- <<"pubsub#subscriber_jid">>},
- {<<"type">>, <<"jid-single">>},
- {<<"label">>,
- translate:translate(Lang, <<"Subscriber Address">>)}],
- children =
- [#xmlel{name = <<"value">>,
- attrs = [],
- children = [#xmlcdata{content = jid:to_binary(Subscriber)}]}]},
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"var">>,
- <<"pubsub#allow">>},
- {<<"type">>, <<"boolean">>},
- {<<"label">>,
- translate:translate(Lang,
- <<"Allow this Jabber ID to subscribe to "
- "this pubsub node?">>)}],
- children = [#xmlel{name = <<"value">>,
- attrs = [],
- children = [#xmlcdata{content = <<"false">>}]}]}],
+ Title = translate:translate(Lang, <<"PubSub subscriber request">>),
+ Instructions = translate:translate(Lang, <<"Choose whether to approve this entity's "
+ "subscription.">>),
+ Fields = [#{var => <<"pubsub#node">>,
+ type => <<"text-single">>,
+ label => translate:translate(Lang, <<"Node ID">>),
+ values => [Node]},
+ #{var => <<"pubsub#subscriber_jid">>,
+ type => <<"jid-single">>,
+ label => translate:translate(Lang, <<"Subscriber Address">>),
+ values => [jid:to_binary(Subscriber)]},
+ #{var => <<"pubsub#allow">>,
+ type => <<"boolean">>,
+ label => translate:translate(Lang, <<"Allow this Jabber ID to subscribe to "
+ "this pubsub node?">>),
+ values => [<<"false">>]}],
+ Form = mongoose_data_forms:form(#{title => Title, instructions => Instructions,
+ ns => ?NS_PUBSUB_SUB_AUTH, fields => Fields}),
Stanza = #xmlel{name = <<"message">>,
attrs = [{<<"id">>, mongoose_bin:gen_from_crypto()}],
- children = [#xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
- children = FormChildren}]},
+ children = [Form]},
lists:foreach(fun(Owner) ->
ejabberd_router:route(service_jid(Host), jid:make(Owner), Stanza)
end, Owners).
-find_authorization_response(#xmlel{ children = Els }) ->
- XData = lists:foldl(fun(#xmlel{name = <<"x">>, attrs = XAttrs} = XEl, Acc) ->
- case {xml:get_attr_s(<<"xmlns">>, XAttrs),
- xml:get_attr_s(<<"type">>, XAttrs)} of
- {?NS_XDATA, <<"submit">>} ->
- [jlib:parse_xdata_submit(XEl) | Acc];
- _ ->
- Acc
- end;
- (_, Acc) ->
- Acc
- end, [], xml:remove_cdata(Els)),
- case XData of
- [] ->
+find_authorization_response(El) ->
+ case mongoose_data_forms:find_form(El) of
+ undefined ->
none;
- [XFields] when is_list(XFields) ->
- ?LOG_DEBUG(#{what => pubsub_xfields, xfields => XFields}),
- case lists:keysearch(<<"FORM_TYPE">>, 1, XFields) of
- {value, {_, [?NS_PUBSUB_SUB_AUTH]}} -> XFields;
- _ -> invalid
- end;
- _ ->
- invalid
+ Form ->
+ case mongoose_data_forms:parse_form_fields(Form) of
+ #{type := <<"submit">>, ns := ?NS_PUBSUB_SUB_AUTH, kvs := KVs} ->
+ KVs;
+ _ ->
+ invalid
+ end
end.
%% @doc Send a message to JID with the supplied Subscription
@@ -1800,12 +1749,10 @@ send_authorization_approval(Host, JID, SNode, Subscription) ->
ejabberd_router:route(service_jid(Host), JID, Stanza).
handle_authorization_response(Acc, Host, From, To, Packet, XFields) ->
- case {lists:keysearch(<<"pubsub#node">>, 1, XFields),
- lists:keysearch(<<"pubsub#subscriber_jid">>, 1, XFields),
- lists:keysearch(<<"pubsub#allow">>, 1, XFields)} of
- {{value, {_, [Node]}},
- {value, {_, [SSubscriber]}},
- {value, {_, [SAllow]}}} ->
+ case XFields of
+ #{<<"pubsub#node">> := [Node],
+ <<"pubsub#subscriber_jid">> := [SSubscriber],
+ <<"pubsub#allow">> := [SAllow]} ->
FromLJID = jid:to_lower(jid:to_bare(From)),
Subscriber = jid:from_binary(SSubscriber),
Allow = string_allow_to_boolean(SAllow),
@@ -1863,12 +1810,10 @@ update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) ->
end.
-define(XFIELD(Type, Label, Var, Val),
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, Type},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children = [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Val}]}]}).
+ #{type => Type,
+ label => translate:translate(Lang, Label),
+ var => Var,
+ values => [Val]}).
-define(BOOLXFIELD(Label, Var, Val),
?XFIELD(<<"boolean">>, Label, Var,
@@ -1881,45 +1826,27 @@ update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) ->
?XFIELD(<<"text-single">>, Label, Var, Val)).
-define(STRINGMXFIELD(Label, Var, Vals),
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"text-multi">>},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children = [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, V}]}
- || V <- Vals]}).
+ #{type => <<"text-multi">>,
+ label => translate:translate(Lang, Label),
+ var => Var,
+ values => Vals}).
-define(XFIELDOPT(Type, Label, Var, Val, Opts),
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, Type},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children = [#xmlel{name = <<"option">>, attrs = [],
- children = [#xmlel{name = <<"value">>,
- attrs = [],
- children = [{xmlcdata, Opt}]}]}
- || Opt <- Opts]
- ++
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Val}]}]}).
+ #{type => Type,
+ label => translate:translate(Lang, Label),
+ var => Var,
+ options => Opts,
+ values => [Val]}).
-define(LISTXFIELD(Label, Var, Val, Opts),
?XFIELDOPT(<<"list-single">>, Label, Var, Val, Opts)).
-define(LISTMXFIELD(Label, Var, Vals, Opts),
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"list-multi">>},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children = [#xmlel{name = <<"option">>, attrs = [],
- children = [#xmlel{name = <<"value">>,
- attrs = [],
- children = [{xmlcdata, Opt}]}]}
- || Opt <- Opts]
- ++
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Val}]}
- || Val <- Vals]}).
+ #{type => <<"list-multi">>,
+ label => translate:translate(Lang, Label),
+ var => Var,
+ options => Opts,
+ values => Vals}).
%% @doc Create new pubsub nodes
%%In addition to method-specific error conditions, there are several general reasons
@@ -1973,17 +1900,7 @@ create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
end;
create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
Type = select_type(ServerHost, Host, Node, GivenType),
- ConfigXEl = case xml:remove_cdata(Configuration) of
- [] ->
- {result, node_options(Host, Type)};
- [#xmlel{name = <<"x">>} = XEl] ->
- XEl;
- _ ->
- ?LOG_INFO(#{what => pubsub_bad_node_configuration,
- pubsub_node => Node, configuration => Configuration}),
- {error, mongoose_xmpp_errors:bad_request()}
- end,
- case parse_create_node_options_if_possible(Host, Type, ConfigXEl) of
+ case parse_create_node_options(Host, Type, xml:remove_cdata(Configuration)) of
{result, NodeOptions} ->
CreateNode = fun () ->
create_node_transaction(Host, ServerHost, Node, Owner,
@@ -2007,21 +1924,27 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
Error
end;
Error ->
+ ?LOG_INFO(#{what => pubsub_bad_node_configuration,
+ pubsub_node => Node, configuration => Configuration}),
Error
end.
-parse_create_node_options_if_possible(Host, Type, #xmlel{} = ConfigXEl) ->
- case jlib:parse_xdata_submit(ConfigXEl) of
- invalid ->
- {error, mongoose_xmpp_errors:bad_request()};
- XData ->
- case set_xoption(Host, XData, node_options(Host, Type)) of
+parse_create_node_options(Host, Type, []) ->
+ {result, node_options(Host, Type)};
+parse_create_node_options(Host, Type, [XEl]) ->
+ case mongoose_data_forms:parse_form(XEl) of
+ #{type := <<"submit">>, kvs := KVs} ->
+ case set_xoption(Host, maps:to_list(KVs), node_options(Host, Type)) of
NewOpts when is_list(NewOpts) -> {result, NewOpts};
Err -> Err
- end
+ end;
+ {error, Msg} ->
+ {error, mongoose_xmpp_errors:bad_request(<<"en">>, Msg)};
+ _ ->
+ {error, mongoose_xmpp_errors:bad_request(<<"en">>, <<"Invalid form type">>)}
end;
-parse_create_node_options_if_possible(_Host, _Type, InvalidConfigXEl) ->
- InvalidConfigXEl.
+parse_create_node_options(_Host, _Type, _) ->
+ {error, mongoose_xmpp_errors:bad_request()}.
create_node_transaction(Host, ServerHost, Node, Owner, Type, Access, NodeOptions) ->
Parent = get_parent(Type, Node),
@@ -3534,11 +3457,10 @@ broadcast_config_notification(Host, Node, Nidx, Type, NodeOptions, Lang) ->
{result, false}
end.
-payload_by_option(Type, NodeOptions, Lang) ->
+payload_by_option(_Type, NodeOptions, Lang) ->
case get_option(NodeOptions, deliver_payloads) of
true ->
- [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"result">>}],
- children = get_configure_xfields(Type, NodeOptions, Lang, [])}];
+ [configure_form(<<"result">>, NodeOptions, Lang, [])];
false ->
[]
end.
@@ -3730,10 +3652,7 @@ get_configure_transaction(ServerHost, Node, From, Lang,
case node_call(Type, get_affiliation, [Nidx, From]) of
{result, owner} ->
Groups = mongoose_hooks:roster_groups(ServerHost),
- XEl = #xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA},
- {<<"type">>, <<"form">>}],
- children = get_configure_xfields(Type, Options, Lang, Groups)},
+ XEl = configure_form(<<"form">>, Options, Lang, Groups),
ConfigureEl = #xmlel{name = <<"configure">>,
attrs = node_attr(Node),
children = [XEl]},
@@ -3748,12 +3667,8 @@ get_configure_transaction(ServerHost, Node, From, Lang,
get_default(Host, Node, _From, #{lang := Lang}) ->
Type = select_type(Host, Node),
Options = node_options(Host, Type),
- DefaultEl = #xmlel{name = <<"default">>, attrs = [],
- children =
- [#xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA},
- {<<"type">>, <<"form">>}],
- children = get_configure_xfields(Type, Options, Lang, [])}]},
+ XEl = configure_form(<<"form">>, Options, Lang, []),
+ DefaultEl = #xmlel{name = <<"default">>, attrs = [], children = [XEl]},
{result,
[#xmlel{name = <<"pubsub">>,
attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
@@ -3855,9 +3770,12 @@ max_items(Host, Options) ->
<<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
get_option(Options, Var, []))).
-get_configure_xfields(_Type, Options, Lang, Groups) ->
- [?XFIELD(<<"hidden">>, <<>>, <<"FORM_TYPE">>, (?NS_PUBSUB_NODE_CONFIG)),
- ?BOOL_CONFIG_FIELD(<<"Deliver payloads with event notifications">>,
+configure_form(Type, Options, Lang, Groups) ->
+ Fields = get_configure_xfields(Options, Lang, Groups),
+ mongoose_data_forms:form(#{type => Type, ns => ?NS_PUBSUB_NODE_CONFIG, fields => Fields}).
+
+get_configure_xfields(Options, Lang, Groups) ->
+ [?BOOL_CONFIG_FIELD(<<"Deliver payloads with event notifications">>,
deliver_payloads),
?BOOL_CONFIG_FIELD(<<"Deliver event notifications">>,
deliver_notifications),
@@ -3905,20 +3823,20 @@ get_configure_xfields(_Type, Options, Lang, Groups) ->
%%
The specified node does not exist.
%%
set_configure(Host, Node, From, #{action_el := ActionEl, lang := Lang}) ->
- case xml:remove_cdata(ActionEl#xmlel.children) of
- [#xmlel{name = <<"x">>} = XEl] ->
- case {xml:get_tag_attr_s(<<"xmlns">>, XEl), xml:get_tag_attr_s(<<"type">>, XEl)} of
- {?NS_XDATA, <<"cancel">>} -> {result, []};
- {?NS_XDATA, <<"submit">>} -> set_configure_submit(Host, Node, From, XEl, Lang);
- _ -> {error, mongoose_xmpp_errors:bad_request()}
- end;
+ case mongoose_data_forms:find_and_parse_form(ActionEl) of
+ #{type := <<"cancel">>} ->
+ {result, []};
+ #{type := <<"submit">>, kvs := KVs} ->
+ set_configure_submit(Host, Node, From, KVs, Lang);
+ {error, Msg} ->
+ {error, mongoose_xmpp_errors:bad_request(Lang, Msg)};
_ ->
- {error, mongoose_xmpp_errors:bad_request()}
+ {error, mongoose_xmpp_errors:bad_request(Lang, <<"Invalid form type">>)}
end.
-set_configure_submit(Host, Node, User, XEl, Lang) ->
+set_configure_submit(Host, Node, User, KVs, Lang) ->
Action = fun(NodeRec) ->
- set_configure_transaction(Host, User, XEl, NodeRec)
+ set_configure_transaction(Host, User, KVs, NodeRec)
end,
case transaction(Host, Node, Action, ?FUNCTION_NAME) of
{result, {_OldNode, TNode}} ->
@@ -3931,24 +3849,21 @@ set_configure_submit(Host, Node, User, XEl, Lang) ->
Other
end.
-set_configure_transaction(Host, User, XEl, #pubsub_node{ type = Type, id = Nidx } = NodeRec) ->
+set_configure_transaction(Host, User, KVs, #pubsub_node{ type = Type, id = Nidx } = NodeRec) ->
case node_call(Type, get_affiliation, [Nidx, User]) of
{result, owner} ->
- case jlib:parse_xdata_submit(XEl) of
- invalid -> {error, mongoose_xmpp_errors:bad_request()};
- XData -> set_configure_valid_transaction(Host, NodeRec, XData)
- end;
+ set_configure_valid_transaction(Host, NodeRec, KVs);
_ ->
{error, mongoose_xmpp_errors:forbidden()}
end.
set_configure_valid_transaction(Host, #pubsub_node{ type = Type, options = Options } = NodeRec,
- XData) ->
+ KVs) ->
OldOpts = case Options of
[] -> node_options(Host, Type);
_ -> Options
end,
- case set_xoption(Host, XData, OldOpts) of
+ case set_xoption(Host, maps:to_list(KVs), OldOpts) of
NewOpts when is_list(NewOpts) ->
NewNode = NodeRec#pubsub_node{options = NewOpts},
case tree_call(Host, set_node, [NewNode]) of
@@ -4002,8 +3917,6 @@ add_opt(Key, Value, Opts) ->
set_xoption(Host, Opts, add_opt(Opt, Val, NewOpts))).
set_xoption(_Host, [], NewOpts) -> NewOpts;
-set_xoption(Host, [{<<"FORM_TYPE">>, _} | Opts], NewOpts) ->
- set_xoption(Host, Opts, NewOpts);
set_xoption(Host, [{<<"pubsub#roster_groups_allowed">>, Value} | Opts], NewOpts) ->
?SET_LIST_XOPT(roster_groups_allowed, Value);
set_xoption(Host, [{<<"pubsub#deliver_payloads">>, [Val]} | Opts], NewOpts) ->
diff --git a/src/pubsub/node_push.erl b/src/pubsub/node_push.erl
index 755ba7e502..ab1880525b 100644
--- a/src/pubsub/node_push.erl
+++ b/src/pubsub/node_push.erl
@@ -111,24 +111,15 @@ is_allowed_to_publish(PublishModel, Affiliation) ->
or (Affiliation == publish_only)).
--spec parse_form(undefined | exml:element()) -> invalid_form | #{atom() => binary()}.
+-spec parse_form(undefined | exml:element()) -> invalid_form | #{binary() => binary()}.
parse_form(undefined) ->
#{};
parse_form(Form) ->
- IsForm = ?NS_XDATA == exml_query:attr(Form, <<"xmlns">>),
- IsSubmit = <<"submit">> == exml_query:attr(Form, <<"type">>, <<"submit">>),
-
- FieldsXML = exml_query:subelements(Form, <<"field">>),
- Fields = [{exml_query:attr(Field, <<"var">>),
- exml_query:path(Field, [{element, <<"value">>}, cdata])} || Field <- FieldsXML],
- {_, CustomFields} = lists:partition(
- fun({Name, _}) ->
- Name == <<"FORM_TYPE">>
- end, Fields),
-
- case IsForm andalso IsSubmit of
- true ->
- maps:from_list(CustomFields);
- false ->
+ case mongoose_data_forms:parse_form(Form) of
+ #{type := <<"submit">>, kvs := KVs} ->
+ maps:filtermap(fun(_, [V]) -> {true, V};
+ (_, _) -> false
+ end, KVs);
+ _ ->
invalid_form
end.
diff --git a/src/pubsub/pubsub_form_utils.erl b/src/pubsub/pubsub_form_utils.erl
index 938f5bb811..ca19f7247d 100644
--- a/src/pubsub/pubsub_form_utils.erl
+++ b/src/pubsub/pubsub_form_utils.erl
@@ -13,9 +13,7 @@
-export([make_sub_xform/1, parse_sub_xform/1]).
--include("mongoose_logger.hrl").
-include("mongoose_ns.hrl").
--include_lib("exml/include/exml.hrl").
-type convert_from_binary_fun() :: fun(([binary()]) -> any()).
-type convert_to_binary_fun() :: fun((any()) -> [binary()]).
@@ -45,7 +43,7 @@
%% TODO: Right now
-spec make_sub_xform(Options :: mod_pubsub:subOptions()) -> {ok, exml:element()}.
make_sub_xform(Options) ->
- XFields = [make_field_xml(OptDefinition, Options) || OptDefinition <- sub_form_options()],
+ XFields = [make_field(OptDefinition, Options) || OptDefinition <- sub_form_options()],
{ok, make_sub_xform_xml(XFields)}.
%% The list of options returned by this function may be a subset of the options schema.
@@ -54,9 +52,11 @@ make_sub_xform(Options) ->
parse_sub_xform(undefined) ->
{ok, []};
parse_sub_xform(XForm) ->
- case jlib:parse_xdata_submit(XForm) of
- invalid -> {error, invalid_form};
- XData -> convert_fields_from_binaries(XData, [], sub_form_options())
+ case mongoose_data_forms:parse_form_fields(XForm) of
+ #{type := <<"submit">>, kvs := KVs} ->
+ convert_fields_from_binaries(maps:to_list(KVs), [], sub_form_options());
+ _ ->
+ {error, invalid_form}
end.
%%====================================================================
@@ -65,41 +65,30 @@ parse_sub_xform(XForm) ->
-spec make_sub_xform_xml(XFields :: [exml:element()]) -> exml:element().
make_sub_xform_xml(XFields) ->
- FormTypeEl = #xmlel{name = <<"field">>,
- attrs = [{<<"var">>, <<"FORM_TYPE">>}, {<<"type">>, <<"hidden">>}],
- children = [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]},
- #xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [FormTypeEl | XFields]}.
-
--spec make_field_xml(OptDefinition :: option_definition(),
- Options :: mod_pubsub:subOptions()) -> exml:element().
-make_field_xml({VarName, Key, #{ label := Label, form_type := FormType } = VarProps}, Options) ->
- ChoicesEls = make_choices_xml(VarProps),
- ValEls = make_values_xml(Key, Options, VarProps),
-
- #xmlel{name = <<"field">>,
- attrs = [{<<"var">>, VarName}, {<<"type">>, FormType}, {<<"label">>, Label}],
- children = ChoicesEls ++ ValEls}.
-
-make_choices_xml(#{ possible_choices := PossibleChoices }) ->
- [ make_option_xml(Value, Label) || {Value, Label} <- PossibleChoices ];
-make_choices_xml(#{}) ->
+ mongoose_data_forms:form(#{ns => ?NS_PUBSUB_SUB_OPTIONS, fields => XFields}).
+
+-spec make_field(OptDefinition :: option_definition(),
+ Options :: mod_pubsub:subOptions()) -> mongoose_data_forms:field().
+make_field({VarName, Key, #{ label := Label, form_type := FormType } = VarProps}, Options) ->
+ #{var => VarName,
+ type => FormType,
+ label => Label,
+ options => make_choices(VarProps),
+ values => make_values(Key, Options, VarProps)}.
+
+make_choices(#{ possible_choices := PossibleChoices }) ->
+ [ {Label, Value} || {Value, Label} <- PossibleChoices ];
+make_choices(#{}) ->
[].
-make_option_xml(Value, Label) ->
- #xmlel{name = <<"option">>, attrs = [{<<"label">>, Label}], children = [make_value_xml(Value)]}.
-
-make_values_xml(Key, Options, #{ data_type := DataType }) ->
+make_values(Key, Options, #{ data_type := DataType }) ->
case lists:keyfind(Key, 1, Options) of
{_, Value} ->
- [make_value_xml(BinVal) || BinVal <- convert_value_to_binaries(Value, DataType)];
+ convert_value_to_binaries(Value, DataType);
false ->
[]
end.
-make_value_xml(Value) ->
- #xmlel{name = <<"value">>, attrs = [], children = [#xmlcdata{ content = Value }]}.
-
%%====================================================================
%% Form definitions & conversions
%%====================================================================
@@ -176,8 +165,6 @@ sub_form_options() ->
{ok, mod_pubsub:subOptions()} | convert_from_binary_error().
convert_fields_from_binaries([], Result, _Schema) ->
{ok, Result};
-convert_fields_from_binaries([{<<"FORM_TYPE">>, _Values} | RData], Acc, Schema) ->
- convert_fields_from_binaries(RData, Acc, Schema);
convert_fields_from_binaries([{VarBin, Values} | RData], Acc, Schema) ->
case lists:keyfind(VarBin, 1, Schema) of
{_VBin, _Var, #{ data_type := DataType }} when Values == [] andalso DataType /= list ->
diff --git a/src/vcard/mod_vcard.erl b/src/vcard/mod_vcard.erl
index ce1d429413..4c58a27ea6 100644
--- a/src/vcard/mod_vcard.erl
+++ b/src/vcard/mod_vcard.erl
@@ -502,8 +502,9 @@ do_route(HostType, LServer, From, To, Acc,
route_search_iq_set(HostType, LServer, From, To, Acc, Lang, SubEl, IQ);
do_route(HostType, LServer, From, To, Acc,
#iq{type = get, xmlns = ?NS_SEARCH, lang = Lang} = IQ) ->
- Form = ?FORM(To, mod_vcard_backend:search_fields(HostType, LServer), Lang),
- ResIQ = make_search_form_result_iq(IQ, Form),
+ Instr = search_instructions(Lang),
+ Form = search_form(To, mod_vcard_backend:search_fields(HostType, LServer), Lang),
+ ResIQ = make_search_form_result_iq(IQ, [Instr, Form]),
ejabberd_router:route(To, From, Acc, jlib:iq_to_xml(ResIQ));
do_route(_HostType, _LServer, From, To, Acc,
#iq{type = set, xmlns = ?NS_DISCO_INFO}) ->
@@ -542,43 +543,50 @@ do_route(_HostType, _LServer, From, To, Acc, _IQ) ->
{Acc1, Err} = jlib:make_error_reply(Acc, mongoose_xmpp_errors:service_unavailable()),
ejabberd_router:route(To, From, Acc1, Err).
-make_search_form_result_iq(IQ, Form) ->
+make_search_form_result_iq(IQ, Elements) ->
IQ#iq{type = result,
sub_el = [#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_SEARCH}],
- children = Form
+ children = Elements
}]}.
+search_instructions(Lang) ->
+ Text = translate:translate(Lang, <<"You need an x:data capable client to search">>),
+ #xmlel{name = <<"instructions">>, attrs = [], children = [#xmlcdata{content = Text}]}.
+
+search_form(JID, SearchFields, Lang) ->
+ Title = <<(translate:translate(Lang, <<"Search users in ">>))/binary,
+ (jid:to_binary(JID))/binary>>,
+ Instructions = <<"Fill in fields to search for any matching Jabber User">>,
+ Fields = lists:map(fun ({X, Y}) -> ?TLFIELD(<<"text-single">>, X, Y) end, SearchFields),
+ mongoose_data_forms:form(#{title => Title, instructions => Instructions, fields => Fields}).
+
route_search_iq_set(HostType, LServer, From, To, Acc, Lang, SubEl, IQ) ->
- XDataEl = find_xdata_el(SubEl),
+ XDataEl = mongoose_data_forms:find_form(SubEl),
RSMIn = jlib:rsm_decode(IQ),
case XDataEl of
- false ->
+ undefined ->
{Acc1, Err} = jlib:make_error_reply(Acc, mongoose_xmpp_errors:bad_request()),
ejabberd_router:route(To, From, Acc1, Err);
_ ->
- XData = jlib:parse_xdata_submit(XDataEl),
- case XData of
- invalid ->
- {Acc1, Err} = jlib:make_error_reply(Acc, mongoose_xmpp_errors:bad_request()),
- ejabberd_router:route(To, From, Acc1, Err);
- _ ->
- {SearchResult, RSMOutEls} = search_result(HostType, LServer, Lang, To, XData, RSMIn),
+ case mongoose_data_forms:parse_form_fields(XDataEl) of
+ #{type := <<"submit">>, kvs := KVs} ->
+ {SearchResult, RSMOutEls} = search_result(HostType, LServer, Lang, To, KVs, RSMIn),
ResIQ = make_search_result_iq(IQ, SearchResult, RSMOutEls),
- ejabberd_router:route(To, From, Acc, jlib:iq_to_xml(ResIQ))
+ ejabberd_router:route(To, From, Acc, jlib:iq_to_xml(ResIQ));
+ _ ->
+ {Acc1, Err} = jlib:make_error_reply(Acc, mongoose_xmpp_errors:bad_request()),
+ ejabberd_router:route(To, From, Acc1, Err)
end
end.
make_search_result_iq(IQ, SearchResult, RSMOutEls) ->
+ Form = mongoose_data_forms:form(SearchResult),
IQ#iq{
type = result,
sub_el = [#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_SEARCH}],
- children = [#xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA},
- {<<"type">>, <<"result">>}],
- children = SearchResult}
- ] ++ RSMOutEls}
+ children = [Form | RSMOutEls]}
]}.
iq_get_vcard() ->
@@ -588,20 +596,6 @@ iq_get_vcard() ->
#xmlel{name = <<"DESC">>,
children = [#xmlcdata{content = [<<"MongooseIM vCard module">>,
<<"\nCopyright (c) Erlang Solutions Ltd.">>]}]}].
-find_xdata_el(#xmlel{children = SubEls}) ->
- find_xdata_el1(SubEls).
-
-find_xdata_el1([]) ->
- false;
-find_xdata_el1([XE = #xmlel{attrs = Attrs} | Els]) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
- ?NS_XDATA ->
- XE;
- _ ->
- find_xdata_el1(Els)
- end;
-find_xdata_el1([_ | Els]) ->
- find_xdata_el1(Els).
features() ->
[?NS_DISCO_INFO, ?NS_SEARCH, ?NS_VCARD].
@@ -612,27 +606,25 @@ identity(Lang) ->
name => translate:translate(Lang, <<"vCard User Search">>)}.
search_result(HostType, LServer, Lang, JID, Data, RSMIn) ->
- Text = translate:translate(Lang, <<"Search Results for ">>),
- TitleEl = #xmlel{name = <<"title">>,
- children = [#xmlcdata{content = [Text, jid:to_binary(JID)]}]},
+ Title = translate:translate(Lang, <<"Search Results for ", (jid:to_binary(JID))/binary>>),
ReportedFields = mod_vcard_backend:search_reported_fields(HostType, LServer, Lang),
- Results1 = mod_vcard_backend:search(HostType, LServer, Data),
+ Results1 = mod_vcard_backend:search(HostType, LServer, maps:to_list(Data)),
Results2 = lists:filtermap(
fun(Result) ->
case search_result_get_jid(Result) of
- {ok, ResultJID} ->
+ [ResultJID] ->
{true, {ResultJID, Result}};
- undefined ->
+ [] ->
false
end
end,
Results1),
%% mnesia does not guarantee sorting order
Results3 = lists:sort(Results2),
- {Results4, RSMOutEls} =
- apply_rsm_to_search_results(Results3, RSMIn, none),
+ {Results4, RSMOutEls} = apply_rsm_to_search_results(Results3, RSMIn, none),
Results5 = [Result || {_, Result} <- Results4],
- {[TitleEl, ReportedFields | Results5], RSMOutEls}.
+ Form = #{type => <<"result">>, title => Title, reported => ReportedFields, items => Results5},
+ {Form, RSMOutEls}.
%% No RSM input, create empty
apply_rsm_to_search_results(Results, none, RSMOut) ->
@@ -721,15 +713,8 @@ apply_rsm_to_search_results([], _, #rsm_out{} = RSMOut1) ->
RSMOut2 = RSMOut1#rsm_out{index = undefined},
{[], jlib:rsm_encode(RSMOut2)}.
-search_result_get_jid(#xmlel{name = <<"item">>,
- children = Children}) ->
- Fields = jlib:parse_xdata_fields(Children),
- case lists:keysearch(<<"jid">>, 1, Fields) of
- {value, {<<"jid">>, JID}} ->
- {ok, list_to_binary(JID)};
- false ->
- undefined
- end.
+search_result_get_jid(Fields) ->
+ [JID || #{var := <<"jid">>, values := [JID]} <- Fields].
parse_vcard(LUser, VHost, VCARD) ->
FN = xml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]),
@@ -805,23 +790,22 @@ prepare_index_allow_emoji(FieldName, Value) ->
prepare_index(FieldName, Sanitized).
--spec get_default_reported_fields(binary()) -> exml:element().
+-spec get_default_reported_fields(binary()) -> [mongoose_data_forms:field()].
get_default_reported_fields(Lang) ->
- #xmlel{name = <<"reported">>,
- children = [
- ?TLFIELD(<<"jid-single">>, <<"Jabber ID">>, <<"jid">>),
- ?TLFIELD(<<"text-single">>, <<"Full Name">>, <<"fn">>),
- ?TLFIELD(<<"text-single">>, <<"Name">>, <<"first">>),
- ?TLFIELD(<<"text-single">>, <<"Middle Name">>, <<"middle">>),
- ?TLFIELD(<<"text-single">>, <<"Family Name">>, <<"last">>),
- ?TLFIELD(<<"text-single">>, <<"Nickname">>, <<"nick">>),
- ?TLFIELD(<<"text-single">>, <<"Birthday">>, <<"bday">>),
- ?TLFIELD(<<"text-single">>, <<"Country">>, <<"ctry">>),
- ?TLFIELD(<<"text-single">>, <<"City">>, <<"locality">>),
- ?TLFIELD(<<"text-single">>, <<"Email">>, <<"email">>),
- ?TLFIELD(<<"text-single">>, <<"Organization Name">>, <<"orgname">>),
- ?TLFIELD(<<"text-single">>, <<"Organization Unit">>, <<"orgunit">>)
- ]}.
+ [
+ ?TLFIELD(<<"jid-single">>, <<"Jabber ID">>, <<"jid">>),
+ ?TLFIELD(<<"text-single">>, <<"Full Name">>, <<"fn">>),
+ ?TLFIELD(<<"text-single">>, <<"Name">>, <<"first">>),
+ ?TLFIELD(<<"text-single">>, <<"Middle Name">>, <<"middle">>),
+ ?TLFIELD(<<"text-single">>, <<"Family Name">>, <<"last">>),
+ ?TLFIELD(<<"text-single">>, <<"Nickname">>, <<"nick">>),
+ ?TLFIELD(<<"text-single">>, <<"Birthday">>, <<"bday">>),
+ ?TLFIELD(<<"text-single">>, <<"Country">>, <<"ctry">>),
+ ?TLFIELD(<<"text-single">>, <<"City">>, <<"locality">>),
+ ?TLFIELD(<<"text-single">>, <<"Email">>, <<"email">>),
+ ?TLFIELD(<<"text-single">>, <<"Organization Name">>, <<"orgname">>),
+ ?TLFIELD(<<"text-single">>, <<"Organization Unit">>, <<"orgunit">>)
+ ].
config_metrics(Host) ->
mongoose_module_metrics:opts_for_module(Host, ?MODULE, [backend]).
diff --git a/src/vcard/mod_vcard_backend.erl b/src/vcard/mod_vcard_backend.erl
index 647a8e6560..ef8d2ca7a8 100644
--- a/src/vcard/mod_vcard_backend.erl
+++ b/src/vcard/mod_vcard_backend.erl
@@ -95,7 +95,7 @@ get_vcard(HostType, LUser, LServer) ->
mongoose_backend:call_tracked(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args).
-spec search(mongooseim:host_type(), jid:lserver(), Data) ->
- Res :: term() when
+ Res :: [[mongoose_data_forms:field()]] when
Data :: term().
search(HostType, LServer, Data) ->
Args = [HostType, LServer, Data],
@@ -107,7 +107,7 @@ search_fields(HostType, LServer) ->
mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args).
-spec search_reported_fields(mongooseim:host_type(), jid:lserver(), Lang) ->
- Res :: term() when
+ Res :: [mongoose_data_forms:field()] when
Lang :: binary().
search_reported_fields(HostType, LServer, Lang) ->
Args = [HostType, LServer, Lang],
diff --git a/src/vcard/mod_vcard_ldap.erl b/src/vcard/mod_vcard_ldap.erl
index 9070f66a3b..41cc5e9faa 100644
--- a/src/vcard/mod_vcard_ldap.erl
+++ b/src/vcard/mod_vcard_ldap.erl
@@ -156,7 +156,8 @@ get_vcard(HostType, LUser, LServer) ->
set_vcard(_HostType, _User, _LServer, _VCard, _VCardSearch) ->
{error, mongoose_xmpp_errors:not_allowed()}.
--spec search(mongooseim:host_type(), jid:lserver(), [{binary(), [binary()]}]) -> [eldap_utils:eldap_entry()].
+-spec search(mongooseim:host_type(), jid:lserver(), [{binary(), [binary()]}]) ->
+ [[mongoose_data_forms:field()]].
search(HostType, LServer, Data) ->
State = get_state(HostType, LServer),
search_internal(State, Data).
@@ -166,20 +167,13 @@ search_fields(HostType, LServer) ->
State = get_state(HostType, LServer),
State#state.search_fields.
--spec search_reported_fields(mongooseim:host_type(), jid:lserver(), binary()) -> exml:element().
+-spec search_reported_fields(mongooseim:host_type(), jid:lserver(), binary()) ->
+ [mongoose_data_forms:field()].
search_reported_fields(HostType, LServer, Lang) ->
State = get_state(HostType, LServer),
SearchReported = State#state.search_reported,
- #xmlel{name = <<"reported">>, attrs = [],
- children =
- [?TLFIELD(<<"text-single">>, <<"Jabber ID">>,
- <<"jid">>)]
- ++
- lists:map(fun ({Name, Value}) ->
- ?TLFIELD(<<"text-single">>, Name,
- Value)
- end,
- SearchReported)}.
+ [?TLFIELD(<<"text-single">>, Name, Value) ||
+ {Name, Value} <- [{<<"Jabber ID">>, <<"jid">>} | SearchReported]].
%%--------------------------------------------------------------------
%% API
@@ -362,10 +356,10 @@ limited_results(E, _) ->
E.
search_items(Entries, State) ->
- lists:flatmap(fun(#eldap_entry{attributes = Attrs}) -> attrs_to_item_xml(Attrs, State) end,
+ lists:flatmap(fun(#eldap_entry{attributes = Attrs}) -> attrs_to_item(Attrs, State) end,
Entries).
-attrs_to_item_xml(Attrs, #state{uids = UIDs} = State) ->
+attrs_to_item(Attrs, #state{uids = UIDs} = State) ->
case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
{U, UIDAttrFormat} ->
case eldap_utils:get_user_part(U, UIDAttrFormat) of
@@ -387,9 +381,9 @@ make_user_item_if_exists(Username, Attrs,
{Username, LServer})}
end,
SearchReported),
- Result = [?FIELD(<<"jid">>, <>)] ++
- [?FIELD(Name, search_item_value(Name, Value, BinFields)) || {Name, Value} <- RFields],
- [#xmlel{name = <<"item">>, attrs = [], children = Result}];
+ [[?FIELD(<<"jid">>, <>)] ++
+ [?FIELD(Name, search_item_value(Name, Value, BinFields)) ||
+ {Name, Value} <- RFields]];
_ -> []
end.
diff --git a/src/vcard/mod_vcard_mnesia.erl b/src/vcard/mod_vcard_mnesia.erl
index 8ff8c0fd5d..1c40c49da4 100644
--- a/src/vcard/mod_vcard_mnesia.erl
+++ b/src/vcard/mod_vcard_mnesia.erl
@@ -57,6 +57,7 @@ set_vcard(HostType, User, LServer, VCard, VCardSearch) ->
mongoose_hooks:vcard_set(HostType, LServer, LUser, VCard),
ok.
+-spec search(mongooseim:host_type(), jid:lserver(), term()) -> [[mongoose_data_forms:field()]].
search(HostType, LServer, Data) ->
MatchHead = make_matchhead(LServer, Data),
R = do_search(HostType, LServer, MatchHead),
@@ -84,6 +85,8 @@ do_search(HostType, LServer, MatchHeadIn) ->
search_fields(_HostType, _LServer) ->
mod_vcard:default_search_fields().
+-spec search_reported_fields(mongooseim:host_type(), jid:lserver(), ejabberd:lang()) ->
+ [mongoose_data_forms:field()].
search_reported_fields(_HostType, _LServer, Lang) ->
mod_vcard:get_default_reported_fields(Lang).
@@ -181,18 +184,17 @@ make_val(ValBin) ->
record_to_item(R) ->
{User, Server} = R#vcard_search.user,
- #xmlel{name = <<"item">>,
- children = [
- ?FIELD(<<"jid">>, [User, <<"@">>, Server]),
- ?FIELD(<<"fn">>, (R#vcard_search.fn)),
- ?FIELD(<<"last">>, (R#vcard_search.family)),
- ?FIELD(<<"first">>, (R#vcard_search.given)),
- ?FIELD(<<"middle">>, (R#vcard_search.middle)),
- ?FIELD(<<"nick">>, (R#vcard_search.nickname)),
- ?FIELD(<<"bday">>, (R#vcard_search.bday)),
- ?FIELD(<<"ctry">>, (R#vcard_search.ctry)),
- ?FIELD(<<"locality">>, (R#vcard_search.locality)),
- ?FIELD(<<"email">>, (R#vcard_search.email)),
- ?FIELD(<<"orgname">>, (R#vcard_search.orgname)),
- ?FIELD(<<"orgunit">>, (R#vcard_search.orgunit))
- ]}.
+ [
+ ?FIELD(<<"jid">>, <>),
+ ?FIELD(<<"fn">>, (R#vcard_search.fn)),
+ ?FIELD(<<"last">>, (R#vcard_search.family)),
+ ?FIELD(<<"first">>, (R#vcard_search.given)),
+ ?FIELD(<<"middle">>, (R#vcard_search.middle)),
+ ?FIELD(<<"nick">>, (R#vcard_search.nickname)),
+ ?FIELD(<<"bday">>, (R#vcard_search.bday)),
+ ?FIELD(<<"ctry">>, (R#vcard_search.ctry)),
+ ?FIELD(<<"locality">>, (R#vcard_search.locality)),
+ ?FIELD(<<"email">>, (R#vcard_search.email)),
+ ?FIELD(<<"orgname">>, (R#vcard_search.orgname)),
+ ?FIELD(<<"orgunit">>, (R#vcard_search.orgunit))
+ ].
diff --git a/src/vcard/mod_vcard_rdbms.erl b/src/vcard/mod_vcard_rdbms.erl
index ddd56b23b2..033d22913c 100644
--- a/src/vcard/mod_vcard_rdbms.erl
+++ b/src/vcard/mod_vcard_rdbms.erl
@@ -163,10 +163,13 @@ search_fields(_HostType, _VHost) ->
mod_vcard:default_search_fields().
%% Search vCards reported fields callback
+-spec search_reported_fields(mongooseim:host_type(), jid:lserver(), ejabberd:lang()) ->
+ [mongoose_data_forms:field()].
search_reported_fields(_HostType, _VHost, Lang) ->
mod_vcard:get_default_reported_fields(Lang).
%% Search vCards callback
+-spec search(mongooseim:host_type(), jid:lserver(), term()) -> [[mongoose_data_forms:field()]].
search(HostType, LServer, Data) ->
Filters = make_filters(LServer, Data),
case Filters of
@@ -379,18 +382,17 @@ record_to_items(Records) ->
record_to_item({Username, VCardVHost, FN, Family, Given, Middle,
Nickname, BDay, CTRY, Locality,
EMail, OrgName, OrgUnit}) ->
- #xmlel{name = <<"item">>,
- children = [
- ?FIELD(<<"jid">>, <>),
- ?FIELD(<<"fn">>, FN),
- ?FIELD(<<"last">>, Family),
- ?FIELD(<<"first">>, Given),
- ?FIELD(<<"middle">>, Middle),
- ?FIELD(<<"nick">>, Nickname),
- ?FIELD(<<"bday">>, BDay),
- ?FIELD(<<"ctry">>, CTRY),
- ?FIELD(<<"locality">>, Locality),
- ?FIELD(<<"email">>, EMail),
- ?FIELD(<<"orgname">>, OrgName),
- ?FIELD(<<"orgunit">>, OrgUnit)
- ]}.
+ [
+ ?FIELD(<<"jid">>, <>),
+ ?FIELD(<<"fn">>, FN),
+ ?FIELD(<<"last">>, Family),
+ ?FIELD(<<"first">>, Given),
+ ?FIELD(<<"middle">>, Middle),
+ ?FIELD(<<"nick">>, Nickname),
+ ?FIELD(<<"bday">>, BDay),
+ ?FIELD(<<"ctry">>, CTRY),
+ ?FIELD(<<"locality">>, Locality),
+ ?FIELD(<<"email">>, EMail),
+ ?FIELD(<<"orgname">>, OrgName),
+ ?FIELD(<<"orgunit">>, OrgUnit)
+ ].
diff --git a/src/vcard/mod_vcard_riak.erl b/src/vcard/mod_vcard_riak.erl
index 38648f57d3..0f7568837b 100644
--- a/src/vcard/mod_vcard_riak.erl
+++ b/src/vcard/mod_vcard_riak.erl
@@ -62,6 +62,7 @@ get_vcard(HostType, LUser, LServer) ->
Other
end.
+-spec search(mongooseim:host_type(), jid:lserver(), term()) -> [[mongoose_data_forms:field()]].
search(HostType, LServer, Data) ->
YZQuery = make_yz_query(Data, []),
do_search(YZQuery, HostType, LServer).
@@ -85,6 +86,8 @@ do_search(YZQueryIn, HostType, LServer) ->
search_fields(_HostType, _LServer) ->
mod_vcard:default_search_fields().
+-spec search_reported_fields(mongooseim:host_type(), jid:lserver(), ejabberd:lang()) ->
+ [mongoose_data_forms:field()].
search_reported_fields(_HostType, _LServer, Lang) ->
mod_vcard:get_default_reported_fields(Lang).
@@ -116,9 +119,7 @@ make_val(Val) ->
end.
doc2item(HostType, LServer, Props) ->
- Vals = lists:map(pa:bind(fun extract_field/2, Props), search_fields(HostType, LServer)),
- #xmlel{name = <<"item">>,
- children = Vals}.
+ lists:map(pa:bind(fun extract_field/2, Props), search_fields(HostType, LServer)).
extract_field(Props, {_, <<"user">>}) ->
{_, Username} = lists:keyfind(riak_search_mapping(<<"user">>), 1, Props),