From 558eb857fdf03dec3d8a51e1b84346515a306627 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Thu, 17 Mar 2022 17:57:53 +0100 Subject: [PATCH 1/6] Export all instead of duplicate test declare --- big_tests/tests/inbox_extensions_SUITE.erl | 57 +--------------------- 1 file changed, 1 insertion(+), 56 deletions(-) diff --git a/big_tests/tests/inbox_extensions_SUITE.erl b/big_tests/tests/inbox_extensions_SUITE.erl index 7df8362558..3a7850c0cc 100644 --- a/big_tests/tests/inbox_extensions_SUITE.erl +++ b/big_tests/tests/inbox_extensions_SUITE.erl @@ -1,4 +1,5 @@ -module(inbox_extensions_SUITE). +-compile([export_all, nowarn_export_all]). -include_lib("escalus/include/escalus.hrl"). -include_lib("escalus/include/escalus_xmlns.hrl"). @@ -23,62 +24,6 @@ init_per_testcase/2, end_per_testcase/2]). -%% ERRORS --export([ - % General - returns_error_when_no_jid_provided/1, - returns_error_when_invalid_jid_provided/1, - returns_error_when_valid_jid_but_no_property/1, - % Set-unread errors - returns_error_when_read_invalid_value/1, - returns_error_when_read_valid_request_but_not_in_inbox/1, - % Archiving errors - returns_error_when_archive_invalid_value/1, - returns_error_when_archive_valid_request_but_not_in_inbox/1, - % Muting errors - returns_error_when_mute_invalid_value/1, - returns_error_when_mute_invalid_seconds/1, - returns_error_when_mute_valid_request_but_not_in_inbox/1, - % Form errors - returns_error_when_archive_field_is_invalid/1, - returns_error_when_max_is_not_a_number/1 - ]). -%% SUCCESSES --export([ - % read - read_unread_entry_set_to_read/1, - read_unread_entry_set_to_read_iq_id_as_fallback/1, - read_unread_entry_set_to_read_queryid/1, - read_read_entry_set_to_unread/1, - read_unread_entry_with_two_messages_when_set_unread_then_unread_count_stays_in_two/1, - % archive - archive_active_entry_gets_archived/1, - archive_archived_entry_gets_active_on_request/1, - archive_archived_entry_gets_active_for_the_sender_on_new_message/1, - archive_archived_entry_gets_active_for_the_receiver_on_new_message/1, - archive_active_unread_entry_gets_archived_and_still_unread/1, - archive_full_archive_can_be_fetched/1, - archive_full_archive_can_be_fetched_queryid/1, - % mute - mute_unmuted_entry_gets_muted/1, - mute_muted_entry_gets_unmuted/1, - mute_after_timestamp_gets_unmuted/1, - mute_muted_conv_restarts_timestamp/1, - % other - returns_valid_properties_form/1, - properties_can_be_get/1, - properties_many_can_be_set/1, - properties_many_can_be_set_queryid/1, - max_queries_can_be_limited/1, - max_queries_can_fetch_ahead/1, - timestamp_is_not_reset_with_setting_properties/1 - ]). -%% Groupchats --export([ - groupchat_setunread_stanza_sets_inbox/1 % muclight - ]). - - all() -> inbox_helper:skip_or_run_inbox_tests(tests()). From 7144f85d7405d79390470a250bbc1e3e846cd299 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Thu, 17 Mar 2022 18:06:16 +0100 Subject: [PATCH 2/6] Move build_forward_el as a helper function --- src/inbox/mod_inbox.erl | 13 +------------ src/inbox/mod_inbox_utils.erl | 14 +++++++++++++- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/inbox/mod_inbox.erl b/src/inbox/mod_inbox.erl index c4ead75bcd..c94ed80ef9 100644 --- a/src/inbox/mod_inbox.erl +++ b/src/inbox/mod_inbox.erl @@ -315,7 +315,7 @@ build_inbox_message(#{msg := Content, -spec build_result_el(exml:element(), id(), integer(), integer(), boolean(), integer(), integer()) -> exml:element(). build_result_el(Msg, QueryId, Count, Timestamp, Archive, MutedUntil, AccTS) -> - Forwarded = build_forward_el(Msg, Timestamp), + Forwarded = mod_inbox_utils:build_forward_el(Msg, Timestamp), Properties = mod_inbox_entries:extensions_result(Archive, MutedUntil, AccTS), QueryAttr = [{<<"queryid">>, QueryId} || QueryId =/= undefined, QueryId =/= <<>>], #xmlel{name = <<"result">>, @@ -338,17 +338,6 @@ build_result_iq(List) -> build_result_el(Name, CountBin) -> #xmlel{name = Name, children = [#xmlcdata{content = CountBin}]}. --spec build_forward_el(exml:element(), integer()) -> exml:element(). -build_forward_el(Content, Timestamp) -> - Delay = build_delay_el(Timestamp), - #xmlel{name = <<"forwarded">>, attrs = [{<<"xmlns">>, ?NS_FORWARD}], - children = [Delay, Content]}. - --spec build_delay_el(Timestamp :: integer()) -> exml:element(). -build_delay_el(Timestamp) -> - TS = calendar:system_time_to_rfc3339(Timestamp, [{offset, "Z"}, {unit, microsecond}]), - jlib:timestamp_to_xml(TS, undefined, undefined). - %%%%%%%%%%%%%%%%%%% %% iq-get -spec build_inbox_form() -> exml:element(). diff --git a/src/inbox/mod_inbox_utils.erl b/src/inbox/mod_inbox_utils.erl index ba891a3dc7..de67d83c72 100644 --- a/src/inbox/mod_inbox_utils.erl +++ b/src/inbox/mod_inbox_utils.erl @@ -36,7 +36,8 @@ maybe_muted_until/2, binary_to_bool/1, bool_to_binary/1, - build_inbox_entry_key/2 + build_inbox_entry_key/2, + build_forward_el/2 ]). -ignore_xref([ @@ -221,3 +222,14 @@ build_inbox_entry_key(FromJid, ToJid) -> {LUser, LServer} = jid:to_lus(FromJid), ToBareJid = jid:nameprep(jid:to_binary(jid:to_lus(ToJid))), {LUser, LServer, ToBareJid}. + +-spec build_forward_el(exml:element(), integer()) -> exml:element(). +build_forward_el(Content, Timestamp) -> + Delay = build_delay_el(Timestamp), + #xmlel{name = <<"forwarded">>, attrs = [{<<"xmlns">>, ?NS_FORWARD}], + children = [Delay, Content]}. + +-spec build_delay_el(Timestamp :: integer()) -> exml:element(). +build_delay_el(Timestamp) -> + TS = calendar:system_time_to_rfc3339(Timestamp, [{offset, "Z"}, {unit, microsecond}]), + jlib:timestamp_to_xml(TS, undefined, undefined). From 116e6f26c31c614641fd0cf0a62a1978e1d6d119 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Thu, 17 Mar 2022 18:07:16 +0100 Subject: [PATCH 3/6] Use the same types in the backend module as in the rest of the inbox subsystem --- src/inbox/mod_inbox_backend.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/inbox/mod_inbox_backend.erl b/src/inbox/mod_inbox_backend.erl index 7a9e5a1e7f..4445488f3d 100644 --- a/src/inbox/mod_inbox_backend.erl +++ b/src/inbox/mod_inbox_backend.erl @@ -2,6 +2,8 @@ %% the backend modules (i.e. mod_inbox_rdbms). -module(mod_inbox_backend). +-include("mod_inbox.hrl"). + -export([init/2, get_inbox/4, clear_inbox/3, @@ -39,7 +41,7 @@ mod_inbox:write_res() when HostType :: mongooseim:host_type(), InboxEntryKey :: mod_inbox:entry_key(), - Content :: binary(), + Content :: content(), Count :: integer(), MsgId :: binary(), Timestamp :: integer(). @@ -52,7 +54,7 @@ mod_inbox:count_res() when HostType :: mongooseim:host_type(), InboxEntryKey :: mod_inbox:entry_key(), - Content :: binary(), + Content :: content(), MsgId :: binary(), Timestamp :: integer(). @@ -112,7 +114,7 @@ remove_domain(HostType, LServer) -> mod_inbox:write_res() when HostType :: mongooseim:host_type(), InboxEntryKey :: mod_inbox:entry_key(), - Content :: binary(), + Content :: content(), Count :: integer(), MsgId :: binary(), Timestamp :: integer(). @@ -131,7 +133,7 @@ remove_inbox_row(HostType, InboxEntryKey) -> mod_inbox:count_res() when HostType :: mongooseim:host_type(), InboxEntryKey :: mod_inbox:entry_key(), - Content :: binary(), + Content :: content(), MsgId :: binary(), Timestamp :: integer(). set_inbox_incr_unread(HostType, InboxEntryKey, Content, MsgId, Timestamp) -> From a1daf311245a4703ac5e23704086478f3040a944 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Thu, 17 Mar 2022 18:09:08 +0100 Subject: [PATCH 4/6] Fix typos and names in inbox_rdbms --- src/inbox/mod_inbox_rdbms.erl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/inbox/mod_inbox_rdbms.erl b/src/inbox/mod_inbox_rdbms.erl index 82944d7d50..54dadd76c1 100644 --- a/src/inbox/mod_inbox_rdbms.erl +++ b/src/inbox/mod_inbox_rdbms.erl @@ -153,7 +153,7 @@ set_inbox_incr_unread(HostType, {LUser, LServer, LToBareJid}, Content, MsgId, Ti InsertParams, UpdateParams, UniqueKeyValues), check_result(Res). --spec reset_unread(HosType :: mongooseim:host_type(), +-spec reset_unread(HostType :: mongooseim:host_type(), InboxEntryKey :: mod_inbox:entry_key(), MsgId :: binary() | undefined) -> mod_inbox:write_res(). reset_unread(HostType, {LUser, LServer, LToBareJid}, MsgId) -> @@ -167,7 +167,7 @@ clear_inbox(HostType, LUser, LServer) -> Res = execute_delete(HostType, LUser, LServer), check_result(Res). --spec get_entry_properties(HosType :: mongooseim:host_type(), +-spec get_entry_properties(HostType :: mongooseim:host_type(), InboxEntryKey :: mod_inbox:entry_key()) -> entry_properties() | nil(). get_entry_properties(HostType, {LUser, LServer, RemBareJID}) -> @@ -175,7 +175,7 @@ get_entry_properties(HostType, {LUser, LServer, RemBareJID}) -> {selected, []} -> []; {selected, [Selected]} -> - decode_entries(Selected) + decode_properties(Selected) end. -spec set_entry_properties(HostType :: mongooseim:host_type(), @@ -191,17 +191,9 @@ set_entry_properties(HostType, {LUser, LServer, RemBareJID}, Properties) -> {updated, 0} -> {error, <<"item-not-found">>}; {selected, [Result]} -> - decode_entries(Result) + decode_properties(Result) end. -decode_entries({BArchive, BCount, BMutedUntil}) -> - Archive = mongoose_rdbms:to_bool(BArchive), - Count = mongoose_rdbms:result_to_integer(BCount), - MutedUntil = mongoose_rdbms:result_to_integer(BMutedUntil), - #{archive => Archive, - unread_count => Count, - muted_until => MutedUntil}. - %% ---------------------------------------------------------------------- %% Internal functions %% ---------------------------------------------------------------------- @@ -434,6 +426,14 @@ decode_row(HostType, {Username, Content, Count, MsgId, Timestamp, Archive, Muted archive => BoolArchive, muted_until => NumericMutedUntil}. +decode_properties({BArchive, BCount, BMutedUntil}) -> + Archive = mongoose_rdbms:to_bool(BArchive), + Count = mongoose_rdbms:result_to_integer(BCount), + MutedUntil = mongoose_rdbms:result_to_integer(BMutedUntil), + #{archive => Archive, + unread_count => Count, + muted_until => MutedUntil}. + -spec check_result_is_expected(_, list()) -> mod_inbox:write_res(). check_result_is_expected({updated, Val}, ValList) when is_list(ValList) -> case lists:member(Val, ValList) of From 96e6f3e6c8caf59abd8e68eb56e82584903963f7 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Thu, 17 Mar 2022 18:09:53 +0100 Subject: [PATCH 5/6] Add get_full_entry callback to the inbox backend --- src/inbox/mod_inbox_backend.erl | 16 +++++++++++++++- src/inbox/mod_inbox_rdbms.erl | 23 +++++++++++++++++++++++ src/inbox/mod_inbox_rdbms_async.erl | 6 ++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/inbox/mod_inbox_backend.erl b/src/inbox/mod_inbox_backend.erl index 4445488f3d..7d082a542b 100644 --- a/src/inbox/mod_inbox_backend.erl +++ b/src/inbox/mod_inbox_backend.erl @@ -12,6 +12,7 @@ remove_inbox_row/2, set_inbox_incr_unread/5, get_inbox_unread/2, + get_full_entry/2, get_entry_properties/2, set_entry_properties/3, reset_unread/3]). @@ -67,6 +68,11 @@ HostType :: mongooseim:host_type(), InboxEntryKey :: mod_inbox:entry_key(). +-callback get_full_entry(HostType, InboxEntryKey) -> Ret when + HostType :: mongooseim:host_type(), + InboxEntryKey :: mod_inbox:entry_key(), + Ret :: inbox_res() | nil(). + -callback get_entry_properties(HostType, InboxEntryKey) -> Ret when HostType :: mongooseim:host_type(), InboxEntryKey :: mod_inbox:entry_key(), @@ -155,6 +161,14 @@ get_inbox_unread(HostType, InboxEntryKey) -> Args = [HostType, InboxEntryKey], mongoose_backend:call_tracked(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). +-spec get_full_entry(HostType, InboxEntryKey) -> Ret when + HostType :: mongooseim:host_type(), + InboxEntryKey :: mod_inbox:entry_key(), + Ret :: inbox_res() | nil(). +get_full_entry(HostType, InboxEntryKey) -> + Args = [HostType, InboxEntryKey], + mongoose_backend:call_tracked(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). + -spec get_entry_properties(HostType, InboxEntryKey) -> Ret when HostType :: mongooseim:host_type(), InboxEntryKey :: mod_inbox:entry_key(), @@ -175,4 +189,4 @@ set_entry_properties(HostType, InboxEntryKey, Params) -> callback_funs() -> [get_inbox, set_inbox, set_inbox_incr_unread, reset_unread, remove_inbox_row, clear_inbox, get_inbox_unread, - get_entry_properties, set_entry_properties, remove_domain]. + get_full_entry, get_entry_properties, set_entry_properties, remove_domain]. diff --git a/src/inbox/mod_inbox_rdbms.erl b/src/inbox/mod_inbox_rdbms.erl index 54dadd76c1..70b0a8f587 100644 --- a/src/inbox/mod_inbox_rdbms.erl +++ b/src/inbox/mod_inbox_rdbms.erl @@ -21,6 +21,7 @@ remove_domain/2, clear_inbox/3, get_inbox_unread/2, + get_full_entry/2, get_entry_properties/2, set_entry_properties/3]). -export([check_result/1]). @@ -43,6 +44,11 @@ %% TODO pools aren't multitenancy-ready yet init(HostType, _Options) -> RowCond = <<"WHERE luser = ? AND lserver = ? AND remote_bare_jid = ?">>, + mongoose_rdbms:prepare( + inbox_select_entry, inbox, + [luser, lserver, remote_bare_jid], + <<"SELECT remote_bare_jid, content, unread_count, msg_id, timestamp, archive, muted_until ", + "FROM inbox ", RowCond/binary>>), mongoose_rdbms:prepare(inbox_select_unread_count, inbox, [luser, lserver, remote_bare_jid], <<"SELECT unread_count FROM inbox ", @@ -167,6 +173,17 @@ clear_inbox(HostType, LUser, LServer) -> Res = execute_delete(HostType, LUser, LServer), check_result(Res). +-spec get_full_entry(HostType :: mongooseim:host_type(), + InboxEntryKey :: mod_inbox:entry_key()) -> + inbox_res() | nil(). +get_full_entry(HostType, {LUser, LServer, RemBareJID}) -> + case execute_select_full_entry(HostType, LUser, LServer, RemBareJID) of + {selected, []} -> + []; + {selected, [Selected]} -> + decode_row(HostType, Selected) + end. + -spec get_entry_properties(HostType :: mongooseim:host_type(), InboxEntryKey :: mod_inbox:entry_key()) -> entry_properties() | nil(). @@ -378,6 +395,12 @@ execute_select_unread_count(HostType, LUser, LServer, RemBareJID) -> mongoose_rdbms:execute_successfully(HostType, inbox_select_unread_count, [LUser, LServer, RemBareJID]). +-spec execute_select_full_entry(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid()) -> + mongoose_rdbms:query_result(). +execute_select_full_entry(HostType, LUser, LServer, RemBareJID) -> + mongoose_rdbms:execute_successfully(HostType, inbox_select_entry, + [LUser, LServer, RemBareJID]). + -spec execute_select_properties(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid()) -> mongoose_rdbms:query_result(). execute_select_properties(HostType, LUser, LServer, RemBareJID) -> diff --git a/src/inbox/mod_inbox_rdbms_async.erl b/src/inbox/mod_inbox_rdbms_async.erl index e220f60ed6..bd5a3baaa1 100644 --- a/src/inbox/mod_inbox_rdbms_async.erl +++ b/src/inbox/mod_inbox_rdbms_async.erl @@ -22,6 +22,7 @@ clear_inbox/3, get_inbox/4, get_inbox_unread/2, + get_full_entry/2, get_entry_properties/2, set_entry_properties/3]). -export([stop/1]). @@ -135,6 +136,11 @@ remove_domain(HostType, LServer) -> clear_inbox(HostType, LUser, LServer) -> mod_inbox_rdbms:clear_inbox(HostType, LUser, LServer). +-spec get_full_entry(mongooseim:host_type(), mod_inbox:entry_key()) -> + inbox_res() | nil(). +get_full_entry(HostType, Entry) -> + mod_inbox_rdbms:get_full_entry(HostType, Entry). + -spec get_entry_properties(mongooseim:host_type(), mod_inbox:entry_key()) -> entry_properties() | nil(). get_entry_properties(HostType, Entry) -> From de8d0c9bf8cf72d1332c48c5e7feb3ba678328af Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Thu, 17 Mar 2022 18:11:29 +0100 Subject: [PATCH 6/6] Extend the inbox query to be able to fetch an entire inbox entry at once This is useful for example when a conversation is entered, through some link, to any point in the far past of the conversation, instead of at the bottom. The inbox might be desired in a two-column layout, like in many phone apps when displayed horizontally, but if loading from MAM at some point far in the past, in a conversation whose inbox entry wasn't already loaded (old conversation, pagination), building the right inbox snippet would take many round-trips. --- big_tests/tests/inbox_extensions_SUITE.erl | 33 +++++++++++++++++++-- doc/open-extensions/inbox.md | 25 +++++++++++++++- src/inbox/mod_inbox_entries.erl | 34 ++++++++++++++++++---- 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/big_tests/tests/inbox_extensions_SUITE.erl b/big_tests/tests/inbox_extensions_SUITE.erl index 3a7850c0cc..3a972ece60 100644 --- a/big_tests/tests/inbox_extensions_SUITE.erl +++ b/big_tests/tests/inbox_extensions_SUITE.erl @@ -80,6 +80,7 @@ groups() -> % other returns_valid_properties_form, properties_can_be_get, + properties_full_entry_can_be_get, properties_many_can_be_set, properties_many_can_be_set_queryid, max_queries_can_be_limited, @@ -440,6 +441,17 @@ properties_can_be_get(Config) -> % Then Bob can just query the properties of this entry at will query_properties(Bob, Alice, [{archive, false}, {read, true}, {mute, 0}]) end). + +properties_full_entry_can_be_get(Config) -> + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> + % Alice sends a message to Bob + Body = <<"Hi Bob">>, + inbox_helper:send_and_mark_msg(Alice, Bob, Body), + inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), + % Then Bob can just query the properties of this entry at will + query_properties(Bob, Alice, [{archive, false}, {read, true}, {mute, 0}], full_entry) + end). + properties_many_can_be_set(Config) -> properties_many_can_be_set(Config, undefined). properties_many_can_be_set_queryid(Config) -> @@ -548,17 +560,32 @@ groupchat_setunread_stanza_sets_inbox(Config) -> -spec query_properties(escalus:client(), escalus:client(), proplists:proplist()) -> [exml:element()]. query_properties(From, To, Expected) -> - Stanza = make_inbox_get_properties(To), + query_properties(From, To, Expected, none). + +-spec query_properties(escalus:client(), escalus:client(), proplists:proplist(), none | full_entry) -> + [exml:element()]. +query_properties(From, To, Expected, FullEntry) -> + Stanza = make_inbox_get_properties(To, FullEntry), escalus:send(From, Stanza), Result = escalus:wait_for_stanza(From), ?assert(escalus_pred:is_iq_result(Stanza, Result)), [Props] = exml_query:subelements(Result, <<"query">>), ?assertEqual(inbox_helper:inbox_ns_conversation(), exml_query:attr(Props, <<"xmlns">>)), + maybe_assert_full_entry(Props, FullEntry), lists:foreach(fun({Key, Val}) -> assert_property(Props, Key, Val) end, Expected). --spec make_inbox_get_properties(escalus:client()) -> exml:element(). -make_inbox_get_properties(To) -> +maybe_assert_full_entry(_, none) -> + ok; +maybe_assert_full_entry(Props, full_entry) -> + ?assertNotEqual(undefined, exml_query:path(Props, [{element, <<"forwarded">>}])). + +-spec make_inbox_get_properties(escalus:client(), boolean()) -> exml:element(). +make_inbox_get_properties(To, none) -> Query = escalus_stanza:query_el(inbox_helper:inbox_ns_conversation(), jid_attr(To), []), + escalus_stanza:iq(<<"get">>, [Query]); +make_inbox_get_properties(To, full_entry) -> + Attrs = [{<<"complete">>, <<"true">>} | jid_attr(To)], + Query = escalus_stanza:query_el(inbox_helper:inbox_ns_conversation(), Attrs, []), escalus_stanza:iq(<<"get">>, [Query]). -spec set_inbox_properties(escalus:client(), escalus:client(), proplists:proplist()) -> ok. diff --git a/doc/open-extensions/inbox.md b/doc/open-extensions/inbox.md index 829401ef6a..6b720160c8 100644 --- a/doc/open-extensions/inbox.md +++ b/doc/open-extensions/inbox.md @@ -200,6 +200,29 @@ To which the server will reply, just like before, with: ``` +If an entire entry wanted to be queried, and not only its attributes, a `complete='true'` can be provided: +```xml + + + +``` +To which the server will reply, just like before, with: +```xml + + + + + + Hello + + + false + 0 + true + + +``` + ## Setting properties Setting properties is done using the standard XMPP pattern of `iq-query` and `iq-result`, as below: @@ -217,7 +240,7 @@ where `Property` and `Value` are a list of key-value pairs as follows: - `mute`: number of _seconds_ to mute for. Choose `0` for unmuting. - `read` (adjective, not verb): `true` or `false`. Setting to true essentially sets the unread-count to `0`, `false` sets the unread-count to `1` (if it was equal to `0`, otherwise it lefts it unchanged). No other possibilities are offered, to reduce the risk of inconsistencies or problems induced by a faulty client. -*Note* that resetting the inbox count will not be forwarded. While a chat marker will be forwarded to the interlocutor(s), (including the case of a big groupchat with thousands of participants), this reset stanza will not. +*Note* that resetting the inbox count will not be forwarded. While a chat marker will be forwarded to the interlocutor(s), (including the case of a big groupchat with thousands of participants), this reset stanza will not. If the query was successful, the server will answer with two stanzas, following the classic pattern of broadcasting state changes. First, it would send a message with a `` children containing all new configuration, to the bare-jid of the user: this facilitates broadcasting to all online resources to successfully synchronise their interfaces. ```xml diff --git a/src/inbox/mod_inbox_entries.erl b/src/inbox/mod_inbox_entries.erl index f5bb339469..81ff95b6aa 100644 --- a/src/inbox/mod_inbox_entries.erl +++ b/src/inbox/mod_inbox_entries.erl @@ -9,6 +9,9 @@ -export([should_be_stored_in_inbox/1]). -export([extensions_result/3]). +-type get_entry_type() :: full_entry | only_properties. +-export_type([get_entry_type/0]). + -spec process_iq_conversation(Acc :: mongoose_acc:t(), From :: jid:jid(), To :: jid:jid(), @@ -33,7 +36,15 @@ process_iq_conversation_get(Acc, IQ, From, SubEl) -> SubElWithForm = SubEl#xmlel{children = [Form]}, {Acc, IQ#iq{type = result, sub_el = SubElWithForm}}; EntryJID -> - get_properties_for_jid(Acc, IQ, From, EntryJID) + FullEntry = maybe_get_full_entry(SubEl), + get_properties_for_jid(Acc, IQ, From, EntryJID, FullEntry) + end. + +-spec maybe_get_full_entry(exml:element()) -> get_entry_type(). +maybe_get_full_entry(SubEl) -> + case exml_query:attr(SubEl, <<"complete">>) of + <<"true">> -> full_entry; + _ -> only_properties end. -spec build_inbox_entry_form() -> exml:element(). @@ -46,12 +57,17 @@ build_inbox_entry_form() -> jlib:form_field({<<"read">>, <<"boolean">>, <<"false">>}), jlib:form_field({<<"mute">>, <<"text-single">>, <<"0">>})]}. --spec get_properties_for_jid(mongoose_acc:t(), jlib:iq(), jid:jid(), jid:jid()) -> +fetch_right_query(HostType, InboxEntryKey, only_properties) -> + mod_inbox_backend:get_entry_properties(HostType, InboxEntryKey); +fetch_right_query(HostType, InboxEntryKey, full_entry) -> + mod_inbox_backend:get_full_entry(HostType, InboxEntryKey). + +-spec get_properties_for_jid(mongoose_acc:t(), jlib:iq(), jid:jid(), jid:jid(), get_entry_type()) -> {mongoose_acc:t(), jlib:iq()}. -get_properties_for_jid(Acc, IQ, From, EntryJID) -> +get_properties_for_jid(Acc, IQ, From, EntryJID, QueryType) -> HostType = mongoose_acc:host_type(Acc), {_, _, BinEntryJID} = InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(From, EntryJID), - case mod_inbox_backend:get_entry_properties(HostType, InboxEntryKey) of + case fetch_right_query(HostType, InboxEntryKey, QueryType) of [] -> return_error(Acc, IQ, <<"Entry not found">>); Result -> CurrentTS = mongoose_acc:timestamp(Acc), @@ -143,14 +159,20 @@ process_reset_stanza(Acc, From, IQ, _ResetStanza, InterlocutorJID) -> %% Helpers %%-------------------------------------------------------------------- --spec build_result(entry_properties(), integer()) -> [exml:element()]. -build_result(#{archive := Archived, unread_count := UnreadCount, muted_until := MutedUntil}, CurrentTS) -> +-spec build_result(inbox_res() | entry_properties(), integer()) -> [exml:element()]. +build_result(Entry = #{archive := Archived, unread_count := UnreadCount, muted_until := MutedUntil}, CurrentTS) -> + maybe_full_entry(Entry) ++ [ kv_to_el(<<"archive">>, mod_inbox_utils:bool_to_binary(Archived)), kv_to_el(<<"read">>, mod_inbox_utils:bool_to_binary(0 =:= UnreadCount)), kv_to_el(<<"mute">>, mod_inbox_utils:maybe_muted_until(MutedUntil, CurrentTS)) ]. +maybe_full_entry(#{msg := Msg, timestamp := Timestamp}) -> + [ mod_inbox_utils:build_forward_el(Msg, Timestamp) ]; +maybe_full_entry(_) -> + []. + -spec kv_to_el(binary(), binary()) -> exml:element(). kv_to_el(Key, Value) -> #xmlel{name = Key, children = [#xmlcdata{content = Value}]}.