~singpolyma/biboumi

e75d7ad8ea72044fdfd2317e03f91ba5bea06b86 — louiz’ 5 years ago 40db183
Add a Record History option in the Channel configuration form

fix #3269
M CHANGELOG.rst => CHANGELOG.rst +2 -0
@@ 3,6 3,8 @@ Version 6.0

 - The LiteSQL dependency was removed. Only libsqlite3 is now necessary
   to work with the database.
 - The RecordHistory option can now also be configured for each IRC channel,
   individually.

Version 5.0 - 2017-05-24
========================

M doc/biboumi.1.rst => doc/biboumi.1.rst +6 -1
@@ 358,7 358,7 @@ History
-------

Public channel messages are saved into archives, inside the database, unless
the `record_history` option is set to false for that user `Ad-hoc commands`.
the `record_history` option is set to false by that user (see `Ad-hoc commands`).
Private messages (messages that are sent directly to a nickname, not a
channel) are never stored in the database. When a channel is joined, biboumi
sends the `max_history_length` messages found in the database as the MUC


@@ 631,6 631,11 @@ On a channel JID (e.g on the JID #test%chat.freenode.org@biboumi.example.com)
      the archiving of messages is enabled for this room, the client will
      receive the messages that where sent in this channel. This option can be
      used to make biboumi act as an IRC bouncer.
    * Record History: whether or not history messages should be saved in
      the database, for this specific channel. If the value is “unset” (the
      default), then the value configured globally is used. This option is there,
      for example, to be able to enable history recording globally while disabling
      it for a few specific “private” channels.

Raw IRC messages
----------------

M src/utils/optional_bool.hpp => src/utils/optional_bool.hpp +10 -0
@@ 20,6 20,16 @@ struct OptionalBool
    this->is_set = false;
  }

  std::string to_string()
  {
    if (this->is_set == false)
      return "unset";
    else if (this->value)
      return "true";
    else
      return "false";
  }

  bool is_set{false};
  bool value{false};
};

M src/xmpp/biboumi_adhoc_commands.cpp => src/xmpp/biboumi_adhoc_commands.cpp +49 -4
@@ 434,6 434,26 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, 
  XmlSubNode instructions(x, "instructions");
  instructions.set_inner("Edit the form, to configure the settings of the IRC channel "s + iid.get_local());

  XmlSubNode record_history(x, "field");
  record_history["var"] = "record_history";
  record_history["type"] = "list-single";
  record_history["label"] = "Record history for this channel";
  record_history["desc"] = "If unset, the value is the one configured globally";

  {
    // Value selected by default
    XmlSubNode value(record_history, "value");
    value.set_inner(options.col<Database::RecordHistoryOptional>().to_string());
  }
  // All three possible values
  for (const auto& val: {"unset", "true", "false"})
  {
    XmlSubNode option(record_history, "option");
    option["label"] = val;
    XmlSubNode value(option, "value");
    value.set_inner(val);
  }

  XmlSubNode encoding_out(x, "field");
  encoding_out["var"] = "encoding_out";
  encoding_out["type"] = "text-single";


@@ 471,12 491,12 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, 
  }
}

void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node)
void ConfigureIrcChannelStep2(XmppComponent& xmpp_component, AdhocSession& session, XmlNode& command_node)
{
  const Jid owner(session.get_owner_jid());
  const Jid target(session.get_target_jid());

  if (handle_irc_channel_configuration_form(command_node, owner, target))
  if (handle_irc_channel_configuration_form(xmpp_component, command_node, owner, target))
    {
      command_node.delete_all_children();
      XmlSubNode note(command_node, "note");


@@ 492,7 512,7 @@ void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& co
    }
}

bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& requester, const Jid& target)
bool handle_irc_channel_configuration_form(XmppComponent& xmpp_component, const XmlNode& node, const Jid& requester, const Jid& target)
{
  const XmlNode* x = node.get_child("x", "jabber:x:data");
  if (x)


@@ 500,7 520,7 @@ bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& reque
      if (x->get_tag("type") == "submit")
        {
          const Iid iid(target.local, {});
          auto options = Database::get_irc_channel_options(requester.local + "@" + requester.domain,
          auto options = Database::get_irc_channel_options(requester.bare(),
                                                           iid.get_server(), iid.get_local());
          for (const XmlNode *field: x->get_children("field", "jabber:x:data"))
            {


@@ 517,6 537,31 @@ bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& reque
              else if (field->get_tag("var") == "persistent" &&
                       value)
                options.col<Database::Persistent>() = to_bool(value->get_inner());
              else if (field->get_tag("var") == "record_history" &&
                       value && !value->get_inner().empty())
                {
                  OptionalBool& database_value = options.col<Database::RecordHistoryOptional>();
                  if (value->get_inner() == "true")
                    database_value.set_value(true);
                  else if (value->get_inner() == "false")
                    database_value.set_value(false);
                  else
                    database_value.unset();
                  auto& biboumi_component = dynamic_cast<BiboumiComponent&>(xmpp_component);
                  Bridge* bridge = biboumi_component.find_user_bridge(requester.bare());
                  if (bridge)
                    {
                      if (database_value.is_set)
                        bridge->set_record_history(database_value.value);
                      else
                        { // It is unset, we need to fetch the Global option, to
                          // know if it’s enabled or not
                          auto g_options = Database::get_global_options(requester.bare());
                          bridge->set_record_history(g_options.col<Database::RecordHistory>());
                        }
                    }
                }

            }

          options.save(Database::db);

M src/xmpp/biboumi_adhoc_commands.hpp => src/xmpp/biboumi_adhoc_commands.hpp +1 -1
@@ 20,7 20,7 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com
void ConfigureIrcChannelStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node);
void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, const Jid& target);
void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node);
bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& requester, const Jid& target);
bool handle_irc_channel_configuration_form(XmppComponent&, const XmlNode& node, const Jid& requester, const Jid& target);

void DisconnectUserFromServerStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node);
void DisconnectUserFromServerStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node);

M src/xmpp/biboumi_component.cpp => src/xmpp/biboumi_component.cpp +1 -1
@@ 719,7 719,7 @@ bool BiboumiComponent::handle_room_configuration_form(const XmlNode& query, cons
    return false;

  Jid requester(from);
  if (!handle_irc_channel_configuration_form(query, requester, to))
  if (!handle_irc_channel_configuration_form(*this, query, requester, to))
    return false;

  Stanza iq("iq");

M tests/end_to_end/__main__.py => tests/end_to_end/__main__.py +3 -0
@@ 2467,6 2467,7 @@ if __name__ == '__main__':
                     partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']",
                                             "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']",
                                             "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']",
                                             "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='unset']",
                                             ),
                                             after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
                             ),


@@ 2476,6 2477,7 @@ if __name__ == '__main__':
                                          "<field var='ports' />"
                                          "<field var='encoding_out'><value>UTF-8</value></field>"
                                          "<field var='encoding_in'><value>latin-1</value></field>"
                                          "<field var='record_history'><value>true</value></field>"
                                          "</x></command></iq>"),
                     partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"),



@@ 2484,6 2486,7 @@ if __name__ == '__main__':
                                             "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC channel #foo on server irc.localhost']",
                                             "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']",
                                             "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']",
                                             "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='true']",
                                             "/iq/commands:command/commands:actions/commands:next",
                                             ),
                             after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))