mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-21 20:45:51 +03:00
Add GUI testing SocketApi extension
This commit is contained in:
parent
e97784bb9d
commit
3288a36da6
7 changed files with 357 additions and 78 deletions
|
@ -196,6 +196,8 @@ if(APPLE)
|
|||
endif()
|
||||
|
||||
if(BUILD_CLIENT)
|
||||
OPTION(GUI_TESTING "Build with gui introspection features of socket api" OFF)
|
||||
|
||||
if(APPLE AND BUILD_UPDATER)
|
||||
find_package(Sparkle)
|
||||
endif()
|
||||
|
|
|
@ -35,4 +35,6 @@
|
|||
#cmakedefine SHAREDIR "@SHAREDIR@"
|
||||
#cmakedefine PLUGINDIR "@PLUGINDIR@"
|
||||
|
||||
#cmakedefine01 GUI_TESTING
|
||||
|
||||
#endif
|
||||
|
|
|
@ -224,7 +224,11 @@ void SettingsDialog::accountAdded(AccountState *s)
|
|||
|
||||
_toolBar->insertAction(_actionBefore, accountAction);
|
||||
auto accountSettings = new AccountSettings(s, this);
|
||||
_ui->stack->insertWidget(0, accountSettings);
|
||||
QString objectName = QLatin1String("accountSettings_");
|
||||
objectName += s->account()->displayName();
|
||||
accountSettings->setObjectName(objectName);
|
||||
_ui->stack->insertWidget(0 , accountSettings);
|
||||
|
||||
_actionGroup->addAction(accountAction);
|
||||
_actionGroupWidgets.insert(accountAction, accountSettings);
|
||||
_actionForAccount.insert(s->account().data(), accountAction);
|
||||
|
@ -339,6 +343,10 @@ public:
|
|||
}
|
||||
|
||||
auto *btn = new QToolButton(parent);
|
||||
QString objectName = QLatin1String("settingsdialog_toolbutton_");
|
||||
objectName += text();
|
||||
btn->setObjectName(objectName);
|
||||
|
||||
btn->setDefaultAction(this);
|
||||
btn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
|
||||
btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
#include "socketapi.h"
|
||||
#include "socketapi_p.h"
|
||||
|
||||
#include "conflictdialog.h"
|
||||
#include "conflictsolver.h"
|
||||
|
@ -53,6 +54,13 @@
|
|||
#include <QMessageBox>
|
||||
#include <QInputDialog>
|
||||
#include <QFileDialog>
|
||||
|
||||
|
||||
#include <QAction>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QWidget>
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
|
||||
|
@ -62,10 +70,30 @@
|
|||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
|
||||
// This is the version that is returned when the client asks for the VERSION.
|
||||
// The first number should be changed if there is an incompatible change that breaks old clients.
|
||||
// The second number should be changed when there are new features.
|
||||
#define MIRALL_SOCKET_API_VERSION "1.1"
|
||||
#define DEBUG qDebug() << "SocketApi: "
|
||||
|
||||
namespace {
|
||||
#if GUI_TESTING
|
||||
QWidget *findWidget(const QString &objectName)
|
||||
{
|
||||
auto widgets = QApplication::allWidgets();
|
||||
|
||||
auto foundWidget = std::find_if(widgets.constBegin(), widgets.constEnd(), [&](QWidget *widget) {
|
||||
return widget->objectName() == objectName;
|
||||
});
|
||||
|
||||
if (foundWidget == widgets.constEnd()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return *foundWidget;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline QString removeTrailingSlash(QString path)
|
||||
{
|
||||
|
@ -89,6 +117,7 @@ static QString buildMessage(const QString &verb, const QString &path, const QStr
|
|||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
namespace OCC {
|
||||
|
||||
|
@ -96,80 +125,28 @@ Q_LOGGING_CATEGORY(lcSocketApi, "nextcloud.gui.socketapi", QtInfoMsg)
|
|||
Q_LOGGING_CATEGORY(lcPublicLink, "nextcloud.gui.socketapi.publiclink", QtInfoMsg)
|
||||
|
||||
|
||||
class BloomFilter
|
||||
void SocketListener::sendMessage(const QString &message, bool doWait) const
|
||||
{
|
||||
// Initialize with m=1024 bits and k=2 (high and low 16 bits of a qHash).
|
||||
// For a client navigating in less than 100 directories, this gives us a probability less than (1-e^(-2*100/1024))^2 = 0.03147872136 false positives.
|
||||
const static int NumBits = 1024;
|
||||
|
||||
public:
|
||||
BloomFilter()
|
||||
: hashBits(NumBits)
|
||||
{
|
||||
if (!socket) {
|
||||
qCInfo(lcSocketApi) << "Not sending message to dead socket:" << message;
|
||||
return;
|
||||
}
|
||||
|
||||
void storeHash(uint hash)
|
||||
{
|
||||
hashBits.setBit((hash & 0xFFFF) % NumBits); // NOLINT it's uint all the way and the modulo puts us back in the 0..1023 range
|
||||
hashBits.setBit((hash >> 16) % NumBits); // NOLINT
|
||||
}
|
||||
bool isHashMaybeStored(uint hash) const
|
||||
{
|
||||
return hashBits.testBit((hash & 0xFFFF) % NumBits) // NOLINT
|
||||
&& hashBits.testBit((hash >> 16) % NumBits); // NOLINT
|
||||
qCInfo(lcSocketApi) << "Sending SocketAPI message -->" << message << "to" << socket;
|
||||
QString localMessage = message;
|
||||
if (!localMessage.endsWith(QLatin1Char('\n'))) {
|
||||
localMessage.append(QLatin1Char('\n'));
|
||||
}
|
||||
|
||||
private:
|
||||
QBitArray hashBits;
|
||||
};
|
||||
|
||||
class SocketListener
|
||||
{
|
||||
public:
|
||||
QPointer<QIODevice> socket;
|
||||
|
||||
explicit SocketListener(QIODevice *socket)
|
||||
: socket(socket)
|
||||
{
|
||||
QByteArray bytesToSend = localMessage.toUtf8();
|
||||
qint64 sent = socket->write(bytesToSend);
|
||||
if (doWait) {
|
||||
socket->waitForBytesWritten(1000);
|
||||
}
|
||||
|
||||
void sendMessage(const QString &message, bool doWait = false) const
|
||||
{
|
||||
if (!socket) {
|
||||
qCInfo(lcSocketApi) << "Not sending message to dead socket:" << message;
|
||||
return;
|
||||
}
|
||||
|
||||
qCInfo(lcSocketApi) << "Sending SocketAPI message -->" << message << "to" << socket;
|
||||
QString localMessage = message;
|
||||
if (!localMessage.endsWith(QLatin1Char('\n'))) {
|
||||
localMessage.append(QLatin1Char('\n'));
|
||||
}
|
||||
|
||||
QByteArray bytesToSend = localMessage.toUtf8();
|
||||
qint64 sent = socket->write(bytesToSend);
|
||||
if (doWait) {
|
||||
socket->waitForBytesWritten(1000);
|
||||
}
|
||||
if (sent != bytesToSend.length()) {
|
||||
qCWarning(lcSocketApi) << "Could not send all data on socket for " << localMessage;
|
||||
}
|
||||
if (sent != bytesToSend.length()) {
|
||||
qCWarning(lcSocketApi) << "Could not send all data on socket for " << localMessage;
|
||||
}
|
||||
|
||||
void sendMessageIfDirectoryMonitored(const QString &message, uint systemDirectoryHash) const
|
||||
{
|
||||
if (_monitoredDirectoriesBloomFilter.isHashMaybeStored(systemDirectoryHash))
|
||||
sendMessage(message, false);
|
||||
}
|
||||
|
||||
void registerMonitoredDirectory(uint systemDirectoryHash)
|
||||
{
|
||||
_monitoredDirectoriesBloomFilter.storeHash(systemDirectoryHash);
|
||||
}
|
||||
|
||||
private:
|
||||
BloomFilter _monitoredDirectoriesBloomFilter;
|
||||
};
|
||||
}
|
||||
|
||||
struct ListenerHasSocketPred
|
||||
{
|
||||
|
@ -186,6 +163,9 @@ SocketApi::SocketApi(QObject *parent)
|
|||
{
|
||||
QString socketPath;
|
||||
|
||||
qRegisterMetaType<SocketListener *>("SocketListener*");
|
||||
qRegisterMetaType<QSharedPointer<SocketApiJob>>("QSharedPointer<SocketApiJob>");
|
||||
|
||||
if (Utility::isWindows()) {
|
||||
socketPath = QLatin1String(R"(\\.\pipe\)")
|
||||
+ QLatin1String(APPLICATION_EXECUTABLE)
|
||||
|
@ -321,20 +301,48 @@ void SocketApi::slotReadSocket()
|
|||
line.chop(1); // remove the '\n'
|
||||
qCInfo(lcSocketApi) << "Received SocketAPI message <--" << line << "from" << socket;
|
||||
QByteArray command = line.split(":").value(0).toLatin1();
|
||||
QByteArray functionWithArguments = "command_" + command + "(QString,SocketListener*)";
|
||||
|
||||
QByteArray functionWithArguments = "command_" + command;
|
||||
if (command.startsWith("ASYNC_")) {
|
||||
functionWithArguments += "(QSharedPointer<SocketApiJob>)";
|
||||
} else {
|
||||
functionWithArguments += "(QString,SocketListener*)";
|
||||
}
|
||||
|
||||
int indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments);
|
||||
|
||||
QString argument = line.remove(0, command.length() + 1);
|
||||
if (indexOfMethod == -1) {
|
||||
// Fallback: Try upper-case command
|
||||
functionWithArguments = "command_" + command.toUpper() + "(QString,SocketListener*)";
|
||||
indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments);
|
||||
}
|
||||
if (command.startsWith("ASYNC_")) {
|
||||
|
||||
if (indexOfMethod != -1) {
|
||||
staticMetaObject.method(indexOfMethod).invoke(this, Q_ARG(QString, argument), Q_ARG(SocketListener *, listener));
|
||||
auto arguments = argument.split('|');
|
||||
if (arguments.size() != 2) {
|
||||
listener->sendMessage(QLatin1String("argument count is wrong"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto json = QJsonDocument::fromJson(arguments[1].toUtf8()).object();
|
||||
|
||||
auto jobId = arguments[0];
|
||||
|
||||
auto socketApiJob = QSharedPointer<SocketApiJob>(
|
||||
new SocketApiJob(jobId, listener, json), &QObject::deleteLater);
|
||||
if (indexOfMethod != -1) {
|
||||
staticMetaObject.method(indexOfMethod)
|
||||
.invoke(this, Qt::QueuedConnection,
|
||||
Q_ARG(QSharedPointer<SocketApiJob>, socketApiJob));
|
||||
} else {
|
||||
qCWarning(lcSocketApi) << "The command is not supported by this version of the client:" << command
|
||||
<< "with argument:" << argument;
|
||||
socketApiJob->reject("command not found");
|
||||
}
|
||||
} else {
|
||||
qCWarning(lcSocketApi) << "The command is not supported by this version of the client:" << command << "with argument:" << argument;
|
||||
if (indexOfMethod != -1) {
|
||||
staticMetaObject.method(indexOfMethod)
|
||||
.invoke(this, Qt::QueuedConnection, Q_ARG(QString, argument),
|
||||
Q_ARG(SocketListener *, listener));
|
||||
} else {
|
||||
qCWarning(lcSocketApi) << "The command is not supported by this version of the client:" << command << "with argument:" << argument;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1123,6 +1131,109 @@ DirectEditor* SocketApi::getDirectEditorForLocalFile(const QString &localFile)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
#if GUI_TESTING
|
||||
void SocketApi::command_ASYNC_LIST_WIDGETS(const QSharedPointer<SocketApiJob> &job)
|
||||
{
|
||||
QString response;
|
||||
for (auto &widget : QApplication::allWidgets()) {
|
||||
auto objectName = widget->objectName();
|
||||
if (!objectName.isEmpty()) {
|
||||
response += objectName + ":" + widget->property("text").toString() + ", ";
|
||||
}
|
||||
}
|
||||
job->resolve(response);
|
||||
}
|
||||
|
||||
void SocketApi::command_ASYNC_INVOKE_WIDGET_METHOD(const QSharedPointer<SocketApiJob> &job)
|
||||
{
|
||||
auto &arguments = job->arguments();
|
||||
|
||||
auto widget = findWidget(arguments["objectName"].toString());
|
||||
if (!widget) {
|
||||
job->reject(QLatin1String("widget not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(widget, arguments["method"].toString().toLocal8Bit().constData());
|
||||
job->resolve();
|
||||
}
|
||||
|
||||
void SocketApi::command_ASYNC_GET_WIDGET_PROPERTY(const QSharedPointer<SocketApiJob> &job)
|
||||
{
|
||||
auto widget = findWidget(job->arguments()[QLatin1String("objectName")].toString());
|
||||
if (!widget) {
|
||||
job->reject(QLatin1String("widget not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto propertyName = job->arguments()[QLatin1String("property")].toString();
|
||||
|
||||
job->resolve(widget->property(propertyName.toLocal8Bit().constData())
|
||||
.toString()
|
||||
.toLocal8Bit()
|
||||
.constData());
|
||||
}
|
||||
|
||||
void SocketApi::command_ASYNC_SET_WIDGET_PROPERTY(const QSharedPointer<SocketApiJob> &job)
|
||||
{
|
||||
auto &arguments = job->arguments();
|
||||
auto widget = findWidget(arguments["objectName"].toString());
|
||||
if (!widget) {
|
||||
job->reject(QLatin1String("widget not found"));
|
||||
return;
|
||||
}
|
||||
widget->setProperty(arguments["property"].toString().toLocal8Bit().constData(),
|
||||
arguments["value"].toString().toLocal8Bit().constData());
|
||||
job->resolve();
|
||||
}
|
||||
|
||||
void SocketApi::command_ASYNC_WAIT_FOR_WIDGET_SIGNAL(const QSharedPointer<SocketApiJob> &job)
|
||||
{
|
||||
auto &arguments = job->arguments();
|
||||
auto widget = findWidget(arguments["objectName"].toString());
|
||||
if (!widget) {
|
||||
job->reject(QLatin1String("widget not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
ListenerClosure *closure = new ListenerClosure([job]() { job->resolve("signal emitted"); });
|
||||
|
||||
auto signalSignature = arguments["signalSignature"].toString();
|
||||
signalSignature.prepend("2");
|
||||
auto local8bit = signalSignature.toLocal8Bit();
|
||||
auto signalSignatureFinal = local8bit.constData();
|
||||
connect(widget, signalSignatureFinal, closure, SLOT(closureSlot()), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void SocketApi::command_ASYNC_TRIGGER_MENU_ACTION(const QSharedPointer<SocketApiJob> &job)
|
||||
{
|
||||
auto &arguments = job->arguments();
|
||||
|
||||
auto objectName = arguments["objectName"].toString();
|
||||
auto widget = findWidget(objectName);
|
||||
if (!widget) {
|
||||
job->reject(QLatin1String("widget not found: ") + objectName);
|
||||
return;
|
||||
}
|
||||
|
||||
auto children = widget->findChildren<QWidget *>();
|
||||
for (auto childWidget : children) {
|
||||
// foo is the popupwidget!
|
||||
auto actions = childWidget->actions();
|
||||
for (auto action : actions) {
|
||||
if (action->objectName() == arguments["actionName"].toString()) {
|
||||
action->trigger();
|
||||
|
||||
job->resolve("action found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
job->reject("Action not found");
|
||||
}
|
||||
#endif
|
||||
|
||||
QString SocketApi::buildRegisterPathMessage(const QString &path)
|
||||
{
|
||||
QFileInfo fi(path);
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
* for more details.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SOCKETAPI_H
|
||||
#define SOCKETAPI_H
|
||||
|
||||
|
@ -21,6 +20,8 @@
|
|||
#include "sharedialog.h" // for the ShareDialogStartPage
|
||||
#include "common/syncjournalfilerecord.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
#include "socketapisocket_mac.h"
|
||||
#else
|
||||
|
@ -38,6 +39,7 @@ class SyncFileStatus;
|
|||
class Folder;
|
||||
class SocketListener;
|
||||
class DirectEditor;
|
||||
class SocketApiJob;
|
||||
|
||||
/**
|
||||
* @brief The SocketApi class
|
||||
|
@ -147,6 +149,15 @@ private:
|
|||
Q_INVOKABLE void command_EDIT(const QString &localFile, SocketListener *listener);
|
||||
DirectEditor* getDirectEditorForLocalFile(const QString &localFile);
|
||||
|
||||
#if GUI_TESTING
|
||||
Q_INVOKABLE void command_ASYNC_LIST_WIDGETS(const QSharedPointer<SocketApiJob> &job);
|
||||
Q_INVOKABLE void command_ASYNC_INVOKE_WIDGET_METHOD(const QSharedPointer<SocketApiJob> &job);
|
||||
Q_INVOKABLE void command_ASYNC_GET_WIDGET_PROPERTY(const QSharedPointer<SocketApiJob> &job);
|
||||
Q_INVOKABLE void command_ASYNC_SET_WIDGET_PROPERTY(const QSharedPointer<SocketApiJob> &job);
|
||||
Q_INVOKABLE void command_ASYNC_WAIT_FOR_WIDGET_SIGNAL(const QSharedPointer<SocketApiJob> &job);
|
||||
Q_INVOKABLE void command_ASYNC_TRIGGER_MENU_ACTION(const QSharedPointer<SocketApiJob> &job);
|
||||
#endif
|
||||
|
||||
QString buildRegisterPathMessage(const QString &path);
|
||||
|
||||
QSet<QString> _registeredAliases;
|
||||
|
@ -154,4 +165,5 @@ private:
|
|||
SocketApiServer _localServer;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SOCKETAPI_H
|
||||
|
|
142
src/gui/socketapi_p.h
Normal file
142
src/gui/socketapi_p.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (C) by Dominik Schmidt <dev@dominik-schmidt.de>
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef SOCKETAPI_P_H
|
||||
#define SOCKETAPI_P_H
|
||||
|
||||
#include <functional>
|
||||
#include <QBitArray>
|
||||
#include <QPointer>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <memory>
|
||||
#include <QTimer>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class BloomFilter
|
||||
{
|
||||
// Initialize with m=1024 bits and k=2 (high and low 16 bits of a qHash).
|
||||
// For a client navigating in less than 100 directories, this gives us a probability less than
|
||||
// (1-e^(-2*100/1024))^2 = 0.03147872136 false positives.
|
||||
const static int NumBits = 1024;
|
||||
|
||||
public:
|
||||
BloomFilter()
|
||||
: hashBits(NumBits)
|
||||
{
|
||||
}
|
||||
|
||||
void storeHash(uint hash)
|
||||
{
|
||||
hashBits.setBit((hash & 0xFFFF) % NumBits); // NOLINT it's uint all the way and the modulo puts us back in the 0..1023 range
|
||||
hashBits.setBit((hash >> 16) % NumBits); // NOLINT
|
||||
}
|
||||
bool isHashMaybeStored(uint hash) const
|
||||
{
|
||||
return hashBits.testBit((hash & 0xFFFF) % NumBits) // NOLINT
|
||||
&& hashBits.testBit((hash >> 16) % NumBits); // NOLINT
|
||||
}
|
||||
|
||||
private:
|
||||
QBitArray hashBits;
|
||||
};
|
||||
|
||||
class SocketListener
|
||||
{
|
||||
public:
|
||||
QPointer<QIODevice> socket;
|
||||
|
||||
explicit SocketListener(QIODevice *socket)
|
||||
: socket(socket)
|
||||
{
|
||||
}
|
||||
|
||||
void sendMessage(const QString &message, bool doWait = false) const;
|
||||
|
||||
void sendMessageIfDirectoryMonitored(const QString &message, uint systemDirectoryHash) const
|
||||
{
|
||||
if (_monitoredDirectoriesBloomFilter.isHashMaybeStored(systemDirectoryHash))
|
||||
sendMessage(message, false);
|
||||
}
|
||||
|
||||
void registerMonitoredDirectory(uint systemDirectoryHash)
|
||||
{
|
||||
_monitoredDirectoriesBloomFilter.storeHash(systemDirectoryHash);
|
||||
}
|
||||
|
||||
private:
|
||||
BloomFilter _monitoredDirectoriesBloomFilter;
|
||||
};
|
||||
|
||||
class ListenerClosure : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using CallbackFunction = std::function<void()>;
|
||||
ListenerClosure(CallbackFunction callback)
|
||||
: callback_(callback)
|
||||
{
|
||||
}
|
||||
|
||||
public slots:
|
||||
void closureSlot()
|
||||
{
|
||||
callback_();
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
private:
|
||||
CallbackFunction callback_;
|
||||
};
|
||||
|
||||
class SocketApiJob : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SocketApiJob(const QString &jobId, SocketListener *socketListener, const QJsonObject &arguments)
|
||||
: _jobId(jobId)
|
||||
, _socketListener(socketListener)
|
||||
, _arguments(arguments)
|
||||
{
|
||||
}
|
||||
|
||||
void resolve(const QString &response = QString())
|
||||
{
|
||||
_socketListener->sendMessage(QLatin1String("RESOLVE|") + _jobId + '|' + response);
|
||||
}
|
||||
|
||||
void resolve(const QJsonObject &response) { resolve(QJsonDocument{ response }.toJson()); }
|
||||
|
||||
const QJsonObject &arguments() { return _arguments; }
|
||||
|
||||
void reject(const QString &response)
|
||||
{
|
||||
_socketListener->sendMessage(QLatin1String("REJECT|") + _jobId + '|' + response);
|
||||
}
|
||||
|
||||
private:
|
||||
QString _jobId;
|
||||
SocketListener *_socketListener;
|
||||
QJsonObject _arguments;
|
||||
};
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(OCC::SocketListener *)
|
||||
|
||||
#endif // SOCKETAPI_P_H
|
|
@ -59,6 +59,8 @@ OwncloudWizard::OwncloudWizard(QWidget *parent)
|
|||
, _resultPage(new OwncloudWizardResultPage)
|
||||
, _webViewPage(new WebViewPage(this))
|
||||
{
|
||||
setObjectName("owncloudWizard");
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setPage(WizardCommon::Page_ServerSetup, _setupPage);
|
||||
setPage(WizardCommon::Page_HttpCreds, _httpCredsPage);
|
||||
|
|
Loading…
Reference in a new issue