Add general API to set per user settings using adhoc commands which can be defaulted from config file. Add 'Send messages as headlines' Transport setting

This commit is contained in:
Jan Kaluza 2012-12-18 10:59:52 +01:00
parent ad8ed5be56
commit 0b0e020cdf
13 changed files with 196 additions and 8 deletions

View file

@ -44,6 +44,13 @@ class AdHocCommandFactory {
virtual std::string getNode() = 0;
virtual std::string getName() = 0;
virtual std::map<std::string, std::string> &getUserSettings() {
return m_userSettings;
}
protected:
std::map<std::string, std::string> m_userSettings;
};
}

View file

@ -66,6 +66,8 @@ class AdHocManager : public Swift::Responder<Swift::Command> {
private:
virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::Command> payload);
virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::Command> payload);
void handleUserCreated(User *user);
Component *m_component;
DiscoItemsResponder *m_discoItemsResponder;

View file

@ -70,6 +70,7 @@ message ConversationMessage {
optional string nickname = 4;
optional string xhtml = 5;
optional string timestamp = 6;
optional bool headline = 7;
}
message Room {

View file

@ -53,7 +53,10 @@ class SettingsAdHocCommand : public AdHocCommand {
class SettingsAdHocCommandFactory : public AdHocCommandFactory {
public:
SettingsAdHocCommandFactory() {}
SettingsAdHocCommandFactory() {
m_userSettings["send_headlines"] = "0";
}
virtual ~SettingsAdHocCommandFactory() {}
AdHocCommand *createAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) {

View file

@ -116,6 +116,14 @@ class User : public Swift::EntityCapsProvider {
return m_resources;
}
void addUserSetting(const std::string &key, const std::string &value) {
m_settings[key] = value;
}
const std::string &getUserSetting(const std::string &key) {
return m_settings[key];
}
boost::signal<void ()> onReadyToConnect;
boost::signal<void (Swift::Presence::ref presence)> onPresenceChanged;
boost::signal<void (const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password)> onRoomJoined;
@ -145,6 +153,7 @@ class User : public Swift::EntityCapsProvider {
int m_resources;
int m_reconnectCounter;
std::list<Swift::Presence::ref> m_joinedRooms;
std::map<std::string, std::string> m_settings;
};
}

View file

@ -42,6 +42,8 @@ AdHocManager::AdHocManager(Component *component, DiscoItemsResponder *discoItems
m_collectTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(20);
m_collectTimer->onTick.connect(boost::bind(&AdHocManager::removeOldSessions, this));
m_collectTimer->start();
m_userManager->onUserCreated.connect(boost::bind(&AdHocManager::handleUserCreated, this, _1));
}
AdHocManager::~AdHocManager() {
@ -66,6 +68,21 @@ void AdHocManager::stop() {
m_sessions.clear();
}
void AdHocManager::handleUserCreated(User *user) {
if (!m_storageBackend) {
return;
}
for (std::map<std::string, AdHocCommandFactory *>::const_iterator it = m_factories.begin(); it != m_factories.end(); it++) {
for (std::map<std::string, std::string>::const_iterator it2 = it->second->getUserSettings().begin(); it2 != it->second->getUserSettings().end(); it2++) {
std::string value = CONFIG_STRING_DEFAULTED(m_component->getConfig(), it->second->getNode() + "." + it2->first, it2->second);
int type = (int) TYPE_BOOLEAN;
m_storageBackend->getUserSetting(user->getUserInfo().id, it2->first, type, value);
user->addUserSetting(it2->first, value);
}
}
}
void AdHocManager::addAdHocCommand(AdHocCommandFactory *factory) {
if (m_factories.find(factory->getNode()) != m_factories.end()) {
LOG4CXX_ERROR(logger, "Command with node " << factory->getNode() << " is already registered. Ignoring this attempt.");
@ -159,6 +176,12 @@ bool AdHocManager::handleSetRequest(const Swift::JID& from, const Swift::JID& to
// Command completed, so we can remove it now
if (response->getStatus() == Swift::Command::Completed || response->getStatus() == Swift::Command::Canceled) {
// update userSettings map if user is already connected
User *user = m_userManager->getUser(from.toBare().toString());
if (user) {
handleUserCreated(user);
}
m_sessions[from].erase(command->getId());
if (m_sessions[from].empty()) {
m_sessions.erase(from);

View file

@ -82,7 +82,14 @@ void Conversation::handleMessage(boost::shared_ptr<Swift::Message> &message, con
message->setType(Swift::Message::Groupchat);
}
else {
message->setType(Swift::Message::Chat);
if (message->getType() != Swift::Message::Headline) {
if (m_conversationManager->getUser()->getUserSetting("send_headlines") == "1") {
message->setType(Swift::Message::Headline);
}
else {
message->setType(Swift::Message::Chat);
}
}
}
std::string n = nickname;

View file

@ -39,12 +39,7 @@ ConversationManager::ConversationManager(User *user, Component *component){
}
ConversationManager::~ConversationManager() {
while(!m_convs.empty()) {
LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Removing conversation " << (*m_convs.begin()).first);
(*m_convs.begin()).second->destroyRoom();
delete (*m_convs.begin()).second;
m_convs.erase(m_convs.begin());
}
deleteAllConversations();
}
void ConversationManager::deleteAllConversations() {

View file

@ -649,6 +649,10 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool
msg->setBody(payload.message());
}
if (payload.headline()) {
msg->setType(Swift::Message::Headline);
}
// Add xhtml-im payload.
if (CONFIG_BOOL(m_config, "service.enable_xhtml") && !payload.xhtml().empty()) {
msg->addPayload(boost::make_shared<Swift::XHTMLIMPayload>(payload.xhtml()));

View file

@ -40,6 +40,11 @@ SettingsAdHocCommand::SettingsAdHocCommand(Component *component, UserManager *us
field->setName("enable_transport");
field->setLabel("Enable transport");
addFormField(field);
field = Swift::BooleanFormField::create(CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.send_headlines", "0") == "1");
field->setName("send_headlines");
field->setLabel("Send messages as headlines");
addFormField(field);
}
SettingsAdHocCommand::~SettingsAdHocCommand() {

View file

@ -193,6 +193,15 @@ class TestingStorageBackend : public StorageBackend {
settings[userId][variable] = value;
}
void dumpUserSettings() {
std::cout << "\n\nUserSettings dump:\n";
for (std::map<int, std::map<std::string, std::string> >::const_iterator it = settings.begin(); it != settings.end(); it++) {
for (std::map<std::string, std::string>::const_iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) {
std::cout << it->first << ":" << it2->first << "=" << it2->second << "\n";
}
}
}
virtual void beginTransaction() {}
virtual void commitTransaction() {}
};

View file

@ -24,6 +24,7 @@ using namespace Transport;
class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
CPPUNIT_TEST_SUITE(ConversationManagerTest);
CPPUNIT_TEST(handleNormalMessages);
CPPUNIT_TEST(handleNormalMessagesHeadline);
CPPUNIT_TEST(handleGroupchatMessages);
CPPUNIT_TEST(handleGroupchatMessagesTwoResources);
CPPUNIT_TEST(handleChatstateMessages);
@ -204,6 +205,55 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
received.clear();
}
void handleNormalMessagesHeadline() {
User *user = userManager->getUser("user@localhost");
user->addUserSetting("send_headlines", "1");
TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1@test");
user->getConversationManager()->addConversation(conv);
conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
boost::shared_ptr<Swift::Message> msg(new Swift::Message());
msg->setBody("hi there<>!");
// Forward it
conv->handleMessage(msg);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::Message::Headline, dynamic_cast<Swift::Message *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT_EQUAL(std::string("hi there<>!"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
received.clear();
user->addUserSetting("send_headlines", "0");
// Forward it - Conversation should keep the Headline type, because msg->getType() is Headline
conv->handleMessage(msg);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::Message::Headline, dynamic_cast<Swift::Message *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT_EQUAL(std::string("hi there<>!"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
received.clear();
msg->setType(Swift::Message::Chat);
// Forward it
conv->handleMessage(msg);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::Message::Chat, dynamic_cast<Swift::Message *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT_EQUAL(std::string("hi there<>!"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
}
void handleGroupchatMessages() {
User *user = userManager->getUser("user@localhost");
TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);

View file

@ -33,6 +33,8 @@ class SettingsAdHocCommandTest : public CPPUNIT_NS :: TestFixture, public BasicT
CPPUNIT_TEST(executeBadSessionID);
CPPUNIT_TEST(executeNotRegistered);
CPPUNIT_TEST(cancel);
CPPUNIT_TEST(propagateUserSetting);
CPPUNIT_TEST(defaultAccordingToConfig);
CPPUNIT_TEST_SUITE_END();
public:
@ -327,6 +329,77 @@ class SettingsAdHocCommandTest : public CPPUNIT_NS :: TestFixture, public BasicT
CPPUNIT_ASSERT_EQUAL(Swift::Command::Canceled, getStanza(received[0])->getPayload<Swift::Command>()->getStatus());
}
void propagateUserSetting() {
connectUser();
User *user = userManager->getUser("user@localhost");
CPPUNIT_ASSERT_EQUAL(std::string("0"), user->getUserSetting("send_headlines"));
boost::shared_ptr<Swift::Command> payload(new Swift::Command("settings"));
boost::shared_ptr<Swift::IQ> iq = Swift::IQ::createRequest(Swift::IQ::Set, Swift::JID("localhost"), "id", payload);
iq->setFrom("user@localhost");
injectIQ(iq);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::IQ *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast<Swift::IQ *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>());
CPPUNIT_ASSERT_EQUAL(std::string("settings"), getStanza(received[0])->getPayload<Swift::Command>()->getNode());
CPPUNIT_ASSERT_EQUAL(Swift::Command::Executing, getStanza(received[0])->getPayload<Swift::Command>()->getStatus());
// form element
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>()->getForm());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>()->getForm()->getField("send_headlines"));
// set enabled_transport = 0
Swift::FormField::ref f = getStanza(received[0])->getPayload<Swift:: Command>()->getForm()->getField("send_headlines");
boost::dynamic_pointer_cast<Swift::BooleanFormField>(f)->setValue(true);
std::string sessionId = getStanza(received[0])->getPayload<Swift::Command>()->getSessionID();
// finish the command
payload = boost::shared_ptr<Swift::Command>(new Swift::Command("settings"));
payload->setSessionID(sessionId);
payload->setForm(getStanza(received[0])->getPayload<Swift::Command>()->getForm());
iq = Swift::IQ::createRequest(Swift::IQ::Set, Swift::JID("localhost"), "id", payload);
iq->setFrom("user@localhost");
received.clear();
injectIQ(iq);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(std::string("1"), user->getUserSetting("send_headlines"));
}
void defaultAccordingToConfig() {
std::istringstream ifs("service.server_mode = 1\nservice.jid_escaping=0\nservice.jid=localhost\nsettings.send_headlines=1\n");
cfg->load(ifs);
connectUser();
User *user = userManager->getUser("user@localhost");
CPPUNIT_ASSERT_EQUAL(std::string("1"), user->getUserSetting("send_headlines"));
boost::shared_ptr<Swift::Command> payload(new Swift::Command("settings"));
boost::shared_ptr<Swift::IQ> iq = Swift::IQ::createRequest(Swift::IQ::Set, Swift::JID("localhost"), "id", payload);
iq->setFrom("user@localhost");
injectIQ(iq);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::IQ *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast<Swift::IQ *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>());
CPPUNIT_ASSERT_EQUAL(std::string("settings"), getStanza(received[0])->getPayload<Swift::Command>()->getNode());
CPPUNIT_ASSERT_EQUAL(Swift::Command::Executing, getStanza(received[0])->getPayload<Swift::Command>()->getStatus());
// form element
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>()->getForm());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>()->getForm()->getField("send_headlines"));
Swift::FormField::ref f = getStanza(received[0])->getPayload<Swift:: Command>()->getForm()->getField("send_headlines");
CPPUNIT_ASSERT_EQUAL(true, boost::dynamic_pointer_cast<Swift::BooleanFormField>(f)->getValue());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION (SettingsAdHocCommandTest);