summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2012-01-06 22:18:25 (GMT)
committerKevin Smith <git@kismith.co.uk>2012-01-23 14:51:26 (GMT)
commit83afa3d4bf18e4feb8a33d084ed181508084fc64 (patch)
treeb81a0c6eaaedbad3f3e607ea52a69acf98f95eff
parent093d499945d779cfed92b45e413644834004b0d9 (diff)
downloadswift-83afa3d4bf18e4feb8a33d084ed181508084fc64.zip
swift-83afa3d4bf18e4feb8a33d084ed181508084fc64.tar.bz2
XEP-0004 form support for user search.
License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php
-rw-r--r--Swift/Controllers/Chat/UserSearchController.cpp26
-rw-r--r--Swift/Controllers/UIInterfaces/UserSearchWindow.h1
-rw-r--r--Swift/QtUI/QtFormResultItemModel.cpp87
-rw-r--r--Swift/QtUI/QtFormResultItemModel.h36
-rw-r--r--Swift/QtUI/SConscript1
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchFieldsPage.cpp23
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchFieldsPage.h9
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchFieldsPage.ui39
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchResultsPage.ui6
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchWindow.cpp102
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchWindow.h4
-rw-r--r--Swiften/Elements/Form.cpp15
-rw-r--r--Swiften/Elements/Form.h8
-rw-r--r--Swiften/Parser/PayloadParsers/FormParser.cpp61
-rw-r--r--Swiften/Parser/PayloadParsers/FormParser.h4
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp67
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp110
-rw-r--r--Swiften/Serializer/PayloadSerializers/FormSerializer.cpp23
-rw-r--r--Swiften/Serializer/PayloadSerializers/FormSerializer.h2
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp95
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/SearchPayloadSerializerTest.cpp100
21 files changed, 743 insertions, 76 deletions
diff --git a/Swift/Controllers/Chat/UserSearchController.cpp b/Swift/Controllers/Chat/UserSearchController.cpp
index 3e734df..d90a3f8 100644
--- a/Swift/Controllers/Chat/UserSearchController.cpp
+++ b/Swift/Controllers/Chat/UserSearchController.cpp
@@ -123,18 +123,24 @@ void UserSearchController::handleSearchResponse(boost::shared_ptr<SearchPayload>
window_->setSearchError(true);
return;
}
+
std::vector<UserSearchResult> results;
- foreach (SearchPayload::Item item, resultsPayload->getItems()) {
- JID jid(item.jid);
- std::map<std::string, std::string> fields;
- fields["first"] = item.first;
- fields["last"] = item.last;
- fields["nick"] = item.nick;
- fields["email"] = item.email;
- UserSearchResult result(jid, fields);
- results.push_back(result);
+
+ if (resultsPayload->getForm()) {
+ window_->setResultsForm(resultsPayload->getForm());
+ } else {
+ foreach (SearchPayload::Item item, resultsPayload->getItems()) {
+ JID jid(item.jid);
+ std::map<std::string, std::string> fields;
+ fields["first"] = item.first;
+ fields["last"] = item.last;
+ fields["nick"] = item.nick;
+ fields["email"] = item.email;
+ UserSearchResult result(jid, fields);
+ results.push_back(result);
+ }
+ window_->setResults(results);
}
- window_->setResults(results);
}
void UserSearchController::handleDiscoWalkFinished() {
diff --git a/Swift/Controllers/UIInterfaces/UserSearchWindow.h b/Swift/Controllers/UIInterfaces/UserSearchWindow.h
index 0bb7400..0bfc509 100644
--- a/Swift/Controllers/UIInterfaces/UserSearchWindow.h
+++ b/Swift/Controllers/UIInterfaces/UserSearchWindow.h
@@ -23,6 +23,7 @@ namespace Swift {
virtual void clear() = 0;
virtual void setResults(const std::vector<UserSearchResult>& results) = 0;
+ virtual void setResultsForm(const Form::ref results) = 0;
virtual void addSavedServices(const std::vector<JID>& services) = 0;
virtual void setSelectedService(const JID& service) = 0;
virtual void setServerSupportsSearch(bool support) = 0;
diff --git a/Swift/QtUI/QtFormResultItemModel.cpp b/Swift/QtUI/QtFormResultItemModel.cpp
new file mode 100644
index 0000000..5461f05
--- /dev/null
+++ b/Swift/QtUI/QtFormResultItemModel.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtFormResultItemModel.h"
+
+#include <boost/algorithm/string/join.hpp>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swiften/Base/foreach.h>
+
+namespace Swift {
+
+QtFormResultItemModel::QtFormResultItemModel(QObject *parent) : QAbstractTableModel(parent) {
+
+}
+
+QtFormResultItemModel::QtFormResultItemModel(QObject* parent, Form::ref formResult) : QAbstractTableModel(parent), formResult_(formResult) {
+
+}
+
+void QtFormResultItemModel::setForm(Form::ref formResult) {
+ emit layoutAboutToBeChanged();
+ formResult_ = formResult;
+ emit layoutChanged();
+}
+
+const Form::ref QtFormResultItemModel::getForm() const {
+ return formResult_;
+}
+
+QVariant QtFormResultItemModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const {
+ if (!formResult_) return QVariant();
+ if (role != Qt::DisplayRole) return QVariant();
+ if (static_cast<size_t>(section) >= formResult_->getReportedFields().size()) return QVariant();
+ return QVariant(QString::fromStdString(formResult_->getReportedFields().at(section)->getLabel()));
+}
+
+int QtFormResultItemModel::rowCount(const QModelIndex &/*parent*/) const {
+ if (!formResult_) return 0;
+ return formResult_->getItems().size();
+}
+
+int QtFormResultItemModel::columnCount(const QModelIndex &/*parent*/) const {
+ if (!formResult_) return 0;
+ return formResult_->getReportedFields().size();
+}
+
+QVariant QtFormResultItemModel::data(const QModelIndex &index, int role) const {
+ if (!formResult_) return QVariant();
+ if (role != Qt::DisplayRole || !index.isValid()) {
+ return QVariant();
+ }
+
+ if (static_cast<size_t>(index.row()) >= formResult_->getItems().size()) return QVariant();
+ if (static_cast<size_t>(index.column()) >= formResult_->getReportedFields().size()) return QVariant();
+
+ Form::FormItem item = formResult_->getItems().at(index.row());
+
+ return QVariant(P2QSTRING(getFieldValue(item, index.column())));
+}
+
+const std::string QtFormResultItemModel::getFieldValue(const Form::FormItem& item, const int column) const {
+ // determine field name
+ std::string name = formResult_->getReportedFields().at(column)->getName();
+
+ foreach(FormField::ref field, item) {
+ if (field->getName() == name) {
+ std::string delimiter = "";
+ if (boost::dynamic_pointer_cast<TextMultiFormField>(field)) {
+ delimiter = "\n";
+ } else if (boost::dynamic_pointer_cast<JIDMultiFormField>(field)) {
+ delimiter = ", ";
+ } else if (boost::dynamic_pointer_cast<ListMultiFormField>(field)) {
+ delimiter = ", ";
+ }
+
+ return boost::algorithm::join(field->getRawValues(), delimiter);
+ }
+ }
+
+ return "";
+}
+
+}
diff --git a/Swift/QtUI/QtFormResultItemModel.h b/Swift/QtUI/QtFormResultItemModel.h
new file mode 100644
index 0000000..f383f74
--- /dev/null
+++ b/Swift/QtUI/QtFormResultItemModel.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QAbstractTableModel>
+
+#include <Swiften/Elements/Form.h>
+
+namespace Swift {
+
+class QtFormResultItemModel : public QAbstractTableModel {
+ Q_OBJECT
+ public:
+ QtFormResultItemModel(QObject* parent);
+ QtFormResultItemModel(QObject* parent, Form::ref formResult);
+
+ void setForm(Form::ref formResult);
+ const Form::ref getForm() const;
+
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ private:
+ const std::string getFieldValue(const Form::FormItem& item, const int column) const;
+
+ private:
+ Form::ref formResult_;
+};
+
+}
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 95ec49a..2283001 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -99,6 +99,7 @@ sources = [
"SystemMessageSnippet.cpp",
"QtElidingLabel.cpp",
"QtFormWidget.cpp",
+ "QtFormResultItemModel.cpp",
"QtLineEdit.cpp",
"QtJoinMUCWindow.cpp",
"Roster/RosterModel.cpp",
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.cpp b/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.cpp
index 3ef048b..13858a7 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.cpp
@@ -8,12 +8,31 @@
namespace Swift {
-QtUserSearchFieldsPage::QtUserSearchFieldsPage() {
+QtUserSearchFieldsPage::QtUserSearchFieldsPage() : formWidget_(0) {
setupUi(this);
}
bool QtUserSearchFieldsPage::isComplete() const {
- return nickInput_->isEnabled() || firstInput_->isEnabled() || lastInput_->isEnabled() || emailInput_->isEnabled();
+ if (formWidget_) {
+ return formWidget_->isEnabled();
+ } else {
+ return nickInput_->isEnabled() || firstInput_->isEnabled() || lastInput_->isEnabled() || emailInput_->isEnabled();
+ }
+}
+
+QtFormWidget* QtUserSearchFieldsPage::getFormWidget() {
+ return formWidget_;
+}
+
+void QtUserSearchFieldsPage::setFormWidget(QtFormWidget *widget) {
+ if (formWidget_) {
+ delete formWidget_;
+ formWidget_ = NULL;
+ }
+ if (widget) {
+ formContainer_->layout()->addWidget(widget);
+ }
+ formWidget_ = widget;
}
void QtUserSearchFieldsPage::emitCompletenessCheck() {
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.h b/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.h
index 12c28b5..066205b 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.h
@@ -8,6 +8,8 @@
#include <QWizardPage>
+#include <Swift/QtUI/QtFormWidget.h>
+
#include <Swift/QtUI/UserSearch/ui_QtUserSearchFieldsPage.h>
namespace Swift {
@@ -16,7 +18,14 @@ namespace Swift {
public:
QtUserSearchFieldsPage();
virtual bool isComplete() const;
+
+ QtFormWidget* getFormWidget();
+ void setFormWidget(QtFormWidget *widget);
+
public slots:
void emitCompletenessCheck();
+
+ private:
+ QtFormWidget *formWidget_;
};
}
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.ui b/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.ui
index fa6e376..f972516 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.ui
+++ b/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.ui
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
- <string></string>
+ <string/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
@@ -61,7 +61,30 @@
<item row="4" column="1">
<widget class="QLineEdit" name="emailInput_"/>
</item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="fetchingThrobber_">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
<item row="6" column="1">
+ <widget class="QLabel" name="fetchingLabel_">
+ <property name="text">
+ <string>Fetching search fields</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" colspan="2">
+ <widget class="QWidget" name="formContainer_" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </layout>
+ </widget>
+ </item>
+ <item row="7" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -74,20 +97,6 @@
</property>
</spacer>
</item>
- <item row="5" column="0">
- <widget class="QLabel" name="fetchingThrobber_">
- <property name="text">
- <string></string>
- </property>
- </widget>
- </item>
- <item row="5" column="1">
- <widget class="QLabel" name="fetchingLabel_">
- <property name="text">
- <string>Fetching search fields</string>
- </property>
- </widget>
- </item>
</layout>
</widget>
<resources/>
diff --git a/Swift/QtUI/UserSearch/QtUserSearchResultsPage.ui b/Swift/QtUI/UserSearch/QtUserSearchResultsPage.ui
index f312fde..ceb15e1 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchResultsPage.ui
+++ b/Swift/QtUI/UserSearch/QtUserSearchResultsPage.ui
@@ -11,10 +11,10 @@
</rect>
</property>
<property name="windowTitle">
- <string></string>
+ <string/>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
+ <layout class="QGridLayout" name="layout">
+ <item row="0" column="0">
<widget class="QTreeView" name="results_">
<property name="rootIsDecorated">
<bool>false</bool>
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
index 0559683..933612c 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
@@ -6,17 +6,20 @@
#include "Swift/QtUI/UserSearch/QtUserSearchWindow.h"
+#include <QItemDelegate>
#include <QModelIndex>
#include <QWizardPage>
#include <QMovie>
#include <boost/smart_ptr/make_shared.hpp>
+#include <Swiften/Base/foreach.h>
#include "Swift/Controllers/UIEvents/UIEventStream.h"
#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
#include "Swift/Controllers/UIEvents/AddContactUIEvent.h"
#include "Swift/QtUI/UserSearch/UserSearchModel.h"
#include "Swift/QtUI/UserSearch/UserSearchDelegate.h"
#include "Swift/QtUI/QtSwiftUtil.h"
+#include "Swift/QtUI/QtFormResultItemModel.h"
#include "QtUserSearchFirstPage.h"
#include "QtUserSearchFieldsPage.h"
#include "QtUserSearchResultsPage.h"
@@ -24,7 +27,7 @@
namespace Swift {
-QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type, const std::set<std::string>& groups) : eventStream_(eventStream), type_(type) {
+QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type, const std::set<std::string>& groups) : eventStream_(eventStream), type_(type), model_(NULL) {
setupUi(this);
#ifndef Q_WS_MAC
setWindowIcon(QIcon(":/logo-icon-16.png"));
@@ -32,7 +35,6 @@ QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWin
QString title(type == UserSearchWindow::AddContact ? tr("Add Contact") : tr("Chat to User"));
setWindowTitle(title);
- model_ = new UserSearchModel();
delegate_ = new UserSearchDelegate();
firstPage_ = new QtUserSearchFirstPage(type, title);
@@ -51,9 +53,7 @@ QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWin
setPage(2, fieldsPage_);
resultsPage_ = new QtUserSearchResultsPage();
- resultsPage_->results_->setModel(model_);
- resultsPage_->results_->setItemDelegate(delegate_);
- resultsPage_->results_->setHeaderHidden(true);
+
#ifdef SWIFT_PLATFORM_MACOSX
resultsPage_->results_->setAlternatingRowColors(true);
#endif
@@ -74,7 +74,7 @@ QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWin
}
QtUserSearchWindow::~QtUserSearchWindow() {
-
+ delete model_;
}
void QtUserSearchWindow::handleCurrentChanged(int page) {
@@ -101,9 +101,28 @@ JID QtUserSearchWindow::getServerToSearch() {
void QtUserSearchWindow::handleAccepted() {
JID jid;
if (!firstPage_->byJID_->isChecked()) {
- UserSearchResult* userItem = static_cast<UserSearchResult*>(resultsPage_->results_->currentIndex().internalPointer());
- if (userItem) { /* Remember to leave this if we change to dynamic cast */
- jid = userItem->getJID();
+ if (dynamic_cast<UserSearchModel*>(model_)) {
+ UserSearchResult* userItem = static_cast<UserSearchResult*>(resultsPage_->results_->currentIndex().internalPointer());
+ if (userItem) { /* Remember to leave this if we change to dynamic cast */
+ jid = userItem->getJID();
+ }
+ } else {
+ int row = resultsPage_->results_->currentIndex().row();
+
+ Form::FormItem item = dynamic_cast<QtFormResultItemModel*>(model_)->getForm()->getItems().at(row);
+ JID fallbackJid;
+ foreach(FormField::ref field, item) {
+ if (boost::dynamic_pointer_cast<JIDSingleFormField>(field)) {
+ jid = JID(field->getRawValues().at(0));
+ break;
+ }
+ if (field->getName() == "jid") {
+ fallbackJid = field->getRawValues().at(0);
+ }
+ }
+ if (!jid.isValid()) {
+ jid = fallbackJid;
+ }
}
}
else {
@@ -152,17 +171,21 @@ void QtUserSearchWindow::handleFirstPageRadioChange() {
void QtUserSearchWindow::handleSearch() {
boost::shared_ptr<SearchPayload> search(new SearchPayload());
- if (fieldsPage_->nickInput_->isEnabled()) {
- search->setNick(Q2PSTRING(fieldsPage_->nickInput_->text()));
- }
- if (fieldsPage_->firstInput_->isEnabled()) {
- search->setFirst(Q2PSTRING(fieldsPage_->firstInput_->text()));
- }
- if (fieldsPage_->lastInput_->isEnabled()) {
- search->setLast(Q2PSTRING(fieldsPage_->lastInput_->text()));
- }
- if (fieldsPage_->emailInput_->isEnabled()) {
- search->setEMail(Q2PSTRING(fieldsPage_->emailInput_->text()));
+ if (fieldsPage_->getFormWidget()) {
+ search->setForm(fieldsPage_->getFormWidget()->getCompletedForm());
+ } else {
+ if (fieldsPage_->nickInput_->isEnabled()) {
+ search->setNick(Q2PSTRING(fieldsPage_->nickInput_->text()));
+ }
+ if (fieldsPage_->firstInput_->isEnabled()) {
+ search->setFirst(Q2PSTRING(fieldsPage_->firstInput_->text()));
+ }
+ if (fieldsPage_->lastInput_->isEnabled()) {
+ search->setLast(Q2PSTRING(fieldsPage_->lastInput_->text()));
+ }
+ if (fieldsPage_->emailInput_->isEnabled()) {
+ search->setEMail(Q2PSTRING(fieldsPage_->emailInput_->text()));
+ }
}
onSearchRequested(search, getServerToSearch());
}
@@ -184,18 +207,41 @@ void QtUserSearchWindow::setSearchFields(boost::shared_ptr<SearchPayload> fields
fieldsPage_->fetchingThrobber_->hide();
fieldsPage_->fetchingThrobber_->movie()->stop();
fieldsPage_->fetchingLabel_->hide();
+
fieldsPage_->instructionsLabel_->setText(fields->getInstructions() ? P2QSTRING(fields->getInstructions().get()) : "Enter search terms");
- bool enabled[8] = {fields->getNick(), fields->getNick(), fields->getFirst(), fields->getFirst(), fields->getLast(), fields->getLast(), fields->getEMail(), fields->getEMail()};
- QWidget* legacySearchWidgets[8] = {fieldsPage_->nickInputLabel_, fieldsPage_->nickInput_, fieldsPage_->firstInputLabel_, fieldsPage_->firstInput_, fieldsPage_->lastInputLabel_, fieldsPage_->lastInput_, fieldsPage_->emailInputLabel_, fieldsPage_->emailInput_};
- for (int i = 0; i < 8; i++) {
- legacySearchWidgets[i]->setVisible(enabled[i]);
- legacySearchWidgets[i]->setEnabled(enabled[i]);
+ if (fields->getForm()) {
+ fieldsPage_->setFormWidget(new QtFormWidget(fields->getForm(), fieldsPage_));
+ } else {
+ fieldsPage_->setFormWidget(NULL);
+ bool enabled[8] = {fields->getNick(), fields->getNick(), fields->getFirst(), fields->getFirst(), fields->getLast(), fields->getLast(), fields->getEMail(), fields->getEMail()};
+ QWidget* legacySearchWidgets[8] = {fieldsPage_->nickInputLabel_, fieldsPage_->nickInput_, fieldsPage_->firstInputLabel_, fieldsPage_->firstInput_, fieldsPage_->lastInputLabel_, fieldsPage_->lastInput_, fieldsPage_->emailInputLabel_, fieldsPage_->emailInput_};
+ for (int i = 0; i < 8; i++) {
+ legacySearchWidgets[i]->setVisible(enabled[i]);
+ legacySearchWidgets[i]->setEnabled(enabled[i]);
+ }
}
fieldsPage_->emitCompletenessCheck();
}
void QtUserSearchWindow::setResults(const std::vector<UserSearchResult>& results) {
- model_->setResults(results);
+ UserSearchModel *newModel = new UserSearchModel();
+ newModel->setResults(results);
+ resultsPage_->results_->setModel(newModel);
+ resultsPage_->results_->setItemDelegate(delegate_);
+ resultsPage_->results_->setHeaderHidden(true);
+ delete model_;
+ model_ = newModel;
+}
+
+void QtUserSearchWindow::setResultsForm(Form::ref results) {
+ QtFormResultItemModel *newModel = new QtFormResultItemModel(this);
+ newModel->setForm(results);
+ resultsPage_->results_->setModel(newModel);
+ resultsPage_->results_->setItemDelegate(new QItemDelegate());
+ resultsPage_->results_->setHeaderHidden(false);
+ resultsPage_->results_->header()->setResizeMode(QHeaderView::ResizeToContents);
+ delete model_;
+ model_ = newModel;
}
void QtUserSearchWindow::setSelectedService(const JID& jid) {
@@ -228,7 +274,9 @@ void QtUserSearchWindow::clear() {
firstPage_->howLabel_->setText(howText);
firstPage_->byJID_->setChecked(true);
clearForm();
- model_->clear();
+ resultsPage_->results_->setModel(NULL);
+ delete model_;
+ model_ = NULL;
handleFirstPageRadioChange();
restart();
lastPage_ = 1;
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.h b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
index bb40cd3..4cfe93f 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
@@ -21,6 +21,7 @@ namespace Swift {
class QtUserSearchFieldsPage;
class QtUserSearchResultsPage;
class QtUserSearchDetailsPage;
+ class QtFormResultItemModel;
class QtUserSearchWindow : public QWizard, public UserSearchWindow, private Ui::QtUserSearchWizard {
Q_OBJECT
@@ -33,6 +34,7 @@ namespace Swift {
virtual void clear();
virtual void show();
virtual void setResults(const std::vector<UserSearchResult>& results);
+ virtual void setResultsForm(Form::ref results);
virtual void setSelectedService(const JID& jid);
virtual void setServerSupportsSearch(bool error);
virtual void setSearchError(bool error);
@@ -52,7 +54,7 @@ namespace Swift {
private:
UIEventStream* eventStream_;
UserSearchWindow::Type type_;
- UserSearchModel* model_;
+ QAbstractItemModel* model_;
UserSearchDelegate* delegate_;
QtUserSearchFirstPage* firstPage_;
QtUserSearchFieldsPage* fieldsPage_;
diff --git a/Swiften/Elements/Form.cpp b/Swiften/Elements/Form.cpp
index c34b868..cf9ecf6 100644
--- a/Swiften/Elements/Form.cpp
+++ b/Swiften/Elements/Form.cpp
@@ -24,5 +24,20 @@ FormField::ref Form::getField(const std::string& name) const {
return FormField::ref();
}
+void Form::addReportedField(FormField::ref field) {
+ reportedFields_.push_back(field);
+}
+
+const std::vector<FormField::ref>& Form::getReportedFields() const {
+ return reportedFields_;
+}
+
+void Form::addItem(const Form::FormItem& item) {
+ items_.push_back(item);
+}
+
+const std::vector<Form::FormItem>& Form::getItems() const {
+ return items_;
+}
}
diff --git a/Swiften/Elements/Form.h b/Swiften/Elements/Form.h
index 0d1ed63..2c6f963 100644
--- a/Swiften/Elements/Form.h
+++ b/Swiften/Elements/Form.h
@@ -22,6 +22,7 @@ namespace Swift {
class Form : public Payload {
public:
typedef boost::shared_ptr<Form> ref;
+ typedef std::vector<FormField::ref> FormItem;
enum Type { FormType, SubmitType, CancelType, ResultType };
@@ -46,8 +47,15 @@ namespace Swift {
FormField::ref getField(const std::string& name) const;
+ void addReportedField(FormField::ref field);
+ const std::vector<FormField::ref>& getReportedFields() const;
+
+ void addItem(const FormItem& item);
+ const std::vector<FormItem>& getItems() const;
private:
std::vector<boost::shared_ptr<FormField> > fields_;
+ std::vector<boost::shared_ptr<FormField> > reportedFields_;
+ std::vector<FormItem> items_;
std::string title_;
std::string instructions_;
Type type_;
diff --git a/Swiften/Parser/PayloadParsers/FormParser.cpp b/Swiften/Parser/PayloadParsers/FormParser.cpp
index 3905302..2df0a85 100644
--- a/Swiften/Parser/PayloadParsers/FormParser.cpp
+++ b/Swiften/Parser/PayloadParsers/FormParser.cpp
@@ -6,9 +6,11 @@
#include <Swiften/Parser/PayloadParsers/FormParser.h>
+#include <Swiften/Base/foreach.h>
+
namespace Swift {
-FormParser::FormParser() : level_(TopLevel) {
+FormParser::FormParser() : level_(TopLevel), parsingItem_(false), parsingReported_(false) {
}
void FormParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
@@ -35,32 +37,43 @@ void FormParser::handleStartElement(const std::string& element, const std::strin
currentText_.clear();
}
else if (element == "field") {
- std::string type = attributes.getAttribute("type");
- if (type == "boolean") {
+ std::string type;
+ FormField::ref correspondingReportedField;
+ if (!parsingItem_) {
+ type = attributes.getAttribute("type");
+ } else {
+ foreach(FormField::ref field, getPayloadInternal()->getReportedFields()) {
+ if (field->getName() == attributes.getAttribute("var")) {
+ correspondingReportedField = field;
+ break;
+ }
+ }
+ }
+ if (type == "boolean" || boost::dynamic_pointer_cast<BooleanFormField>(correspondingReportedField)) {
currentFieldParseHelper_ = BooleanFormFieldParseHelper::create();
}
- else if (type == "fixed") {
+ else if (type == "fixed" || boost::dynamic_pointer_cast<FixedFormField>(correspondingReportedField)) {
currentFieldParseHelper_ = FixedFormFieldParseHelper::create();
}
- else if (type == "hidden") {
+ else if (type == "hidden" || boost::dynamic_pointer_cast<HiddenFormField>(correspondingReportedField)) {
currentFieldParseHelper_ = HiddenFormFieldParseHelper::create();
}
- else if (type == "jid-multi") {
+ else if (type == "jid-multi" || boost::dynamic_pointer_cast<JIDMultiFormField>(correspondingReportedField)) {
currentFieldParseHelper_ = JIDMultiFormFieldParseHelper::create();
}
- else if (type == "jid-single") {
+ else if (type == "jid-single" || boost::dynamic_pointer_cast<JIDSingleFormField>(correspondingReportedField)) {
currentFieldParseHelper_ = JIDSingleFormFieldParseHelper::create();
}
- else if (type == "list-multi") {
+ else if (type == "list-multi" || boost::dynamic_pointer_cast<ListMultiFormField>(correspondingReportedField)) {
currentFieldParseHelper_ = ListMultiFormFieldParseHelper::create();
}
- else if (type == "list-single") {
+ else if (type == "list-single" || boost::dynamic_pointer_cast<ListSingleFormField>(correspondingReportedField)) {
currentFieldParseHelper_ = ListSingleFormFieldParseHelper::create();
}
- else if (type == "text-multi") {
+ else if (type == "text-multi" || boost::dynamic_pointer_cast<TextMultiFormField>(correspondingReportedField)) {
currentFieldParseHelper_ = TextMultiFormFieldParseHelper::create();
}
- else if (type == "text-private") {
+ else if (type == "text-private" || boost::dynamic_pointer_cast<TextPrivateFormField>(correspondingReportedField)) {
currentFieldParseHelper_ = TextPrivateFormFieldParseHelper::create();
}
else /*if (type == "text-single") || undefined */ {
@@ -71,6 +84,14 @@ void FormParser::handleStartElement(const std::string& element, const std::strin
currentFieldParseHelper_->getField()->setLabel(attributes.getAttribute("label"));
}
}
+ else if (element == "reported") {
+ parsingReported_ = true;
+ level_ = PayloadLevel - 1;
+ }
+ else if (element == "item") {
+ parsingItem_ = true;
+ level_ = PayloadLevel - 1;
+ }
}
else if (level_ == FieldLevel && currentFieldParseHelper_) {
currentText_.clear();
@@ -104,7 +125,13 @@ void FormParser::handleEndElement(const std::string& element, const std::string&
}
else if (element == "field") {
if (currentFieldParseHelper_) {
- getPayloadInternal()->addField(currentFieldParseHelper_->getField());
+ if (parsingReported_) {
+ getPayloadInternal()->addReportedField(currentFieldParseHelper_->getField());
+ } else if (parsingItem_) {
+ currentFields_.push_back(currentFieldParseHelper_->getField());
+ } else {
+ getPayloadInternal()->addField(currentFieldParseHelper_->getField());
+ }
currentFieldParseHelper_.reset();
}
}
@@ -123,6 +150,16 @@ void FormParser::handleEndElement(const std::string& element, const std::string&
currentFieldParseHelper_->addValue(currentText_);
}
}
+ if (element == "reported") {
+ parsingReported_ = false;
+ level_++;
+ }
+ else if (element == "item") {
+ parsingItem_ = false;
+ level_++;
+ getPayloadInternal()->addItem(currentFields_);
+ currentFields_.clear();
+ }
}
void FormParser::handleCharacterData(const std::string& text) {
diff --git a/Swiften/Parser/PayloadParsers/FormParser.h b/Swiften/Parser/PayloadParsers/FormParser.h
index eae40a1..a3e2524 100644
--- a/Swiften/Parser/PayloadParsers/FormParser.h
+++ b/Swiften/Parser/PayloadParsers/FormParser.h
@@ -49,6 +49,7 @@ namespace Swift {
};
class JIDFieldParseHelper : public FieldParseHelper {
virtual void addValue(const std::string& s) {
+ getField()->addRawValue(s);
boost::dynamic_pointer_cast< GenericFormField<JID> >(getField())->setValue(JID(s));
}
};
@@ -106,5 +107,8 @@ namespace Swift {
std::string currentText_;
std::string currentOptionLabel_;
boost::shared_ptr<FieldParseHelper> currentFieldParseHelper_;
+ bool parsingItem_;
+ bool parsingReported_;
+ std::vector<FormField::ref> currentFields_;
};
}
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp
index 86845be..c36fbeb 100644
--- a/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp
+++ b/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp
@@ -16,6 +16,7 @@ class FormParserTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(FormParserTest);
CPPUNIT_TEST(testParse_FormInformation);
CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST(testParse_FormItems);
CPPUNIT_TEST_SUITE_END();
public:
@@ -115,6 +116,72 @@ class FormParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(std::string("foo"), boost::dynamic_pointer_cast<TextSingleFormField>(payload->getFields()[9])->getValue());
}
+
+ void testParse_FormItems() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<x xmlns='jabber:x:data' type='result'>"
+ "<field type='hidden' var='FORM_TYPE'>"
+ "<value>jabber:iq:search</value>"
+ "</field>"
+ "<reported>"
+ "<field var='first' label='Given Name' type='text-single'/>"
+ "<field var='last' label='Family Name' type='text-single'/>"
+ "<field var='jid' label='Jabber ID' type='jid-single'/>"
+ "<field var='x-gender' label='Gender' type='list-single'/>"
+ "</reported>"
+ "<item>"
+ "<field var='first'><value>Benvolio</value></field>"
+ "<field var='last'><value>Montague</value></field>"
+ "<field var='jid'><value>benvolio@montague.net</value></field>"
+ "<field var='x-gender'><value>male</value></field>"
+ "</item>"
+ "<item>"
+ "<field var='first'><value>Romeo</value></field>"
+ "<field var='last'><value>Montague</value></field>"
+ "<field var='jid'><value>romeo@montague.net</value></field>"
+ "<field var='x-gender'><value>male</value></field>"
+ "</item>"
+ "</x>"));
+
+ Form* dataForm = dynamic_cast<Form*>(parser.getPayload().get());
+ CPPUNIT_ASSERT(dataForm);
+
+ Form::FormItem reported = dataForm->getReportedFields();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), reported.size());
+
+ std::vector<Form::FormItem> items = dataForm->getItems();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), items.size());
+
+ Form::FormItem item = items[0];
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), item.size());
+
+ CPPUNIT_ASSERT_EQUAL(std::string("Benvolio"), item[0]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("first"), item[0]->getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("Montague"), item[1]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("last"), item[1]->getName());
+ JIDSingleFormField::ref jidField = boost::dynamic_pointer_cast<JIDSingleFormField>(item[2]);
+ CPPUNIT_ASSERT(jidField);
+ CPPUNIT_ASSERT_EQUAL(JID("benvolio@montague.net"), jidField->getValue());
+ CPPUNIT_ASSERT_EQUAL(std::string("jid"), item[2]->getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("male"), item[3]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("x-gender"), item[3]->getName());
+
+ item = items[1];
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), item.size());
+
+ CPPUNIT_ASSERT_EQUAL(std::string("Romeo"), item[0]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("first"), item[0]->getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("Montague"), item[1]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("last"), item[1]->getName());
+ jidField = boost::dynamic_pointer_cast<JIDSingleFormField>(item[2]);
+ CPPUNIT_ASSERT(jidField);
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.net"), jidField->getValue());
+ CPPUNIT_ASSERT_EQUAL(std::string("jid"), item[2]->getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("male"), item[3]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("x-gender"), item[3]->getName());
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(FormParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp
index c07cd7f..ef48ced 100644
--- a/Swiften/Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp
+++ b/Swiften/Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp
@@ -16,6 +16,8 @@ class SearchPayloadParserTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(SearchPayloadParserTest);
CPPUNIT_TEST(testParse_FormRequestResponse);
CPPUNIT_TEST(testParse_Results);
+ CPPUNIT_TEST(testParse_FormRequestResponse_XDATA);
+ CPPUNIT_TEST(testParse_Results_XDATA);
CPPUNIT_TEST_SUITE_END();
public:
@@ -66,6 +68,114 @@ class SearchPayloadParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(std::string("juliet@shakespeare.lit"), payload->getItems()[0].email);
CPPUNIT_ASSERT_EQUAL(JID("tybalt@shakespeare.lit"), payload->getItems()[1].jid);
}
+
+ void testParse_FormRequestResponse_XDATA() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<query xmlns='jabber:iq:search'>"
+ "<instructions>"
+ "Use the enclosed form to search. If your Jabber client does not"
+ " support Data Forms, visit http://shakespeare.lit/"
+ "</instructions>"
+ "<x xmlns='jabber:x:data' type='form'>"
+ "<title>User Directory Search</title>"
+ "<instructions>"
+ "Please provide the following information"
+ " to search for Shakespearean characters."
+ "</instructions>"
+ "<field type='hidden'"
+ " var='FORM_TYPE'>"
+ "<value>jabber:iq:search</value>"
+ "</field>"
+ "<field type='text-single'"
+ " label='Given Name'"
+ " var='first'/>"
+ "<field type='text-single'"
+ " label='Family Name'"
+ " var='last'/>"
+ "<field type='list-single'"
+ " label='Gender'"
+ " var='x-gender'>"
+ "<option label='Male'><value>male</value></option>"
+ "<option label='Female'><value>female</value></option>"
+ "</field>"
+ "</x>"
+ "</query>"
+ ));
+
+ SearchPayload::ref payload = parser.getPayload<SearchPayload>();
+ CPPUNIT_ASSERT_EQUAL(std::string("Use the enclosed form to search. If your Jabber client does not"
+ " support Data Forms, visit http://shakespeare.lit/"), *payload->getInstructions());
+ CPPUNIT_ASSERT(payload->getForm());
+ CPPUNIT_ASSERT_EQUAL(std::string("Please provide the following information"
+ " to search for Shakespearean characters."), payload->getForm()->getInstructions());
+ }
+
+ void testParse_Results_XDATA() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<query xmlns='jabber:iq:search'>"
+ " <x xmlns='jabber:x:data' type='result'>"
+ " <field type='hidden' var='FORM_TYPE'>"
+ " <value>jabber:iq:search</value>"
+ " </field>"
+ " <reported>"
+ " <field var='first' label='Given Name' type='text-single'/>"
+ " <field var='last' label='Family Name' type='text-single'/>"
+ " <field var='jid' label='Jabber ID' type='jid-single'/>"
+ " <field var='x-gender' label='Gender' type='list-single'/>"
+ " </reported>"
+ " <item>"
+ " <field var='first'><value>Benvolio</value></field>"
+ " <field var='last'><value>Montague</value></field>"
+ " <field var='jid'><value>benvolio@montague.net</value></field>"
+ " <field var='x-gender'><value>male</value></field>"
+ " </item>"
+ " <item>"
+ " <field var='first'><value>Romeo</value></field>"
+ " <field var='last'><value>Montague</value></field>"
+ " <field var='jid'><value>romeo@montague.net</value></field>"
+ " <field var='x-gender'><value>male</value></field>"
+ " </item>"
+ " </x>"
+ "</query>"));
+ SearchPayload::ref payload = parser.getPayload<SearchPayload>();
+ CPPUNIT_ASSERT(payload);
+
+ Form::ref dataForm = payload->getForm();
+ CPPUNIT_ASSERT(dataForm);
+
+ Form::FormItem reported = dataForm->getReportedFields();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), reported.size());
+
+ std::vector<Form::FormItem> items = dataForm->getItems();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), items.size());
+
+ Form::FormItem item = items[0];
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), item.size());
+
+ CPPUNIT_ASSERT_EQUAL(std::string("Benvolio"), item[0]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("first"), item[0]->getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("Montague"), item[1]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("last"), item[1]->getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("benvolio@montague.net"), item[2]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("jid"), item[2]->getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("male"), item[3]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("x-gender"), item[3]->getName());
+
+ item = items[1];
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), item.size());
+
+ CPPUNIT_ASSERT_EQUAL(std::string("Romeo"), item[0]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("first"), item[0]->getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("Montague"), item[1]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("last"), item[1]->getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("romeo@montague.net"), item[2]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("jid"), item[2]->getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("male"), item[3]->getRawValues()[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("x-gender"), item[3]->getName());
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(SearchPayloadParserTest);
diff --git a/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp b/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp
index 7a6bb79..15c4f32 100644
--- a/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp
@@ -52,12 +52,27 @@ std::string FormSerializer::serializePayload(boost::shared_ptr<Form> form) cons
multiLineify(form->getInstructions(), "instructions", formElement);
}
foreach(boost::shared_ptr<FormField> field, form->getFields()) {
- formElement->addNode(fieldToXML(field));
+ formElement->addNode(fieldToXML(field, true));
}
+ if (!form->getReportedFields().empty()) {
+ boost::shared_ptr<XMLElement> reportedElement(new XMLElement("reported"));
+ foreach(FormField::ref field, form->getReportedFields()) {
+ reportedElement->addNode(fieldToXML(field, true));
+ }
+ formElement->addNode(reportedElement);
+ }
+ foreach(Form::FormItem item, form->getItems()) {
+ boost::shared_ptr<XMLElement> itemElement(new XMLElement("item"));
+ foreach(FormField::ref field, item) {
+ itemElement->addNode(fieldToXML(field, false));
+ }
+ formElement->addNode(itemElement);
+ }
+
return formElement->serialize();
}
-boost::shared_ptr<XMLElement> FormSerializer::fieldToXML(boost::shared_ptr<FormField> field) const {
+boost::shared_ptr<XMLElement> FormSerializer::fieldToXML(boost::shared_ptr<FormField> field, bool withTypeAttribute) const {
boost::shared_ptr<XMLElement> fieldElement(new XMLElement("field"));
if (!field->getName().empty()) {
fieldElement->setAttribute("var", field->getName());
@@ -115,7 +130,7 @@ boost::shared_ptr<XMLElement> FormSerializer::fieldToXML(boost::shared_ptr<FormF
fieldType = "jid-single";
boost::shared_ptr<XMLElement> valueElement(new XMLElement("value"));
valueElement->addNode(XMLTextNode::create(boost::dynamic_pointer_cast<JIDSingleFormField>(field)->getValue().toString()));
- fieldElement->addNode(valueElement);
+ if ( boost::dynamic_pointer_cast<JIDSingleFormField>(field)->getValue().isValid()) fieldElement->addNode(valueElement);
}
else if (boost::dynamic_pointer_cast<ListMultiFormField>(field)) {
fieldType = "list-multi";
@@ -133,7 +148,7 @@ boost::shared_ptr<XMLElement> FormSerializer::fieldToXML(boost::shared_ptr<FormF
else {
assert(false);
}
- if (!fieldType.empty()) {
+ if (!fieldType.empty() && withTypeAttribute) {
fieldElement->setAttribute("type", fieldType);
}
diff --git a/Swiften/Serializer/PayloadSerializers/FormSerializer.h b/Swiften/Serializer/PayloadSerializers/FormSerializer.h
index 43db9e8..d10f649 100644
--- a/Swiften/Serializer/PayloadSerializers/FormSerializer.h
+++ b/Swiften/Serializer/PayloadSerializers/FormSerializer.h
@@ -19,7 +19,7 @@ namespace Swift {
virtual std::string serializePayload(boost::shared_ptr<Form>) const;
private:
- boost::shared_ptr<XMLElement> fieldToXML(boost::shared_ptr<FormField> field) const;
+ boost::shared_ptr<XMLElement> fieldToXML(boost::shared_ptr<FormField> field, bool withTypeAttribute) const;
void multiLineify(const std::string& text, const std::string& elementName, boost::shared_ptr<XMLElement> parent) const;
};
}
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp
index 29e7e59..4608522 100644
--- a/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp
@@ -8,13 +8,13 @@
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <Swiften/Serializer/PayloadSerializers/FormSerializer.h>
-
using namespace Swift;
class FormSerializerTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(FormSerializerTest);
CPPUNIT_TEST(testSerializeFormInformation);
CPPUNIT_TEST(testSerializeFields);
+ CPPUNIT_TEST(testSerializeFormItems);
CPPUNIT_TEST_SUITE_END();
public:
@@ -134,6 +134,99 @@ class FormSerializerTest : public CppUnit::TestFixture {
"</field>"
"</x>"), testling.serialize(form));
}
+
+ void testSerializeFormItems() {
+ FormSerializer testling;
+ boost::shared_ptr<Form> form(new Form(Form::ResultType));
+
+
+ FormField::ref field = HiddenFormField::create("jabber:iq:search");
+ field->setName("FORM_TYPE");
+ form->addField(field);
+
+ // reported fields
+ field = TextSingleFormField::create();
+ field->setName("first");
+ field->setLabel("Given Name");
+ form->addReportedField(field);
+
+ field = TextSingleFormField::create();
+ field->setName("last");
+ field->setLabel("Family Name");
+ form->addReportedField(field);
+
+ field = JIDSingleFormField::create();
+ field->setName("jid");
+ field->setLabel("Jabber ID");
+ form->addReportedField(field);
+
+ field = ListSingleFormField::create();
+ field->setName("x-gender");
+ field->setLabel("Gender");
+ form->addReportedField(field);
+
+ Form::FormItem firstItem;
+ field = TextSingleFormField::create("Benvolio");
+ field->setName("first");
+ firstItem.push_back(field);
+
+ field = TextSingleFormField::create("Montague");
+ field->setName("last");
+ firstItem.push_back(field);
+
+ field = JIDSingleFormField::create(JID("benvolio@montague.net"));
+ field->setName("jid");
+ firstItem.push_back(field);
+
+ field = ListSingleFormField::create("male");
+ field->setName("x-gender");
+ firstItem.push_back(field);
+
+ Form::FormItem secondItem;
+ field = TextSingleFormField::create("Romeo");
+ field->setName("first");
+ secondItem.push_back(field);
+
+ field = TextSingleFormField::create("Montague");
+ field->setName("last");
+ secondItem.push_back(field);
+
+ field = JIDSingleFormField::create(JID("romeo@montague.net"));
+ field->setName("jid");
+ secondItem.push_back(field);
+
+ field = ListSingleFormField::create("male");
+ field->setName("x-gender");
+ secondItem.push_back(field);
+
+ form->addItem(firstItem);
+ form->addItem(secondItem);
+
+ CPPUNIT_ASSERT_EQUAL(std::string(
+ "<x type=\"result\" xmlns=\"jabber:x:data\">"
+ "<field type=\"hidden\" var=\"FORM_TYPE\">"
+ "<value>jabber:iq:search</value>"
+ "</field>"
+ "<reported>"
+ "<field label=\"Given Name\" type=\"text-single\" var=\"first\"/>"
+ "<field label=\"Family Name\" type=\"text-single\" var=\"last\"/>"
+ "<field label=\"Jabber ID\" type=\"jid-single\" var=\"jid\"/>"
+ "<field label=\"Gender\" type=\"list-single\" var=\"x-gender\"/>"
+ "</reported>"
+ "<item>"
+ "<field var=\"first\"><value>Benvolio</value></field>"
+ "<field var=\"last\"><value>Montague</value></field>"
+ "<field var=\"jid\"><value>benvolio@montague.net</value></field>"
+ "<field var=\"x-gender\"><value>male</value></field>"
+ "</item>"
+ "<item>"
+ "<field var=\"first\"><value>Romeo</value></field>"
+ "<field var=\"last\"><value>Montague</value></field>"
+ "<field var=\"jid\"><value>romeo@montague.net</value></field>"
+ "<field var=\"x-gender\"><value>male</value></field>"
+ "</item>"
+ "</x>"), testling.serialize(form));
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(FormSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/SearchPayloadSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/SearchPayloadSerializerTest.cpp
index d0dcbef..42bff72 100644
--- a/Swiften/Serializer/PayloadSerializers/UnitTest/SearchPayloadSerializerTest.cpp
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/SearchPayloadSerializerTest.cpp
@@ -15,6 +15,7 @@ class SearchPayloadSerializerTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(SearchPayloadSerializerTest);
CPPUNIT_TEST(testSerialize_Request);
CPPUNIT_TEST(testSerialize_Items);
+ CPPUNIT_TEST(testSerialize_DataForm);
CPPUNIT_TEST_SUITE_END();
public:
@@ -70,6 +71,105 @@ class SearchPayloadSerializerTest : public CppUnit::TestFixture {
"</query>"
), testling.serialize(payload));
}
+
+ void testSerialize_DataForm() {
+ SearchPayloadSerializer testling;
+
+ SearchPayload::ref payload(new SearchPayload());
+ boost::shared_ptr<Form> form(new Form(Form::ResultType));
+
+ FormField::ref field = HiddenFormField::create("jabber:iq:search");
+ field->setName("FORM_TYPE");
+ form->addField(field);
+
+ // reported fields
+ field = TextSingleFormField::create();
+ field->setName("first");
+ field->setLabel("Given Name");
+ form->addReportedField(field);
+
+ field = TextSingleFormField::create();
+ field->setName("last");
+ field->setLabel("Family Name");
+ form->addReportedField(field);
+
+ field = JIDSingleFormField::create();
+ field->setName("jid");
+ field->setLabel("Jabber ID");
+ form->addReportedField(field);
+
+ field = ListSingleFormField::create();
+ field->setName("x-gender");
+ field->setLabel("Gender");
+ form->addReportedField(field);
+
+ Form::FormItem firstItem;
+ field = TextSingleFormField::create("Benvolio");
+ field->setName("first");
+ firstItem.push_back(field);
+
+ field = TextSingleFormField::create("Montague");
+ field->setName("last");
+ firstItem.push_back(field);
+
+ field = TextSingleFormField::create("benvolio@montague.net");
+ field->setName("jid");
+ firstItem.push_back(field);
+
+ field = ListSingleFormField::create("male");
+ field->setName("x-gender");
+ firstItem.push_back(field);
+
+ Form::FormItem secondItem;
+ field = TextSingleFormField::create("Romeo");
+ field->setName("first");
+ secondItem.push_back(field);
+
+ field = TextSingleFormField::create("Montague");
+ field->setName("last");
+ secondItem.push_back(field);
+
+ field = TextSingleFormField::create("romeo@montague.net");
+ field->setName("jid");
+ secondItem.push_back(field);
+
+ field = ListSingleFormField::create("male");
+ field->setName("x-gender");
+ secondItem.push_back(field);
+
+ form->addItem(firstItem);
+ form->addItem(secondItem);
+
+ payload->setForm(form);
+
+ CPPUNIT_ASSERT_EQUAL(std::string(
+ "<query xmlns=\"jabber:iq:search\">"
+ "<x type=\"result\" xmlns=\"jabber:x:data\">"
+ "<field type=\"hidden\" var=\"FORM_TYPE\">"
+ "<value>jabber:iq:search</value>"
+ "</field>"
+ "<reported>"
+ "<field label=\"Given Name\" type=\"text-single\" var=\"first\"/>"
+ "<field label=\"Family Name\" type=\"text-single\" var=\"last\"/>"
+ "<field label=\"Jabber ID\" type=\"jid-single\" var=\"jid\"/>"
+ "<field label=\"Gender\" type=\"list-single\" var=\"x-gender\"/>"
+ "</reported>"
+ "<item>"
+ "<field var=\"first\"><value>Benvolio</value></field>"
+ "<field var=\"last\"><value>Montague</value></field>"
+ "<field var=\"jid\"><value>benvolio@montague.net</value></field>"
+ "<field var=\"x-gender\"><value>male</value></field>"
+ "</item>"
+ "<item>"
+ "<field var=\"first\"><value>Romeo</value></field>"
+ "<field var=\"last\"><value>Montague</value></field>"
+ "<field var=\"jid\"><value>romeo@montague.net</value></field>"
+ "<field var=\"x-gender\"><value>male</value></field>"
+ "</item>"
+ "</x>"
+ "</query>"), testling.serialize(payload));
+
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(SearchPayloadSerializerTest);