From d3bbd300be4480c0a3b7285c91b3b27e82ca9c2f Mon Sep 17 00:00:00 2001 From: Peter Burgess Date: Fri, 20 Apr 2018 16:18:20 +0100 Subject: Add a XEP-346 FDP Form Submission Dialog A new form submission dialog that will discover nodes containing fdp form templates in a given pubsub domain, and will present the form to the user ready to be filled out and submitted. Test-Infomation: Personally tested the request for all node items, loading the latest form from one of the discovered nodes, submitting a form and checking it turns up on my mlink server using mlc, checked that the submitted infomation is correct, what happens when an incorrect domain is queried, what happens when a valid domain is queried but it has no fdp nodes, and what happens when a form is requested from an invalid node. Unit tests written to test success and failure of the three controller functions requestPubSubNodeData(), requestTemplateForm() and testSubmitForm(). Change-Id: If8c57bb4e3120dd44d52f7332069eb18a14cb385 diff --git a/Swift/Controllers/AccountController.cpp b/Swift/Controllers/AccountController.cpp index 048d140..fe7c200 100644 --- a/Swift/Controllers/AccountController.cpp +++ b/Swift/Controllers/AccountController.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -125,7 +126,8 @@ AccountController::AccountController( loginWindow_(nullptr) , useDelayForLatency_(useDelayForLatency), ftOverview_(nullptr), - emoticons_(emoticons) { + emoticons_(emoticons), + fdpFormSubmitController_(nullptr) { storages_ = nullptr; certificateStorage_ = nullptr; certificateTrustChecker_ = nullptr; @@ -580,6 +582,7 @@ void AccountController::performLoginFromCachedCredentials() { presence->setShow(static_cast(profileSettings_->getIntSetting("lastShow", StatusShow::Online))); presence->setStatus(profileSettings_->getStringSetting("lastStatus")); statusTracker_->setRequestedPresence(presence); + fdpFormSubmitController_ = std::make_unique(jid_, client_->getIQRouter(), uiEventStream_, uiFactory_); } else { /* In case we're in the middle of another login, make sure they don't overlap */ client_->disconnect(); diff --git a/Swift/Controllers/AccountController.h b/Swift/Controllers/AccountController.h index 30ae265..774aa8b 100644 --- a/Swift/Controllers/AccountController.h +++ b/Swift/Controllers/AccountController.h @@ -81,6 +81,7 @@ namespace Swift { class BlockListController; class ContactSuggester; class ContactsFromXMPPRoster; + class FdpFormSubmitController; class AccountController { public: @@ -197,5 +198,6 @@ namespace Swift { HighlightEditorController* highlightEditorController_; std::map emoticons_; boost::signals2::connection enableCarbonsRequestHandlerConnection_; + std::unique_ptr fdpFormSubmitController_; }; } diff --git a/Swift/Controllers/FdpFormSubmitController.cpp b/Swift/Controllers/FdpFormSubmitController.cpp new file mode 100644 index 0000000..639b4e9 --- /dev/null +++ b/Swift/Controllers/FdpFormSubmitController.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Swift { + +FdpFormSubmitController::FdpFormSubmitController(const JID& self, IQRouter* iqRouter, UIEventStream* uiEventStream, FdpFormSubmitWindowFactory* factory) : selfJID_(self), iqRouter_(iqRouter), uiEventStream_(uiEventStream), factory_(factory), formSubmitWindow_(nullptr) { + fdpFormSubmitWindowOpenUIEventConnection_= uiEventStream_->onUIEvent.connect( [this](const std::shared_ptr& uiEvent){ handleUIEvent(uiEvent); }); +} + +FdpFormSubmitController::~FdpFormSubmitController() { +} + +void FdpFormSubmitController::handleUIEvent(const std::shared_ptr& uiEvent) { + if (auto openEvent = std::dynamic_pointer_cast(uiEvent)) { + if (formSubmitWindow_) { + formSubmitWindow_->raise(); + } + else { + createFormSubmitWindow(); + } + } +} + +void FdpFormSubmitController::createFormSubmitWindow() { + formSubmitWindow_ = factory_->createFdpFormSubmitWindow(); + formSubmitWindow_->onCloseEvent.connect([this](){ closeFormSubmitWindow(); }); + formSubmitWindow_->onRequestPubSubNodeData.connect([this](const std::string& string){ requestPubSubNodeData(string); }); + formSubmitWindow_->onRequestTemplateForm.connect([this](const std::string& fdpTemplateNodeName){ requestTemplateForm(fdpTemplateNodeName); }); + formSubmitWindow_->onSubmitForm.connect([this](const std::shared_ptr
& form){ submitForm(form); }); + formSubmitWindow_->show(); +} + +void FdpFormSubmitController::closeFormSubmitWindow() { + formSubmitWindow_.reset(); +} + +void FdpFormSubmitController::requestPubSubNodeData(const std::string& domainName) { + JID domainJID(domainName); + auto discoItemsRequest = GetDiscoItemsRequest::create(domainJID, iqRouter_); + discoItemsRequest->onResponse.connect( + [this, domainName](std::shared_ptr discoItems, ErrorPayload::ref errorPayloadRef) { + if (!discoItems || errorPayloadRef) { + formSubmitWindow_->showNodePlaceholder(FdpFormSubmitWindow::NodeError::DomainNotFound); + return; + } + currentDomain_ = domainName; + + bool templateNodeAdded = false; + formSubmitWindow_->clearNodeData(); + auto discoItemList = discoItems->getItems(); + for (auto discoItem : discoItemList) { + auto node = discoItem.getNode(); + if (node.substr(0, 13) != "fdp/template/") { + continue; + } + + std::string nodeName = discoItem.getName().empty() ? node : discoItem.getName(); + + formSubmitWindow_->addNode(node, nodeName); + templateNodeAdded = true; + } + if (!templateNodeAdded) { + formSubmitWindow_->showNodePlaceholder(FdpFormSubmitWindow::NodeError::NoFdpNodesInDomain); + } + } + ); + discoItemsRequest->send(); +} + +void FdpFormSubmitController::requestTemplateForm(const std::string& nodeName) { + auto pubSubItems = std::make_shared(nodeName); + pubSubItems->setMaximumItems(1); + auto formRequest = std::make_shared>(IQ::Type::Get, selfJID_, currentDomain_, pubSubItems, iqRouter_); + + formRequest->onResponse.connect( + [this, nodeName](std::shared_ptr response, ErrorPayload::ref errorPayload) { + if (!response || errorPayload) { + formSubmitWindow_->showFormPlaceholder(FdpFormSubmitWindow::TemplateError::RequestFailed); + return; + } + auto pubSubItemList = response->getItems(); + if (pubSubItemList.empty()) { + formSubmitWindow_->showFormPlaceholder(FdpFormSubmitWindow::TemplateError::CannotLocateForm); + return; + } + auto payloadList = pubSubItemList[0]->getData(); + if (payloadList.empty()) { + formSubmitWindow_->showFormPlaceholder(FdpFormSubmitWindow::TemplateError::CannotLocateForm); + return; + } + if (auto form = std::dynamic_pointer_cast(payloadList[0])) { + currentTemplateNode_ = nodeName; + formSubmitWindow_->setFormData(form); + } + else { + formSubmitWindow_->showFormPlaceholder(FdpFormSubmitWindow::TemplateError::InvalidPayload); + return; + } + } + ); + + formRequest->send(); +} + +void FdpFormSubmitController::submitForm(const std::shared_ptr& form) { + std::string submittedNode = currentTemplateNode_; + submittedNode.replace(submittedNode.find("/template/", 0), 10, "/submitted/"); + auto pubSubItem = std::make_shared(); + auto pubSubItems = std::make_shared(submittedNode); + auto pubSubPublish = std::make_shared(); + pubSubPublish->setNode(submittedNode); + pubSubPublish->addItem(pubSubItem); + pubSubItem->addData(form); + pubSubItems->addItem(pubSubItem); + auto formRequest = std::make_shared>(IQ::Type::Set, selfJID_, currentDomain_, pubSubPublish, iqRouter_); + + formRequest->onResponse.connect( + [this](std::shared_ptr response, ErrorPayload::ref errorPayload) { + if (!response || errorPayload) { + formSubmitWindow_->handleSubmitServerResponse(false); + return; + } + formSubmitWindow_->handleSubmitServerResponse(true); + } + ); + + formRequest->send(); +} + +} diff --git a/Swift/Controllers/FdpFormSubmitController.h b/Swift/Controllers/FdpFormSubmitController.h new file mode 100644 index 0000000..69db08c --- /dev/null +++ b/Swift/Controllers/FdpFormSubmitController.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include +#include + +#include + +#include + +namespace Swift { + class FdpFormSubmitWindow; + class FdpFormSubmitWindowFactory; + class Form; + class IQRouter; + class UIEvent; + class UIEventStream; + + class FdpFormSubmitController { + public: + FdpFormSubmitController(const JID& self, IQRouter* iqRouter, UIEventStream* uiEventStream, FdpFormSubmitWindowFactory* factory); + ~FdpFormSubmitController(); + + private: + void handleUIEvent(const std::shared_ptr& uiEvent); + void createFormSubmitWindow(); + void closeFormSubmitWindow(); + void requestPubSubNodeData(const std::string& domainName); + void requestTemplateForm(const std::string& nodeName); + void submitForm(const std::shared_ptr& form); + + JID selfJID_; + IQRouter* iqRouter_; + UIEventStream* uiEventStream_; + FdpFormSubmitWindowFactory* factory_; + std::unique_ptr formSubmitWindow_; + std::string currentDomain_; + std::string currentTemplateNode_; + + boost::signals2::scoped_connection fdpFormSubmitWindowOpenUIEventConnection_; + }; +} diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index cbd3bf3..25d326a 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -41,6 +41,7 @@ if env["SCONS_STAGE"] == "build" : "ContactsFromXMPPRoster.cpp", "EventNotifier.cpp", "EventWindowController.cpp", + "FdpFormSubmitController.cpp", "FileTransfer/FileTransferController.cpp", "FileTransfer/FileTransferOverview.cpp", "FileTransfer/FileTransferProgressInfo.cpp", @@ -85,6 +86,7 @@ if env["SCONS_STAGE"] == "build" : "Translator.cpp", "UIEvents/UIEvent.cpp", "UIInterfaces/ChatListWindow.cpp", + "UIInterfaces/FdpFormSubmitWindow.cpp", "UIInterfaces/HighlightEditorWindow.cpp", "UIInterfaces/XMLConsoleWidget.cpp", "WhiteboardManager.cpp", @@ -106,6 +108,7 @@ if env["SCONS_STAGE"] == "build" : File("Settings/UnitTest/SettingsProviderHierachyTest.cpp"), File("UnitTest/ChatMessageSummarizerTest.cpp"), File("UnitTest/ContactSuggesterTest.cpp"), + File("UnitTest/FdpFormSubmitControllerTest.cpp"), File("UnitTest/MockChatWindow.cpp"), File("UnitTest/PresenceNotifierTest.cpp"), File("UnitTest/PreviousStatusStoreTest.cpp"), diff --git a/Swift/Controllers/UIEvents/FdpFormSubmitWindowOpenUIEvent.h b/Swift/Controllers/UIEvents/FdpFormSubmitWindowOpenUIEvent.h new file mode 100644 index 0000000..d540cb2 --- /dev/null +++ b/Swift/Controllers/UIEvents/FdpFormSubmitWindowOpenUIEvent.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include + +namespace Swift { + class FdpFormSubmitWindowOpenUIEvent : public UIEvent { + public: + FdpFormSubmitWindowOpenUIEvent() { + } + }; +} diff --git a/Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.cpp b/Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.cpp new file mode 100644 index 0000000..47ef9cd --- /dev/null +++ b/Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include + +#include + +namespace Swift { + +std::string FdpFormSubmitWindow::getNodeErrorText(NodeError nodeError) const { + switch(nodeError) { + case NodeError::DomainNotFound: return QT_TRANSLATE_NOOP("", "Error: No pubsub domain found"); + case NodeError::NoFdpNodesInDomain: return QT_TRANSLATE_NOOP("", "Error: Domain does not contain an FDP template node"); + case NodeError::NoError: return ""; + } + return ""; +} + +std::string FdpFormSubmitWindow::getTemplateErrorText(TemplateError templateError) const { + switch(templateError) { + case TemplateError::CannotLocateForm: return QT_TRANSLATE_NOOP("", "Error: Could not locate template form"); + case TemplateError::InvalidPayload: return QT_TRANSLATE_NOOP("", "Error: Invalid payload returned from node"); + case TemplateError::RequestFailed: return QT_TRANSLATE_NOOP("", "Error: Pubsub request failed"); + case TemplateError::NoError: return ""; + } + return ""; +} + +} diff --git a/Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.h b/Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.h new file mode 100644 index 0000000..572f910 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include +#include + +#include + +namespace Swift { + + class Form; + + class FdpFormSubmitWindow { + + public: + enum class NodeError { + DomainNotFound, + NoFdpNodesInDomain, + NoError, + }; + enum class TemplateError { + CannotLocateForm, + InvalidPayload, + RequestFailed, + NoError, + }; + + virtual ~FdpFormSubmitWindow() {} + + virtual void show() = 0; + virtual void raise() = 0; + virtual void addNode(const std::string& node, const std::string& nodeName) = 0; + virtual void clearNodeData() = 0; + virtual void setFormData(const std::shared_ptr& form) = 0; + virtual void showNodePlaceholder(NodeError nodeError) = 0; + virtual void showFormPlaceholder(TemplateError templateError) = 0; + virtual void handleSubmitServerResponse(bool submissionSuccess) = 0; + + boost::signals2::signal onCloseEvent; + boost::signals2::signal onRequestPubSubNodeData; + boost::signals2::signal onRequestTemplateForm; + boost::signals2::signal&)> onSubmitForm; + + protected: + + std::string getNodeErrorText(NodeError nodeError) const; + std::string getTemplateErrorText(TemplateError templateError) const; + }; +} diff --git a/Swift/Controllers/UIInterfaces/FdpFormSubmitWindowFactory.h b/Swift/Controllers/UIInterfaces/FdpFormSubmitWindowFactory.h new file mode 100644 index 0000000..ef11eaf --- /dev/null +++ b/Swift/Controllers/UIInterfaces/FdpFormSubmitWindowFactory.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include + +namespace Swift { + class FdpFormSubmitWindow; + + class FdpFormSubmitWindowFactory { + public: + virtual ~FdpFormSubmitWindowFactory() {} + virtual std::unique_ptr createFdpFormSubmitWindow() = 0; + }; +} diff --git a/Swift/Controllers/UIInterfaces/MainWindow.h b/Swift/Controllers/UIInterfaces/MainWindow.h index bfd8c67..e4b4657 100644 --- a/Swift/Controllers/UIInterfaces/MainWindow.h +++ b/Swift/Controllers/UIInterfaces/MainWindow.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -13,12 +13,12 @@ #include #include -#include #include #include namespace Swift { + class JID; class Roster; class MainWindow { diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h index a0976dc..c49364a 100644 --- a/Swift/Controllers/UIInterfaces/UIFactory.h +++ b/Swift/Controllers/UIInterfaces/UIFactory.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,8 @@ namespace Swift { public FileTransferListWidgetFactory, public WhiteboardWindowFactory, public HighlightEditorWindowFactory, - public BlockListEditorWidgetFactory { + public BlockListEditorWidgetFactory, + public FdpFormSubmitWindowFactory { public: virtual ~UIFactory() {} }; diff --git a/Swift/Controllers/UnitTest/FdpFormSubmitControllerTest.cpp b/Swift/Controllers/UnitTest/FdpFormSubmitControllerTest.cpp new file mode 100644 index 0000000..8dc3442 --- /dev/null +++ b/Swift/Controllers/UnitTest/FdpFormSubmitControllerTest.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Swift { + +class FdpFormSubmitControllerTest : public ::testing::Test { + + protected: + void SetUp() { + clientJid_ = JID(clientJIDString_); + stanzaChannel_ = std::make_unique(); + iqRouter_ = std::make_unique(stanzaChannel_.get()); + uiEventStream_ = std::make_unique(); + fdpWindowFactory_ = std::make_unique(); + fdpController_ = std::make_unique(clientJid_, iqRouter_.get(), uiEventStream_.get(), fdpWindowFactory_.get()); + auto fdpWindowOpenUIEvent = std::make_shared(); + uiEventStream_->send(fdpWindowOpenUIEvent); + fdpWindow_ = fdpWindowFactory_->getMockFdpFormSubmitWindow(); + } + + void TearDown() { + } + + std::shared_ptr createDiscoItemsResult(); + std::shared_ptr createTemplatePubSubResult(const std::shared_ptr& form); + std::shared_ptr createSubmittedPubSubResult(); + + std::string clientJIDString_ = "testjid@test.im/swift"; + JID clientJid_; + std::unique_ptr stanzaChannel_; + std::unique_ptr iqRouter_; + std::unique_ptr uiEventStream_; + std::unique_ptr fdpWindowFactory_; + std::unique_ptr fdpController_; + MockFdpFormSubmitWindow* fdpWindow_; +}; + +std::shared_ptr FdpFormSubmitControllerTest::createDiscoItemsResult() { + auto discoItems = std::make_shared(); + discoItems->addItem(DiscoItems::Item("node0", JID(), "fdp/template/testNode0")); + discoItems->addItem(DiscoItems::Item("node1", JID(), "fdp/template/testNode1")); + discoItems->addItem(DiscoItems::Item("node2", JID(), "fdp/template/testNode2")); + return discoItems; +} + +std::shared_ptr FdpFormSubmitControllerTest::createTemplatePubSubResult(const std::shared_ptr& form) { + auto pubSubItem = std::make_shared(); + pubSubItem->addData(form); + auto pubSubItems = std::make_shared(); + pubSubItems->addItem(pubSubItem); + auto pubSub = std::make_shared(); + pubSub->setPayload(pubSubItems); + return pubSub; +} + +std::shared_ptr FdpFormSubmitControllerTest::createSubmittedPubSubResult() { + auto pubSubItem = std::make_shared(); + pubSubItem->setID("testID"); + auto pubSubPublish = std::make_shared(); + pubSubPublish->addItem(pubSubItem); + pubSubPublish->setNode("fdp/submitted/test"); + auto pubSub = std::make_shared(); + pubSub->setPayload(pubSubPublish); + return pubSub; +} + +TEST_F(FdpFormSubmitControllerTest, testRequestPubSubNodeData) { + std::string domainName = "fdp.example.test"; + fdpWindow_->onRequestPubSubNodeData(domainName); + ASSERT_EQ(1, stanzaChannel_->sentStanzas.size()); + auto requestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); + ASSERT_EQ(domainName, requestIq->getTo()); + auto discoItemsPayloads = requestIq->getPayloads(); + ASSERT_EQ(1, discoItemsPayloads.size()); + + auto discoItemsResult = createDiscoItemsResult(); + auto resultIq = IQ::createResult(clientJid_, domainName, requestIq->getID(), discoItemsResult); + stanzaChannel_->onIQReceived(resultIq); + + auto discoItemsList = discoItemsResult->getItems(); + ASSERT_EQ(discoItemsList.size(), fdpWindow_->nodeData.size()); + for (unsigned int i = 0; i < fdpWindow_->nodeData.size(); i++) { + auto nodeItem = fdpWindow_->nodeData[i]; + auto discoItem = discoItemsList[i]; + ASSERT_EQ(discoItem.getNode(), nodeItem.first); + ASSERT_EQ(discoItem.getName(), nodeItem.second); + } + ASSERT_EQ(FdpFormSubmitWindow::NodeError::NoError, fdpWindow_->nodeError_); +} + +TEST_F(FdpFormSubmitControllerTest, testRequestPubSubNodeDataError) { + std::string domainName = "fdp.example.test"; + fdpWindow_->onRequestPubSubNodeData(domainName); + ASSERT_EQ(1, stanzaChannel_->sentStanzas.size()); + auto requestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); + ASSERT_EQ(domainName, requestIq->getTo()); + + auto resultIq = IQ::createError(clientJid_, domainName, requestIq->getID()); + stanzaChannel_->onIQReceived(resultIq); + + ASSERT_EQ(true, fdpWindow_->nodeData.empty()); + ASSERT_EQ(FdpFormSubmitWindow::NodeError::DomainNotFound, fdpWindow_->nodeError_); +} + +TEST_F(FdpFormSubmitControllerTest, testRequestTemplateForm) { + std::string domainName = "fdp.example.test"; + fdpWindow_->onRequestPubSubNodeData(domainName); + auto nodeDataRequestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); + auto discoItemsResult = createDiscoItemsResult(); + auto discoItemsResultIq = IQ::createResult(clientJid_, domainName, nodeDataRequestIq->getID(), discoItemsResult); + stanzaChannel_->onIQReceived(discoItemsResultIq); + + std::string templateNodeName = "fdp/template/test"; + fdpWindow_->onRequestTemplateForm(templateNodeName); + ASSERT_EQ(2, stanzaChannel_->sentStanzas.size()); + auto requestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[1]); + ASSERT_EQ(domainName, requestIq->getTo()); + auto pubSubPayloads = requestIq->getPayloads(); + ASSERT_EQ(1, pubSubPayloads.size()); + + std::string value0("value0"); + std::string value1("value1@example.test"); + auto field0 = std::make_shared(FormField::TextSingleType, value0); + auto field1 = std::make_shared(FormField::JIDSingleType, value1); + auto form = std::make_shared(); + form->addField(field0); + form->addField(field1); + auto pubSubResult = createTemplatePubSubResult(form); + auto resultIq = IQ::createResult(clientJid_, domainName, requestIq->getID(), pubSubResult); + stanzaChannel_->onIQReceived(resultIq); + + ASSERT_EQ(form, fdpWindow_->templateForm_); + auto fields = fdpWindow_->templateForm_->getFields(); + ASSERT_EQ(2, fields.size()); + ASSERT_EQ(field0, fields[0]); + ASSERT_EQ(field1, fields[1]); + ASSERT_EQ(value0, fields[0]->getTextSingleValue()); + ASSERT_EQ(value1, fields[1]->getJIDSingleValue()); + ASSERT_EQ(FdpFormSubmitWindow::TemplateError::NoError, fdpWindow_->templateError_); +} + +TEST_F(FdpFormSubmitControllerTest, testRequestTemplateFormError) { + std::string domainName = "fdp.example.test"; + fdpWindow_->onRequestPubSubNodeData(domainName); + auto nodeDataRequestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); + auto discoItemsResult = createDiscoItemsResult(); + auto discoItemsResultIq = IQ::createResult(clientJid_, domainName, nodeDataRequestIq->getID(), discoItemsResult); + stanzaChannel_->onIQReceived(discoItemsResultIq); + + std::string templateNodeName = "fdp/template/test"; + fdpWindow_->onRequestTemplateForm(templateNodeName); + ASSERT_EQ(2, stanzaChannel_->sentStanzas.size()); + auto requestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[1]); + ASSERT_EQ(domainName, requestIq->getTo()); + + auto resultIq = IQ::createError(clientJid_, domainName, requestIq->getID()); + stanzaChannel_->onIQReceived(resultIq); + + ASSERT_EQ(nullptr, fdpWindow_->templateForm_); + ASSERT_EQ(FdpFormSubmitWindow::TemplateError::RequestFailed, fdpWindow_->templateError_); +} + +TEST_F(FdpFormSubmitControllerTest, testSubmitForm) { + std::string domainName = "fdp.example.test"; + std::string templateNodeName = "fdp/template/test"; + fdpWindow_->onRequestPubSubNodeData(domainName); + auto nodeDataRequestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); + auto discoItemsResult = createDiscoItemsResult(); + auto discoItemsResultIq = IQ::createResult(clientJid_, domainName, nodeDataRequestIq->getID(), discoItemsResult); + stanzaChannel_->onIQReceived(discoItemsResultIq); + fdpWindow_->onRequestTemplateForm(templateNodeName); + auto templateFormRequestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[1]); + auto templateForm = std::make_shared(); + auto templatePubSubResult = createTemplatePubSubResult(templateForm); + auto templatePubSubResultIq = IQ::createResult(clientJid_, domainName, templateFormRequestIq->getID(), templatePubSubResult); + stanzaChannel_->onIQReceived(templatePubSubResultIq); + + std::string value0("value0"); + std::string value1("value1@example.test"); + auto field0 = std::make_shared(FormField::TextSingleType, value0); + auto field1 = std::make_shared(FormField::JIDSingleType, value1); + auto form = std::make_shared(); + form->addField(field0); + form->addField(field1); + fdpWindow_->onSubmitForm(form); + + ASSERT_EQ(3, stanzaChannel_->sentStanzas.size()); + auto requestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[2]); + ASSERT_EQ(domainName, requestIq->getTo()); + auto pubSubPayloads = requestIq->getPayloads(); + ASSERT_EQ(1, pubSubPayloads.size()); + auto pubSubPayload = pubSubPayloads[0]; + auto pubSubPublishPayload = std::dynamic_pointer_cast(pubSubPayload->getPayload()); + ASSERT_TRUE(pubSubPublishPayload); + auto pubSubItems = pubSubPublishPayload->getItems(); + ASSERT_EQ(1, pubSubItems.size()); + auto dataList = pubSubItems[0]->getData(); + ASSERT_EQ(1, dataList.size()); + auto submittedForm = std::dynamic_pointer_cast(dataList[0]); + ASSERT_TRUE(submittedForm); + ASSERT_EQ(form, submittedForm); + auto fields = submittedForm->getFields(); + ASSERT_EQ(2, fields.size()); + ASSERT_EQ(field0, fields[0]); + ASSERT_EQ(field1, fields[1]); + ASSERT_EQ(value0, fields[0]->getTextSingleValue()); + ASSERT_EQ(value1, fields[1]->getJIDSingleValue()); + + auto pubSubResult = createSubmittedPubSubResult(); + auto resultIq = IQ::createResult(clientJid_, domainName, requestIq->getID(), pubSubResult); + stanzaChannel_->onIQReceived(resultIq); + + ASSERT_EQ(true, fdpWindow_->submissionSuccess_); +} + +TEST_F(FdpFormSubmitControllerTest, testSubmitFormError) { + std::string domainName = "fdp.example.test"; + std::string templateNodeName = "fdp/template/test"; + fdpWindow_->onRequestPubSubNodeData(domainName); + auto nodeDataRequestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); + auto discoItemsResult = createDiscoItemsResult(); + auto discoItemsResultIq = IQ::createResult(clientJid_, domainName, nodeDataRequestIq->getID(), discoItemsResult); + stanzaChannel_->onIQReceived(discoItemsResultIq); + fdpWindow_->onRequestTemplateForm(templateNodeName); + auto templateFormRequestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[1]); + auto templateForm = std::make_shared(); + auto templatePubSubResult = createTemplatePubSubResult(templateForm); + auto templatePubSubResultIq = IQ::createResult(clientJid_, domainName, templateFormRequestIq->getID(), templatePubSubResult); + stanzaChannel_->onIQReceived(templatePubSubResultIq); + + auto form = std::make_shared(); + fdpWindow_->onSubmitForm(form); + ASSERT_EQ(3, stanzaChannel_->sentStanzas.size()); + auto requestIq = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[2]); + ASSERT_EQ(domainName, requestIq->getTo()); + + auto resultIq = IQ::createError(clientJid_, domainName, requestIq->getID()); + stanzaChannel_->onIQReceived(resultIq); + + ASSERT_EQ(false, fdpWindow_->submissionSuccess_); +} + +} diff --git a/Swift/Controllers/UnitTest/MockFdpFormSubmitWindow.h b/Swift/Controllers/UnitTest/MockFdpFormSubmitWindow.h new file mode 100644 index 0000000..28ef35f --- /dev/null +++ b/Swift/Controllers/UnitTest/MockFdpFormSubmitWindow.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include +#include + +#include + +namespace Swift { + + class Form; + + class MockFdpFormSubmitWindow : public FdpFormSubmitWindow { + public: + MockFdpFormSubmitWindow() : FdpFormSubmitWindow() {} + virtual void show() override {} + virtual void raise() override {} + virtual void addNode(const std::string& node, const std::string& nodeName) override { nodeData.push_back(std::pair(node, nodeName)); } + virtual void clearNodeData() override { nodeData.clear(); } + virtual void setFormData(const std::shared_ptr& form) override { templateForm_ = form; } + virtual void showNodePlaceholder(NodeError nodeError) override { nodeError_ = nodeError; } + virtual void showFormPlaceholder(TemplateError templateError) override { templateError_ = templateError; } + virtual void handleSubmitServerResponse(bool submissionSuccess) override { submissionSuccess_ = submissionSuccess; } + + std::vector> nodeData; + std::shared_ptr templateForm_ = nullptr; + NodeError nodeError_ = NodeError::NoError; + TemplateError templateError_ = TemplateError::NoError; + bool submissionSuccess_ = false; + }; + +} diff --git a/Swift/Controllers/UnitTest/MockFdpFormSubmitWindowFactory.h b/Swift/Controllers/UnitTest/MockFdpFormSubmitWindowFactory.h new file mode 100644 index 0000000..8015419 --- /dev/null +++ b/Swift/Controllers/UnitTest/MockFdpFormSubmitWindowFactory.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include + +#include +#include + +namespace Swift { + + class MockFdpFormSubmitWindowFactory : public FdpFormSubmitWindowFactory { + public: + MockFdpFormSubmitWindowFactory() : FdpFormSubmitWindowFactory() {} + + virtual std::unique_ptr createFdpFormSubmitWindow() override { + std::unique_ptr fdpFormSubmitWindow = std::make_unique(); + mockFdpFormSubmitWindow_ = static_cast(fdpFormSubmitWindow.get()); + return fdpFormSubmitWindow; + } + + MockFdpFormSubmitWindow* getMockFdpFormSubmitWindow() { return mockFdpFormSubmitWindow_; } + + private: + MockFdpFormSubmitWindow* mockFdpFormSubmitWindow_; + }; +} diff --git a/Swift/Controllers/UnitTest/MockMainWindow.h b/Swift/Controllers/UnitTest/MockMainWindow.h index 6ae2aa7..9265310 100644 --- a/Swift/Controllers/UnitTest/MockMainWindow.h +++ b/Swift/Controllers/UnitTest/MockMainWindow.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -26,6 +26,7 @@ namespace Swift { virtual void setStreamEncryptionStatus(bool /*tlsInPlaceAndValid*/) {} virtual void openCertificateDialog(const std::vector& /*chain*/) {} virtual void setBlockingCommandAvailable(bool /*isAvailable*/) {} + virtual void openFdpFormSubmitDialog(const JID& /*self*/, IQRouter* /*iqRouter*/) {} Roster* roster; }; diff --git a/Swift/QtUI/QtFdpFormSubmitWindow.cpp b/Swift/QtUI/QtFdpFormSubmitWindow.cpp new file mode 100644 index 0000000..5719f87 --- /dev/null +++ b/Swift/QtUI/QtFdpFormSubmitWindow.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Swift { +QtFdpFormSubmitWindow::QtFdpFormSubmitWindow(QWidget* parent) : QDialog(parent), FdpFormSubmitWindow() { + layout_ = new QVBoxLayout(this); + subLayout_ = new QHBoxLayout(this); + + initNodeViewLayout(); + initFormLayout(); + subLayout_->addLayout(nodeViewLayout_); + subLayout_->addLayout(formLayout_); + subLayout_->setStretchFactor(nodeViewLayout_, 2); + subLayout_->setStretchFactor(formLayout_, 3); + layout_->addLayout(subLayout_); + + submitButton_ = new QPushButton(tr("Submit Form"), this); + submitButton_->setEnabled(false); + okButton_ = new QPushButton(tr("Ok"), this); + okButton_->hide(); + cancelButton_ = new QPushButton(tr("Cancel"), this); + connect(submitButton_, &QPushButton::clicked, this, &QtFdpFormSubmitWindow::handleSubmitClicked); + connect(okButton_, &QPushButton::clicked, this, &QWidget::close); + connect(cancelButton_, &QPushButton::clicked, this, &QWidget::close); + auto buttonSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); + buttonLayout_ = new QHBoxLayout(this); + buttonLayout_->addItem(buttonSpacer); + buttonLayout_->addWidget(submitButton_); + buttonLayout_->addWidget(cancelButton_); + layout_->addLayout(buttonLayout_); + + setMinimumWidth(800); + setMinimumHeight(600); + + this->setWindowTitle(tr("FDP Form Submission")); +} + +QtFdpFormSubmitWindow::~QtFdpFormSubmitWindow() { +} + +void QtFdpFormSubmitWindow::closeEvent(QCloseEvent* event) { + event->ignore(); + onCloseEvent(); +} + +void QtFdpFormSubmitWindow::initNodeViewLayout() { + nodeViewLayout_ = new QVBoxLayout(this); + auto domainSearchLayout = new QHBoxLayout(this); + pubSubDomainEdit_ = new QLineEdit(this); + loadDomainButton_ = new QPushButton(tr("Load"), this); + pubSubNodeView_ = new QtListWidget(this); + pubSubDomainEdit_->setPlaceholderText(tr("Enter pubsub domain here")); + pubSubNodeView_->setMinimumWidth(300); + connect(loadDomainButton_, &QPushButton::clicked, this, &QtFdpFormSubmitWindow::handleLoadDomainButtonClicked); + connect(pubSubNodeView_, &QListWidget::itemDoubleClicked, this, &QtFdpFormSubmitWindow::handlePubSubNodeViewItemDoubleClicked); + domainSearchLayout->addWidget(pubSubDomainEdit_); + domainSearchLayout->addWidget(loadDomainButton_); + nodeViewLayout_->addLayout(domainSearchLayout); + nodeViewLayout_->addWidget(pubSubNodeView_); +} + +void QtFdpFormSubmitWindow::initFormLayout() { + formPlaceholder_ = new QLabel(tr("No form loaded")); + formPlaceholder_->setAlignment(Qt::AlignCenter); + formPlaceholder_->setMinimumWidth(200); + formPlaceholder_->setStyleSheet("QLabel { color : #AAAAAA; }"); + formLayout_ = new QVBoxLayout(this); + formLayout_->addWidget(formPlaceholder_, Qt::AlignCenter); +} + +void QtFdpFormSubmitWindow::show() { + QDialog::show(); +} + +void QtFdpFormSubmitWindow::raise() { + QDialog::raise(); +} + +void QtFdpFormSubmitWindow::addNode(const std::string& node, const std::string& nodeName) { + auto listItem = new QListWidgetItem(P2QSTRING(nodeName)); + listItem->setData(Qt::UserRole, P2QSTRING(node)); + pubSubNodeView_->addItem(listItem); +} + +void QtFdpFormSubmitWindow::clearNodeData() { + pubSubNodeView_->clear(); + pubSubNodeView_->setWordWrap(false); + disconnect(pubSubNodeView_, &QtListWidget::onResize, this, &QtFdpFormSubmitWindow::handleNodeListResize); +} + +void QtFdpFormSubmitWindow::setFormData(const std::shared_ptr& form) { + if (formWidget_) { + formLayout_->removeWidget(formWidget_); + formWidget_->deleteLater(); + formWidget_ = nullptr; + } + else if (!formPlaceholder_->isHidden()) { + formLayout_->removeWidget(formPlaceholder_); + formPlaceholder_->hide(); + } + formWidget_ = new QtFormWidget(form); + formWidget_->setEditable(true); + formLayout_->addWidget(formWidget_); + + if (!okButton_->isHidden()) { + buttonLayout_->removeWidget(okButton_); + okButton_->hide(); + } + if (submitButton_->isHidden()) { + buttonLayout_->insertWidget(1, submitButton_); + submitButton_->show(); + } + submitButton_->setEnabled(true); + cancelButton_->setEnabled(true); +} + +void QtFdpFormSubmitWindow::showNodePlaceholder(NodeError nodeError) { + pubSubNodeView_->clear(); + pubSubNodeView_->setWordWrap(true); + auto listItem = new QListWidgetItem; + auto placeholderText = P2QSTRING(getNodeErrorText(nodeError)); + listItem->setText(placeholderText); + listItem->setTextAlignment(Qt::AlignCenter); + listItem->setFlags(Qt::NoItemFlags); + listItem->setSizeHint(QSize(listItem->sizeHint().width(), pubSubNodeView_->height())); + connect(pubSubNodeView_, &QtListWidget::onResize, this, &QtFdpFormSubmitWindow::handleNodeListResize); + pubSubNodeView_->addItem(listItem); +} + +void QtFdpFormSubmitWindow::showFormPlaceholder(TemplateError templateError) { + if (formWidget_) { + formLayout_->removeWidget(formWidget_); + formWidget_->deleteLater(); + formWidget_ = nullptr; + } + auto placeholderText = P2QSTRING(getTemplateErrorText(templateError)); + formPlaceholder_->setText(placeholderText); + if (formPlaceholder_->isHidden()) { + formLayout_->addWidget(formPlaceholder_, Qt::AlignCenter); + formPlaceholder_->show(); + } + + if (!okButton_->isHidden()) { + buttonLayout_->removeWidget(okButton_); + okButton_->hide(); + } + if (submitButton_->isHidden()) { + buttonLayout_->insertWidget(1, submitButton_); + submitButton_->show(); + } + submitButton_->setEnabled(false); + cancelButton_->setEnabled(true); +} + +void QtFdpFormSubmitWindow::handleLoadDomainButtonClicked() { + std::string domainUri = Q2PSTRING(pubSubDomainEdit_->text()); + onRequestPubSubNodeData(domainUri); +} + +void QtFdpFormSubmitWindow::handlePubSubListViewTemplateSelected(const std::string& nodeName) { + onRequestTemplateForm(nodeName); +} + +void QtFdpFormSubmitWindow::handlePubSubNodeViewItemDoubleClicked(QListWidgetItem* item) { + handlePubSubListViewTemplateSelected(Q2PSTRING(item->data(Qt::UserRole).toString())); +} + +void QtFdpFormSubmitWindow::handleSubmitClicked() { + auto form = formWidget_->getCompletedForm(); + formWidget_->setDisabled(true); + submitButton_->setEnabled(false); + onSubmitForm(form); +} + +void QtFdpFormSubmitWindow::handleSubmitServerResponse(bool submissionSuccess) { + if (submissionSuccess) { + if (!submitButton_->isHidden()) { + buttonLayout_->removeWidget(submitButton_); + submitButton_->hide(); + } + if (okButton_->isHidden()) { + buttonLayout_->insertWidget(1, okButton_); + okButton_->show(); + } + cancelButton_->setEnabled(false); + } + else { + formWidget_->setDisabled(false); + submitButton_->setEnabled(true); + } +} + +void QtFdpFormSubmitWindow::handleNodeListResize() { + auto placeholderItem = pubSubNodeView_->item(0); + placeholderItem->setSizeHint(QSize(placeholderItem->sizeHint().width(), pubSubNodeView_->height())); +} + +} diff --git a/Swift/QtUI/QtFdpFormSubmitWindow.h b/Swift/QtUI/QtFdpFormSubmitWindow.h new file mode 100644 index 0000000..c178a5b --- /dev/null +++ b/Swift/QtUI/QtFdpFormSubmitWindow.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include + +#include + +#include + +class QHBoxLayout; +class QLabel; +class QLineEdit; +class QListWidgetItem; +class QPushButton; +class QTextEdit; +class QVBoxLayout; + +namespace Swift { + + class Form; + class QtFormWidget; + class QtListWidget; + class QtPubSubNodeController; + + class QtFdpFormSubmitWindow : public QDialog, public FdpFormSubmitWindow { + Q_OBJECT + + public: + QtFdpFormSubmitWindow(QWidget* parent = nullptr); + virtual ~QtFdpFormSubmitWindow(); + + protected: + virtual void closeEvent(QCloseEvent* event) override; + + private: + void initNodeViewLayout(); + void initFormLayout(); + virtual void show() override; + virtual void raise() override; + virtual void addNode(const std::string& node, const std::string& nodeName) override; + virtual void clearNodeData() override; + virtual void setFormData(const std::shared_ptr& form) override; + virtual void showNodePlaceholder(NodeError nodeError) override; + virtual void showFormPlaceholder(TemplateError templateError) override; + virtual void handleSubmitServerResponse(bool submissionSuccess) override; + + private slots: + void handleLoadDomainButtonClicked(); + void handlePubSubListViewTemplateSelected(const std::string& nodeName); + void handlePubSubNodeViewItemDoubleClicked(QListWidgetItem* item); + void handleSubmitClicked(); + void handleNodeListResize(); + + private: + QVBoxLayout* layout_; + QHBoxLayout* subLayout_; + QHBoxLayout* buttonLayout_; + QVBoxLayout* nodeViewLayout_; + QVBoxLayout* formLayout_; + QLineEdit* pubSubDomainEdit_; + QPushButton* loadDomainButton_; + QtListWidget* pubSubNodeView_; + QLabel* formPlaceholder_; + QTextEdit* nodePlaceholderTextEdit_ = nullptr; + QtFormWidget* formWidget_ = nullptr; + QPushButton* cancelButton_; + QPushButton* submitButton_ = nullptr; + QPushButton* okButton_ = nullptr; + }; +} diff --git a/Swift/QtUI/QtListWidget.cpp b/Swift/QtUI/QtListWidget.cpp new file mode 100644 index 0000000..e35bbbb --- /dev/null +++ b/Swift/QtUI/QtListWidget.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include + +#include + +namespace Swift { + +QtListWidget::QtListWidget(QWidget* parent) : QListWidget(parent) { +} + +void QtListWidget::resizeEvent(QResizeEvent*) { + emit onResize(); +} + +} diff --git a/Swift/QtUI/QtListWidget.h b/Swift/QtUI/QtListWidget.h new file mode 100644 index 0000000..b7380c4 --- /dev/null +++ b/Swift/QtUI/QtListWidget.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include + +namespace Swift { + +class QtListWidget : public QListWidget { + Q_OBJECT + public: + QtListWidget(QWidget* parent = nullptr); + protected: + virtual void resizeEvent(QResizeEvent*); + signals: + void onResize(); +}; + +} diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 7eec8d1..92488ae 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -199,6 +200,13 @@ QtMainWindow::QtMainWindow(Chattables& chattables, SettingsProvider* settings, U } serverAdHocMenu_ = new QMenu(tr("Run Server Command"), this); actionsMenu->addMenu(serverAdHocMenu_); + if (settings_->getSetting(SettingConstants::FUTURE)) { + actionsMenu->addSeparator(); + submitFormAction_ = new QAction(tr("Submit Form"), this); + connect(submitFormAction_, &QAction::triggered, this, &QtMainWindow::handleSubmitFormActionTriggered); + actionsMenu->addAction(submitFormAction_); + onlineOnlyActions_ << submitFormAction_; + } actionsMenu->addSeparator(); QAction* signOutAction = new QAction(tr("&Sign Out"), this); connect(signOutAction, SIGNAL(triggered()), SLOT(handleSignOutAction())); @@ -437,4 +445,8 @@ void QtMainWindow::setBlockingCommandAvailable(bool isAvailable) { openBlockingListEditor_->setVisible(isAvailable); } +void QtMainWindow::handleSubmitFormActionTriggered() { + uiEventStream_->send(std::make_shared()); +} + } diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h index 0e7f290..b285831 100644 --- a/Swift/QtUI/QtMainWindow.h +++ b/Swift/QtUI/QtMainWindow.h @@ -81,6 +81,7 @@ namespace Swift { void handleShowCertificateInfo(); void handleEditBlockingList(); void handleSomethingSelectedChanged(bool itemSelected); + void handleSubmitFormActionTriggered(); private: Chattables& chattables_; @@ -110,5 +111,6 @@ namespace Swift { std::vector serverAdHocCommands_; QList serverAdHocCommandActions_; QList onlineOnlyActions_; + QAction* submitFormAction_; }; } diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index 2762d68..93fca5f 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -175,6 +176,10 @@ AdHocCommandWindow* QtUIFactory::createAdHocCommandWindow(std::shared_ptr QtUIFactory::createFdpFormSubmitWindow() { + return std::make_unique(); +} + void QtUIFactory::showTabs() { if (!tabs_->isVisible()) { tabs_->show(); diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h index ad8dc1f..04836fe 100644 --- a/Swift/QtUI/QtUIFactory.h +++ b/Swift/QtUI/QtUIFactory.h @@ -18,6 +18,7 @@ class QSplitter; namespace Swift { class AutoUpdater; class Chattables; + class FdpFormSubmitWindow; class QtChatTabs; class QtChatTheme; class QtChatWindow; @@ -54,6 +55,7 @@ namespace Swift { virtual HighlightEditorWindow* createHighlightEditorWindow(); virtual BlockListEditorWidget* createBlockListEditorWidget(); virtual AdHocCommandWindow* createAdHocCommandWindow(std::shared_ptr command); + virtual std::unique_ptr createFdpFormSubmitWindow(); private slots: void handleChatWindowFontResized(int); diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 535ccaf..a2ad9b1 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -177,6 +177,7 @@ sources = [ "QtEmojisSelector.cpp", "QtEmoticonsGrid.cpp", "QtExpandedListView.cpp", + "QtFdpFormSubmitWindow.cpp", "QtFileTransferListItemModel.cpp", "QtFileTransferListWidget.cpp", "QtFormResultItemModel.cpp", @@ -185,6 +186,7 @@ sources = [ "QtHistoryWindow.cpp", "QtJoinMUCWindow.cpp", "QtLineEdit.cpp", + "QtListWidget.cpp", "QtLoginWindow.cpp", "QtMainWindow.cpp", "QtMUCConfigurationWindow.cpp", diff --git a/Swiften/Elements/PubSubItem.cpp b/Swiften/Elements/PubSubItem.cpp index 4dc0907..b5f17cc 100644 --- a/Swiften/Elements/PubSubItem.cpp +++ b/Swiften/Elements/PubSubItem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Isode Limited. + * Copyright (c) 2013-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -11,5 +11,8 @@ using namespace Swift; PubSubItem::PubSubItem() { } +PubSubItem::PubSubItem(const std::string& id) : id_(id) { +} + PubSubItem::~PubSubItem() { } diff --git a/Swiften/Elements/PubSubItem.h b/Swiften/Elements/PubSubItem.h index ba13150..161b733 100644 --- a/Swiften/Elements/PubSubItem.h +++ b/Swiften/Elements/PubSubItem.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017 Isode Limited. + * Copyright (c) 2013-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -18,32 +18,32 @@ namespace Swift { public: PubSubItem(); + PubSubItem(const std::string& id); virtual ~PubSubItem(); const std::vector< std::shared_ptr >& getData() const { - return data; + return data_; } void setData(const std::vector< std::shared_ptr >& value) { - this->data = value ; + this->data_ = value ; } void addData(std::shared_ptr value) { - this->data.push_back(value); + this->data_.push_back(value); } const std::string& getID() const { - return id; + return id_; } void setID(const std::string& value) { - this->id = value ; + this->id_ = value ; } - private: - std::vector< std::shared_ptr > data; - std::string id; + std::vector< std::shared_ptr > data_; + std::string id_; }; } diff --git a/Swiften/Elements/PubSubItems.h b/Swiften/Elements/PubSubItems.h index c60adca..c8b7f53 100644 --- a/Swiften/Elements/PubSubItems.h +++ b/Swiften/Elements/PubSubItems.h @@ -61,7 +61,6 @@ namespace Swift { this->subscriptionID = value ; } - private: std::string node; std::vector< std::shared_ptr > items; -- cgit v0.10.2-6-g49f6