summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Burgess <pete.burgess@isode.com>2018-04-20 15:18:20 (GMT)
committerPeter Burgess <pete.burgess@isode.com>2018-07-24 15:05:11 (GMT)
commitd3bbd300be4480c0a3b7285c91b3b27e82ca9c2f (patch)
treea86f47b56647363141523a7182cd84cd2551ef62
parent5b4a0ba19ae6994db6a193a231fd2486bff6f0ec (diff)
downloadswift-d3bbd300be4480c0a3b7285c91b3b27e82ca9c2f.zip
swift-d3bbd300be4480c0a3b7285c91b3b27e82ca9c2f.tar.bz2
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
-rw-r--r--Swift/Controllers/AccountController.cpp5
-rw-r--r--Swift/Controllers/AccountController.h2
-rw-r--r--Swift/Controllers/FdpFormSubmitController.cpp145
-rw-r--r--Swift/Controllers/FdpFormSubmitController.h47
-rw-r--r--Swift/Controllers/SConscript3
-rw-r--r--Swift/Controllers/UIEvents/FdpFormSubmitWindowOpenUIEvent.h17
-rw-r--r--Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.cpp32
-rw-r--r--Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.h54
-rw-r--r--Swift/Controllers/UIInterfaces/FdpFormSubmitWindowFactory.h19
-rw-r--r--Swift/Controllers/UIInterfaces/MainWindow.h4
-rw-r--r--Swift/Controllers/UIInterfaces/UIFactory.h6
-rw-r--r--Swift/Controllers/UnitTest/FdpFormSubmitControllerTest.cpp269
-rw-r--r--Swift/Controllers/UnitTest/MockFdpFormSubmitWindow.h37
-rw-r--r--Swift/Controllers/UnitTest/MockFdpFormSubmitWindowFactory.h31
-rw-r--r--Swift/Controllers/UnitTest/MockMainWindow.h3
-rw-r--r--Swift/QtUI/QtFdpFormSubmitWindow.cpp215
-rw-r--r--Swift/QtUI/QtFdpFormSubmitWindow.h75
-rw-r--r--Swift/QtUI/QtListWidget.cpp20
-rw-r--r--Swift/QtUI/QtListWidget.h23
-rw-r--r--Swift/QtUI/QtMainWindow.cpp12
-rw-r--r--Swift/QtUI/QtMainWindow.h2
-rw-r--r--Swift/QtUI/QtUIFactory.cpp5
-rw-r--r--Swift/QtUI/QtUIFactory.h2
-rw-r--r--Swift/QtUI/SConscript2
-rw-r--r--Swiften/Elements/PubSubItem.cpp5
-rw-r--r--Swiften/Elements/PubSubItem.h18
-rw-r--r--Swiften/Elements/PubSubItems.h1
27 files changed, 1037 insertions, 17 deletions
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 <Swift/Controllers/ContactsFromXMPPRoster.h>
#include <Swift/Controllers/EventNotifier.h>
#include <Swift/Controllers/EventWindowController.h>
+#include <Swift/Controllers/FdpFormSubmitController.h>
#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
#include <Swift/Controllers/FileTransferListController.h>
#include <Swift/Controllers/Highlighting/HighlightEditorController.h>
@@ -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<StatusShow::Type>(profileSettings_->getIntSetting("lastShow", StatusShow::Online)));
presence->setStatus(profileSettings_->getStringSetting("lastStatus"));
statusTracker_->setRequestedPresence(presence);
+ fdpFormSubmitController_ = std::make_unique<FdpFormSubmitController>(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<std::string, std::string> emoticons_;
boost::signals2::connection enableCarbonsRequestHandlerConnection_;
+ std::unique_ptr<FdpFormSubmitController> 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 <Swift/Controllers/FdpFormSubmitController.h>
+
+#include <Swiften/Disco/GetDiscoItemsRequest.h>
+#include <Swiften/Elements/DiscoItems.h>
+#include <Swiften/Queries/IQRouter.h>
+#include <Swiften/Queries/PubSubRequest.h>
+
+#include <Swift/Controllers/UIEvents/FdpFormSubmitWindowOpenUIEvent.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.h>
+#include <Swift/Controllers/UIInterfaces/FdpFormSubmitWindowFactory.h>
+
+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>& uiEvent){ handleUIEvent(uiEvent); });
+}
+
+FdpFormSubmitController::~FdpFormSubmitController() {
+}
+
+void FdpFormSubmitController::handleUIEvent(const std::shared_ptr<UIEvent>& uiEvent) {
+ if (auto openEvent = std::dynamic_pointer_cast<FdpFormSubmitWindowOpenUIEvent>(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>& 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> 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<PubSubItems>(nodeName);
+ pubSubItems->setMaximumItems(1);
+ auto formRequest = std::make_shared<PubSubRequest<PubSubItems>>(IQ::Type::Get, selfJID_, currentDomain_, pubSubItems, iqRouter_);
+
+ formRequest->onResponse.connect(
+ [this, nodeName](std::shared_ptr<PubSubItems> 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<Form>(payloadList[0])) {
+ currentTemplateNode_ = nodeName;
+ formSubmitWindow_->setFormData(form);
+ }
+ else {
+ formSubmitWindow_->showFormPlaceholder(FdpFormSubmitWindow::TemplateError::InvalidPayload);
+ return;
+ }
+ }
+ );
+
+ formRequest->send();
+}
+
+void FdpFormSubmitController::submitForm(const std::shared_ptr<Form>& form) {
+ std::string submittedNode = currentTemplateNode_;
+ submittedNode.replace(submittedNode.find("/template/", 0), 10, "/submitted/");
+ auto pubSubItem = std::make_shared<PubSubItem>();
+ auto pubSubItems = std::make_shared<PubSubItems>(submittedNode);
+ auto pubSubPublish = std::make_shared<PubSubPublish>();
+ pubSubPublish->setNode(submittedNode);
+ pubSubPublish->addItem(pubSubItem);
+ pubSubItem->addData(form);
+ pubSubItems->addItem(pubSubItem);
+ auto formRequest = std::make_shared<PubSubRequest<PubSubPublish>>(IQ::Type::Set, selfJID_, currentDomain_, pubSubPublish, iqRouter_);
+
+ formRequest->onResponse.connect(
+ [this](std::shared_ptr<PubSubPublish> 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 <memory>
+#include <string>
+
+#include <boost/signals2.hpp>
+
+#include <Swiften/JID/JID.h>
+
+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>& uiEvent);
+ void createFormSubmitWindow();
+ void closeFormSubmitWindow();
+ void requestPubSubNodeData(const std::string& domainName);
+ void requestTemplateForm(const std::string& nodeName);
+ void submitForm(const std::shared_ptr<Form>& form);
+
+ JID selfJID_;
+ IQRouter* iqRouter_;
+ UIEventStream* uiEventStream_;
+ FdpFormSubmitWindowFactory* factory_;
+ std::unique_ptr<FdpFormSubmitWindow> 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 <Swift/Controllers/UIEvents/UIEvent.h>
+
+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 <Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.h>
+
+#include <Swift/Controllers/Intl.h>
+
+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 <memory>
+#include <string>
+
+#include <boost/signals2.hpp>
+
+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>& form) = 0;
+ virtual void showNodePlaceholder(NodeError nodeError) = 0;
+ virtual void showFormPlaceholder(TemplateError templateError) = 0;
+ virtual void handleSubmitServerResponse(bool submissionSuccess) = 0;
+
+ boost::signals2::signal<void ()> onCloseEvent;
+ boost::signals2::signal<void (const std::string&)> onRequestPubSubNodeData;
+ boost::signals2::signal<void (const std::string&)> onRequestTemplateForm;
+ boost::signals2::signal<void (const std::shared_ptr<Form>&)> 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 <memory>
+
+namespace Swift {
+ class FdpFormSubmitWindow;
+
+ class FdpFormSubmitWindowFactory {
+ public:
+ virtual ~FdpFormSubmitWindowFactory() {}
+ virtual std::unique_ptr<FdpFormSubmitWindow> 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 <Swiften/Elements/DiscoItems.h>
#include <Swiften/Elements/StatusShow.h>
-#include <Swiften/JID/JID.h>
#include <Swiften/TLS/Certificate.h>
#include <Swift/Controllers/Roster/ContactRosterItem.h>
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 <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/EventWindowFactory.h>
+#include <Swift/Controllers/UIInterfaces/FdpFormSubmitWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h>
#include <Swift/Controllers/UIInterfaces/HighlightEditorWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/HistoryWindowFactory.h>
@@ -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 <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <Swiften/Client/DummyStanzaChannel.h>
+#include <Swiften/Elements/DiscoItems.h>
+#include <Swiften/Elements/Form.h>
+#include <Swiften/Elements/FormField.h>
+#include <Swiften/Elements/IQ.h>
+#include <Swiften/Elements/Payload.h>
+#include <Swiften/Elements/PubSub.h>
+#include <Swiften/Elements/PubSubItems.h>
+#include <Swiften/Elements/PubSubPublish.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Queries/IQRouter.h>
+
+#include <Swift/Controllers/FdpFormSubmitController.h>
+#include <Swift/Controllers/UIEvents/FdpFormSubmitWindowOpenUIEvent.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UnitTest/MockFdpFormSubmitWindowFactory.h>
+#include <Swift/Controllers/UnitTest/MockFdpFormSubmitWindow.h>
+
+namespace Swift {
+
+class FdpFormSubmitControllerTest : public ::testing::Test {
+
+ protected:
+ void SetUp() {
+ clientJid_ = JID(clientJIDString_);
+ stanzaChannel_ = std::make_unique<DummyStanzaChannel>();
+ iqRouter_ = std::make_unique<IQRouter>(stanzaChannel_.get());
+ uiEventStream_ = std::make_unique<UIEventStream>();
+ fdpWindowFactory_ = std::make_unique<MockFdpFormSubmitWindowFactory>();
+ fdpController_ = std::make_unique<FdpFormSubmitController>(clientJid_, iqRouter_.get(), uiEventStream_.get(), fdpWindowFactory_.get());
+ auto fdpWindowOpenUIEvent = std::make_shared<FdpFormSubmitWindowOpenUIEvent>();
+ uiEventStream_->send(fdpWindowOpenUIEvent);
+ fdpWindow_ = fdpWindowFactory_->getMockFdpFormSubmitWindow();
+ }
+
+ void TearDown() {
+ }
+
+ std::shared_ptr<DiscoItems> createDiscoItemsResult();
+ std::shared_ptr<PubSub> createTemplatePubSubResult(const std::shared_ptr<Form>& form);
+ std::shared_ptr<PubSub> createSubmittedPubSubResult();
+
+ std::string clientJIDString_ = "testjid@test.im/swift";
+ JID clientJid_;
+ std::unique_ptr<DummyStanzaChannel> stanzaChannel_;
+ std::unique_ptr<IQRouter> iqRouter_;
+ std::unique_ptr<UIEventStream> uiEventStream_;
+ std::unique_ptr<MockFdpFormSubmitWindowFactory> fdpWindowFactory_;
+ std::unique_ptr<FdpFormSubmitController> fdpController_;
+ MockFdpFormSubmitWindow* fdpWindow_;
+};
+
+std::shared_ptr<DiscoItems> FdpFormSubmitControllerTest::createDiscoItemsResult() {
+ auto discoItems = std::make_shared<DiscoItems>();
+ 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<PubSub> FdpFormSubmitControllerTest::createTemplatePubSubResult(const std::shared_ptr<Form>& form) {
+ auto pubSubItem = std::make_shared<PubSubItem>();
+ pubSubItem->addData(form);
+ auto pubSubItems = std::make_shared<PubSubItems>();
+ pubSubItems->addItem(pubSubItem);
+ auto pubSub = std::make_shared<PubSub>();
+ pubSub->setPayload(pubSubItems);
+ return pubSub;
+}
+
+std::shared_ptr<PubSub> FdpFormSubmitControllerTest::createSubmittedPubSubResult() {
+ auto pubSubItem = std::make_shared<PubSubItem>();
+ pubSubItem->setID("testID");
+ auto pubSubPublish = std::make_shared<PubSubPublish>();
+ pubSubPublish->addItem(pubSubItem);
+ pubSubPublish->setNode("fdp/submitted/test");
+ auto pubSub = std::make_shared<PubSub>();
+ 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<IQ>(stanzaChannel_->sentStanzas[0]);
+ ASSERT_EQ(domainName, requestIq->getTo());
+ auto discoItemsPayloads = requestIq->getPayloads<DiscoItems>();
+ 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<IQ>(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<IQ>(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<IQ>(stanzaChannel_->sentStanzas[1]);
+ ASSERT_EQ(domainName, requestIq->getTo());
+ auto pubSubPayloads = requestIq->getPayloads<PubSub>();
+ ASSERT_EQ(1, pubSubPayloads.size());
+
+ std::string value0("value0");
+ std::string value1("value1@example.test");
+ auto field0 = std::make_shared<FormField>(FormField::TextSingleType, value0);
+ auto field1 = std::make_shared<FormField>(FormField::JIDSingleType, value1);
+ auto form = std::make_shared<Form>();
+ 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<IQ>(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<IQ>(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<IQ>(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<IQ>(stanzaChannel_->sentStanzas[1]);
+ auto templateForm = std::make_shared<Form>();
+ 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>(FormField::TextSingleType, value0);
+ auto field1 = std::make_shared<FormField>(FormField::JIDSingleType, value1);
+ auto form = std::make_shared<Form>();
+ form->addField(field0);
+ form->addField(field1);
+ fdpWindow_->onSubmitForm(form);
+
+ ASSERT_EQ(3, stanzaChannel_->sentStanzas.size());
+ auto requestIq = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[2]);
+ ASSERT_EQ(domainName, requestIq->getTo());
+ auto pubSubPayloads = requestIq->getPayloads<PubSub>();
+ ASSERT_EQ(1, pubSubPayloads.size());
+ auto pubSubPayload = pubSubPayloads[0];
+ auto pubSubPublishPayload = std::dynamic_pointer_cast<PubSubPublish>(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<Form>(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<IQ>(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<IQ>(stanzaChannel_->sentStanzas[1]);
+ auto templateForm = std::make_shared<Form>();
+ auto templatePubSubResult = createTemplatePubSubResult(templateForm);
+ auto templatePubSubResultIq = IQ::createResult(clientJid_, domainName, templateFormRequestIq->getID(), templatePubSubResult);
+ stanzaChannel_->onIQReceived(templatePubSubResultIq);
+
+ auto form = std::make_shared<Form>();
+ fdpWindow_->onSubmitForm(form);
+ ASSERT_EQ(3, stanzaChannel_->sentStanzas.size());
+ auto requestIq = std::dynamic_pointer_cast<IQ>(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 <memory>
+#include <string>
+
+#include <Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.h>
+
+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<std::string, std::string>(node, nodeName)); }
+ virtual void clearNodeData() override { nodeData.clear(); }
+ virtual void setFormData(const std::shared_ptr<Form>& 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<std::pair<std::string, std::string>> nodeData;
+ std::shared_ptr<Form> 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 <memory>
+
+#include <Swift/Controllers/UIInterfaces/FdpFormSubmitWindowFactory.h>
+#include <Swift/Controllers/UnitTest/MockFdpFormSubmitWindow.h>
+
+namespace Swift {
+
+ class MockFdpFormSubmitWindowFactory : public FdpFormSubmitWindowFactory {
+ public:
+ MockFdpFormSubmitWindowFactory() : FdpFormSubmitWindowFactory() {}
+
+ virtual std::unique_ptr<FdpFormSubmitWindow> createFdpFormSubmitWindow() override {
+ std::unique_ptr<FdpFormSubmitWindow> fdpFormSubmitWindow = std::make_unique<MockFdpFormSubmitWindow>();
+ mockFdpFormSubmitWindow_ = static_cast<MockFdpFormSubmitWindow*>(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<Certificate::ref>& /*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 <Swift/QtUI/QtFdpFormSubmitWindow.h>
+
+#include <QCloseEvent>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QSpacerItem>
+#include <QVBoxLayout>
+
+#include <Swift/QtUI/QtFormWidget.h>
+#include <Swift/QtUI/QtListWidget.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+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>& 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 <memory>
+
+#include <QDialog>
+
+#include <Swift/Controllers/UIInterfaces/FdpFormSubmitWindow.h>
+
+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>& 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 <Swift/QtUI/QtListWidget.h>
+
+#include <QListWidget>
+
+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 <QListWidget>
+
+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 <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/FdpFormSubmitWindowOpenUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h>
@@ -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<FdpFormSubmitWindowOpenUIEvent>());
+}
+
}
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<DiscoItems::Item> serverAdHocCommands_;
QList<QAction*> serverAdHocCommandActions_;
QList<QAction*> 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 <Swift/QtUI/QtChatWindow.h>
#include <Swift/QtUI/QtChatWindowFactory.h>
#include <Swift/QtUI/QtContactEditWindow.h>
+#include <Swift/QtUI/QtFdpFormSubmitWindow.h>
#include <Swift/QtUI/QtFileTransferListWidget.h>
#include <Swift/QtUI/QtHighlightNotificationConfigDialog.h>
#include <Swift/QtUI/QtHistoryWindow.h>
@@ -175,6 +176,10 @@ AdHocCommandWindow* QtUIFactory::createAdHocCommandWindow(std::shared_ptr<Outgoi
return new QtAdHocCommandWindow(command);
}
+std::unique_ptr<FdpFormSubmitWindow> QtUIFactory::createFdpFormSubmitWindow() {
+ return std::make_unique<QtFdpFormSubmitWindow>();
+}
+
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<OutgoingAdHocCommandSession> command);
+ virtual std::unique_ptr<FdpFormSubmitWindow> 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<Payload> >& getData() const {
- return data;
+ return data_;
}
void setData(const std::vector< std::shared_ptr<Payload> >& value) {
- this->data = value ;
+ this->data_ = value ;
}
void addData(std::shared_ptr<Payload> 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<Payload> > data;
- std::string id;
+ std::vector< std::shared_ptr<Payload> > 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<PubSubItem> > items;