From: Manuel Nickschas Date: Fri, 8 Feb 2008 00:56:11 +0000 (+0000) Subject: The Core Configuration Wizard is back! teH rul! X-Git-Tag: 0.2.0-alpha1~100 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=0c9cd0eef379e1d3e10a75cc8506a7e65f95fd67 The Core Configuration Wizard is back! teH rul! Well, probably still has some rough edges, but you should be able to configure a Quassel Core from scratch now again. If you want to test this, be sure to remove both the database and the settings (.quassel/* and .config/Quassel Project/* on Linux). Oh, and I completely broke the client/core protocol again. Oh well. --- diff --git a/Quassel.kdevelop.filelist b/Quassel.kdevelop.filelist index 19f4d2eb..b6626fa9 100644 --- a/Quassel.kdevelop.filelist +++ b/Quassel.kdevelop.filelist @@ -154,12 +154,12 @@ src/qtui/chatwidget.cpp src/qtui/chatwidget.h src/qtui/configwizard.cpp src/qtui/configwizard.h +src/qtui/coreconfigwizard.cpp +src/qtui/coreconfigwizard.h src/qtui/coreconnectdlg.cpp src/qtui/coreconnectdlg.h src/qtui/debugconsole.cpp src/qtui/debugconsole.h -src/qtui/guisettings.cpp -src/qtui/guisettings.h src/qtui/inputwidget.cpp src/qtui/inputwidget.h src/qtui/mainwin.cpp diff --git a/src/client/clientsyncer.cpp b/src/client/clientsyncer.cpp index b8cf4b29..29b4973d 100644 --- a/src/client/clientsyncer.cpp +++ b/src/client/clientsyncer.cpp @@ -59,6 +59,10 @@ void ClientSyncer::coreHasData() { emit connectionError(msg["Error"].toString()); disconnectFromCore(); return; + } else if(msg["MsgType"] == "CoreSetupAck") { + emit coreSetupSuccess(); + } else if(msg["MsgType"] == "CoreSetupReject") { + emit coreSetupFailed(msg["Error"].toString()); } else if(msg["MsgType"] == "ClientLoginReject") { emit loginFailed(msg["Error"].toString()); } else if(msg["MsgType"] == "ClientLoginAck") { @@ -173,11 +177,21 @@ void ClientSyncer::clientInitAck(const QVariantMap &msg) { return; } emit connectionMsg(msg["CoreInfo"].toString()); - if(msg["LoginEnabled"].toBool()) { + if(!msg["Configured"].toBool()) { + // start wizard + emit startCoreSetup(msg["StorageBackends"].toList()); + } else if(msg["LoginEnabled"].toBool()) { emit startLogin(); } } +void ClientSyncer::doCoreSetup(const QVariant &setupData) { + QVariantMap setup; + setup["MsgType"] = "CoreSetupData"; + setup["SetupData"] = setupData; + SignalProxy::writeDataToDevice(socket, setup); +} + void ClientSyncer::loginToCore(const QString &user, const QString &passwd) { emit connectionMsg(tr("Logging in...")); QVariantMap clientLogin; diff --git a/src/client/clientsyncer.h b/src/client/clientsyncer.h index 9501d8de..95aee59f 100644 --- a/src/client/clientsyncer.h +++ b/src/client/clientsyncer.h @@ -51,6 +51,9 @@ class ClientSyncer : public QObject { void loginFailed(const QString &error); void loginSuccess(); void syncFinished(); + void startCoreSetup(const QVariantList &); + void coreSetupSuccess(); + void coreSetupFailed(const QString &error); public slots: @@ -79,6 +82,8 @@ class ClientSyncer : public QObject { void syncToCore(const QVariantMap &sessionState); void sessionStateReceived(const QVariantMap &state); + void doCoreSetup(const QVariant &setupData); + private: QPointer socket; quint32 blockSize; diff --git a/src/core/abstractsqlstorage.cpp b/src/core/abstractsqlstorage.cpp index 99289f98..b20d1dcd 100644 --- a/src/core/abstractsqlstorage.cpp +++ b/src/core/abstractsqlstorage.cpp @@ -50,7 +50,7 @@ QSqlDatabase AbstractSqlStorage::logDb() { return db; if(!openDb()) { - qWarning() << "Unable to Open Database" << engineName(); + qWarning() << "Unable to Open Database" << displayName(); qWarning() << " -" << db.lastError().text(); } @@ -116,9 +116,9 @@ QString AbstractSqlStorage::queryString(const QString &queryName, int version) { if(version == 0) version = schemaVersion(); - QFileInfo queryInfo(QString(":/SQL/%1/%2/%3.sql").arg(engineName()).arg(version).arg(queryName)); + QFileInfo queryInfo(QString(":/SQL/%1/%2/%3.sql").arg(displayName()).arg(version).arg(queryName)); if(!queryInfo.exists() || !queryInfo.isFile() || !queryInfo.isReadable()) { - qWarning() << "Unable to read SQL-Query" << queryName << "for Engine" << engineName(); + qWarning() << "Unable to read SQL-Query" << queryName << "for engine" << displayName(); return QString(); } @@ -151,7 +151,7 @@ QSqlQuery *AbstractSqlStorage::cachedQuery(const QString &queryName) { QStringList AbstractSqlStorage::setupQueries() { QStringList queries; - QDir dir = QDir(QString(":/SQL/%1/%2/").arg(engineName()).arg(schemaVersion())); + QDir dir = QDir(QString(":/SQL/%1/%2/").arg(displayName()).arg(schemaVersion())); foreach(QFileInfo fileInfo, dir.entryInfoList(QStringList() << "setup*", QDir::NoFilter, QDir::Name)) { queries << queryString(fileInfo.baseName()); } @@ -178,7 +178,7 @@ bool AbstractSqlStorage::setup(const QVariantMap &settings) { QStringList AbstractSqlStorage::upgradeQueries(int version) { QStringList queries; - QDir dir = QDir(QString(":/SQL/%1/%2/").arg(engineName()).arg(version)); + QDir dir = QDir(QString(":/SQL/%1/%2/").arg(displayName()).arg(version)); foreach(QFileInfo fileInfo, dir.entryInfoList(QStringList() << "upgrade*", QDir::NoFilter, QDir::Name)) { queries << queryString(fileInfo.baseName(), version); } @@ -212,7 +212,7 @@ int AbstractSqlStorage::schemaVersion() { int version; bool ok; - QDir dir = QDir(":/SQL/" + engineName()); + QDir dir = QDir(":/SQL/" + displayName()); foreach(QFileInfo fileInfo, dir.entryInfoList()) { if(!fileInfo.isDir()) continue; diff --git a/src/core/abstractsqlstorage.h b/src/core/abstractsqlstorage.h index 6fefb787..a85fdbbf 100644 --- a/src/core/abstractsqlstorage.h +++ b/src/core/abstractsqlstorage.h @@ -34,10 +34,6 @@ public: AbstractSqlStorage(QObject *parent = 0); virtual ~AbstractSqlStorage(); - //! Returns the name of the storage backend engine - /** \return A virtual equivalent of displayName() */ - virtual QString engineName() { return ""; } - protected: bool init(const QVariantMap &settings = QVariantMap()); virtual void sync(); diff --git a/src/core/core.cpp b/src/core/core.cpp index 2e3da6d9..32b34f53 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -45,13 +45,17 @@ void Core::destroy() { instanceptr = 0; } -Core::Core() - : storage(0) -{ +Core::Core() : storage(0) { startTime = QDateTime::currentDateTime(); // for uptime :) - connect(&_storageSyncTimer, SIGNAL(timeout()), - this, SLOT(syncStorage())); + // Register storage backends here! + registerStorageBackend(new SqliteStorage(this)); + + if(!_storageBackends.count()) { + qWarning() << qPrintable(tr("Could not initialize any storage backend! Exiting...")); + exit(1); // TODO make this less brutal (especially for mono client -> popup) + } + connect(&_storageSyncTimer, SIGNAL(timeout()), this, SLOT(syncStorage())); _storageSyncTimer.start(10 * 60 * 1000); // in msecs } @@ -59,49 +63,46 @@ void Core::init() { configured = false; CoreSettings cs; - if(!(configured = initStorage(cs.databaseSettings().toMap()))) { - qWarning("Core is currently not configured!"); - } - connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection())); - startListening(cs.port()); - guiUser = 0; - -} + // TODO migrate old db settings -bool Core::initStorage(QVariantMap dbSettings, bool setup) { - QString engine = dbSettings["Type"].toString().toLower(); - - if(storage) { - qDebug() << "Deleting old storage object."; - storage->deleteLater(); - storage = 0; - } - - // FIXME register new storageProviders here - if(engine == "sqlite" && SqliteStorage::isAvailable()) { - storage = new SqliteStorage(this); - connect(storage, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)), this, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &))); - } else { - qWarning() << "Selected StorageBackend is not available:" << dbSettings["Type"].toString(); - return configured = false; - } + if(!(configured = initStorage(cs.storageSettings().toMap()))) { + qWarning("Core is currently not configured!"); - if(setup && !storage->setup(dbSettings)) { - return configured = false; + // try to migrate old settings + QVariantMap old = cs.oldDbSettings().toMap(); + if(old.count() && old["Type"].toString() == "SQlite") { + QVariantMap newSettings; + newSettings["Backend"] = "SQLite"; + if((configured = initStorage(newSettings))) { + qWarning("...but thankfully I found some old settings to migrate!"); + cs.setStorageSettings(newSettings); + } + } } - return configured = storage->init(dbSettings); + connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection())); + if(!startListening(cs.port())) exit(1); // TODO make this less brutal } Core::~Core() { - // FIXME properly shutdown the sessions + foreach(QTcpSocket *socket, blocksizes.keys()) { + socket->disconnectFromHost(); // disconnect local (i.e. non-authed) clients + } qDeleteAll(sessions); + qDeleteAll(_storageBackends); } -void Core::syncStorage() { - QMutexLocker locker(&mutex); - return instance()->storage->sync(); +/*** Session Restore ***/ + +void Core::saveState() { + CoreSettings s; + QVariantMap state; + QVariantList activeSessions; + foreach(UserId user, instance()->sessions.keys()) activeSessions << QVariant::fromValue(user); + state["CoreBuild"] = Global::quasselBuild; + state["ActiveSessions"] = activeSessions; + s.setCoreState(state); } void Core::restoreState() { @@ -122,18 +123,83 @@ void Core::restoreState() { UserId user = v.value(); instance()->createSession(user, true); } - qDebug() << "...done."; } } -void Core::saveState() { +/*** Core Setup ***/ + +QString Core::setupCore(const QVariant &setupData_) { + QVariantMap setupData = setupData_.toMap(); + QString user = setupData.take("AdminUser").toString(); + QString password = setupData.take("AdminPasswd").toString(); + if(user.isEmpty() || password.isEmpty()) { + return tr("Admin user or password not set."); + } + if(!initStorage(setupData, true)) { + return tr("Could not setup storage!"); + } CoreSettings s; - QVariantMap state; - QVariantList activeSessions; - foreach(UserId user, instance()->sessions.keys()) activeSessions << QVariant::fromValue(user); - state["CoreBuild"] = Global::quasselBuild; - state["ActiveSessions"] = activeSessions; - s.setCoreState(state); + //s.setStorageSettings(msg); + qDebug() << qPrintable(tr("Creating admin user...")); + mutex.lock(); + storage->addUser(user, password); + mutex.unlock(); + startListening(); // TODO check when we need this + return QString(); +} + +/*** Storage Handling ***/ + +bool Core::registerStorageBackend(Storage *backend) { + if(backend->isAvailable()) { + _storageBackends[backend->displayName()] = backend; + return true; + } else { + backend->deleteLater(); + return false; + } +} + +void Core::unregisterStorageBackend(Storage *backend) { + _storageBackends.remove(backend->displayName()); + backend->deleteLater(); +} + +// old db settings: +// "Type" => "sqlite" +bool Core::initStorage(QVariantMap dbSettings, bool setup) { + QString backend = dbSettings["Backend"].toString(); + if(backend.isEmpty()) { + //qWarning() << "No storage backend selected!"; + return configured = false; + } + + if(_storageBackends.contains(backend)) { + storage = _storageBackends[backend]; + } else { + qWarning() << "Selected storage backend is not available:" << backend; + return configured = false; + } + if(!storage->init(dbSettings)) { + if(!setup || !(storage->setup(dbSettings) && storage->init(dbSettings))) { + qWarning() << "Could not init storage!"; + storage = 0; + return configured = false; + } + } + // delete all other backends + foreach(Storage *s, _storageBackends.values()) { + if(s != storage) s->deleteLater(); + } + _storageBackends.clear(); + + connect(storage, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)), this, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &))); + return configured = true; +} + +void Core::syncStorage() { + QMutexLocker locker(&mutex); + if(storage) storage->sync(); } /*** Storage Access ***/ @@ -142,7 +208,7 @@ bool Core::createNetworkId(UserId user, NetworkInfo &info) { NetworkId networkId = instance()->storage->createNetworkId(user, info); if(!networkId.isValid()) return false; - + info.networkId = networkId; return true; } @@ -186,7 +252,7 @@ QList Core::requestBuffers(UserId user, QDateTime since) { bool Core::startListening(uint port) { if(!server.listen(QHostAddress::Any, port)) { - qWarning(QString(QString("Could not open GUI client port %1: %2").arg(port).arg(server.errorString())).toAscii()); + qWarning(qPrintable(QString("Could not open GUI client port %1: %2").arg(port).arg(server.errorString()))); return false; } qDebug() << "Listening for GUI clients on port" << server.serverPort(); @@ -200,7 +266,7 @@ void Core::stopListening() { void Core::incomingConnection() { // TODO implement SSL - while (server.hasPendingConnections()) { + while(server.hasPendingConnections()) { QTcpSocket *socket = server.nextPendingConnection(); connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected())); connect(socket, SIGNAL(readyRead()), this, SLOT(clientHasData())); @@ -221,53 +287,85 @@ void Core::clientHasData() { QVariant item; while(SignalProxy::readDataFromDevice(socket, blocksizes[socket], item)) { QVariantMap msg = item.toMap(); - if(!msg.contains("MsgType")) { - // Client is way too old, does not even use the current init format - qWarning() << qPrintable(tr("Antique client trying to connect... refusing.")); - socket->close(); - return; + processClientMessage(socket, msg); + } +} + +void Core::processClientMessage(QTcpSocket *socket, const QVariantMap &msg) { + if(!msg.contains("MsgType")) { + // Client is way too old, does not even use the current init format + qWarning() << qPrintable(tr("Antique client trying to connect... refusing.")); + socket->close(); + return; + } + // OK, so we have at least an init message format we can understand + if(msg["MsgType"] == "ClientInit") { + QVariantMap reply; + reply["CoreVersion"] = Global::quasselVersion; + reply["CoreDate"] = Global::quasselDate; + reply["CoreBuild"] = Global::quasselBuild; + // TODO: Make the core info configurable + int uptime = startTime.secsTo(QDateTime::currentDateTime()); + int updays = uptime / 86400; uptime %= 86400; + int uphours = uptime / 3600; uptime %= 3600; + int upmins = uptime / 60; + reply["CoreInfo"] = tr("Quassel Core Version %1 (Build >= %2)
" + "Up %3d%4h%5m (since %6)").arg(Global::quasselVersion).arg(Global::quasselBuild) + .arg(updays).arg(uphours,2,10,QChar('0')).arg(upmins,2,10,QChar('0')).arg(startTime.toString(Qt::TextDate)); + + reply["SupportSsl"] = false; + reply["LoginEnabled"] = true; + + // Just version information -- check it! + if(msg["ClientBuild"].toUInt() < Global::clientBuildNeeded) { + reply["MsgType"] = "ClientInitReject"; + reply["Error"] = tr("Your Quassel Client is too old!
" + "This core needs at least client version %1 (Build >= %2).
" + "Please consider upgrading your client.").arg(Global::quasselVersion).arg(Global::quasselBuild); + SignalProxy::writeDataToDevice(socket, reply); + qWarning() << qPrintable(tr("Client %1 too old, rejecting.").arg(socket->peerAddress().toString())); + socket->close(); return; + } + // check if we are configured, start wizard otherwise + if(!configured) { + reply["Configured"] = false; + QList backends; + foreach(Storage *backend, _storageBackends.values()) { + QVariantMap v; + v["DisplayName"] = backend->displayName(); + v["Description"] = backend->description(); + backends.append(v); + } + reply["StorageBackends"] = backends; + reply["LoginEnabled"] = false; + } else { + reply["Configured"] = true; + } + clientInfo[socket] = msg; // store for future reference + reply["MsgType"] = "ClientInitAck"; + SignalProxy::writeDataToDevice(socket, reply); + } else { + // for the rest, we need an initialized connection + if(!clientInfo.contains(socket)) { + QVariantMap reply; + reply["MsgType"] = "ClientLoginReject"; + reply["Error"] = tr("Client not initialized!
You need to send an init message before trying to login."); + SignalProxy::writeDataToDevice(socket, reply); + qWarning() << qPrintable(tr("Client %1 did not send an init message before trying to login, rejecting.").arg(socket->peerAddress().toString())); + socket->close(); return; } - // OK, so we have at least an init message format we can understand - if(msg["MsgType"] == "ClientInit") { + if(msg["MsgType"] == "CoreSetupData") { QVariantMap reply; - reply["CoreVersion"] = Global::quasselVersion; - reply["CoreDate"] = Global::quasselDate; - reply["CoreBuild"] = Global::quasselBuild; - // TODO: Make the core info configurable - int uptime = startTime.secsTo(QDateTime::currentDateTime()); - int updays = uptime / 86400; uptime %= 86400; - int uphours = uptime / 3600; uptime %= 3600; - int upmins = uptime / 60; - reply["CoreInfo"] = tr("Quassel Core Version %1 (Build >= %2)
" - "Up %3d%4h%5m (since %6)").arg(Global::quasselVersion).arg(Global::quasselBuild) - .arg(updays).arg(uphours,2,10,QChar('0')).arg(upmins,2,10,QChar('0')).arg(startTime.toString(Qt::TextDate)); - - reply["SupportSsl"] = false; - reply["LoginEnabled"] = true; - // TODO: check if we are configured, start wizard otherwise - - // Just version information -- check it! - if(msg["ClientBuild"].toUInt() < Global::clientBuildNeeded) { - reply["MsgType"] = "ClientInitReject"; - reply["Error"] = tr("Your Quassel Client is too old!
" - "This core needs at least client version %1 (Build >= %2).
" - "Please consider upgrading your client.").arg(Global::quasselVersion).arg(Global::quasselBuild); - SignalProxy::writeDataToDevice(socket, reply); - qWarning() << qPrintable(tr("Client %1 too old, rejecting.").arg(socket->peerAddress().toString())); - socket->close(); return; + QString result = setupCore(msg["SetupData"]); + if(!result.isEmpty()) { + reply["MsgType"] = "CoreSetupReject"; + reply["Error"] = result; + } else { + reply["MsgType"] = "CoreSetupAck"; } - clientInfo[socket] = msg; // store for future reference - reply["MsgType"] = "ClientInitAck"; SignalProxy::writeDataToDevice(socket, reply); } else if(msg["MsgType"] == "ClientLogin") { QVariantMap reply; - if(!clientInfo.contains(socket)) { - reply["MsgType"] = "ClientLoginReject"; - reply["Error"] = tr("Client not initialized!
You need to send an init message before trying to login."); - SignalProxy::writeDataToDevice(socket, reply); - qWarning() << qPrintable(tr("Client %1 did not send an init message before trying to login, rejecting.").arg(socket->peerAddress().toString())); - socket->close(); return; - } mutex.lock(); UserId uid = storage->validateUser(msg["User"].toString(), msg["Password"].toString()); mutex.unlock(); @@ -275,48 +373,27 @@ void Core::clientHasData() { reply["MsgType"] = "ClientLoginReject"; reply["Error"] = tr("Invalid username or password!
The username/password combination you supplied could not be found in the database."); SignalProxy::writeDataToDevice(socket, reply); - continue; + return; } reply["MsgType"] = "ClientLoginAck"; SignalProxy::writeDataToDevice(socket, reply); qDebug() << qPrintable(tr("Client %1 initialized and authentificated successfully as \"%2\".").arg(socket->peerAddress().toString(), msg["User"].toString())); setupClientSession(socket, uid); } - //socket->close(); return; - /* - // we need to auth the client - try { - QVariantMap msg = item.toMap(); - if (msg["GuiProtocol"].toUInt() != GUI_PROTOCOL) { - throw Exception("GUI client version mismatch"); - } - if (configured) { - processClientInit(socket, msg); - } else { - processCoreSetup(socket, msg); - } - } catch(Storage::AuthError) { - qWarning() << "Authentification error!"; // FIXME: send auth error to client - socket->close(); - return; - } catch(Exception e) { - qWarning() << "Client init error:" << e.msg(); - socket->close(); - return; - } */ } } // Potentially called during the initialization phase (before handing the connection off to the session) void Core::clientDisconnected() { - QTcpSocket *socket = dynamic_cast(sender()); + QTcpSocket *socket = dynamic_cast(sender()); // Note: This might be a QObject* already (if called by ~Core())! + Q_ASSERT(socket); blocksizes.remove(socket); clientInfo.remove(socket); - qDebug() << qPrintable(tr("Client %1 disconnected.").arg(socket->peerAddress().toString())); + qDebug() << qPrintable(tr("Non-authed client disconnected.")); socket->deleteLater(); socket = 0; - // make server listen again if still not configured FIXME + // make server listen again if still not configured if (!configured) { startListening(); } @@ -325,6 +402,7 @@ void Core::clientDisconnected() { // Suggestion: kill sessions if they are not connected to any network and client. } +/* void Core::processCoreSetup(QTcpSocket *socket, QVariantMap &msg) { if(msg["HasSettings"].toBool()) { QVariantMap auth; @@ -359,6 +437,7 @@ void Core::processCoreSetup(QTcpSocket *socket, QVariantMap &msg) { SignalProxy::writeDataToDevice(socket, reply); } } +*/ void Core::setupClientSession(QTcpSocket *socket, UserId uid) { // Find or create session for validated user @@ -367,6 +446,8 @@ void Core::setupClientSession(QTcpSocket *socket, UserId uid) { else sess = createSession(uid); // Hand over socket, session then sends state itself disconnect(socket, 0, this, 0); + blocksizes.remove(socket); + clientInfo.remove(socket); if(!sess) { qWarning() << qPrintable(tr("Could not initialize session for client %1!").arg(socket->peerAddress().toString())); socket->close(); @@ -384,14 +465,3 @@ SessionThread *Core::createSession(UserId uid, bool restore) { sess->start(); return sess; } - -QStringList Core::availableStorageProviders() { - QStringList storageProviders; - if (SqliteStorage::isAvailable()) { - storageProviders.append(SqliteStorage::displayName()); - } - // TODO: temporary - // storageProviders.append("MySQL"); - - return storageProviders; -} diff --git a/src/core/core.h b/src/core/core.h index 091c0f71..6b0126ca 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -158,11 +158,13 @@ class Core : public QObject { SessionThread *createSession(UserId userId, bool restoreState = false); void setupClientSession(QTcpSocket *socket, UserId uid); - void processCoreSetup(QTcpSocket *socket, QVariantMap &msg); + void processClientMessage(QTcpSocket *socket, const QVariantMap &msg); + //void processCoreSetup(QTcpSocket *socket, QVariantMap &msg); + QString setupCore(const QVariant &setupData); - QStringList availableStorageProviders(); + bool registerStorageBackend(Storage *); + void unregisterStorageBackend(Storage *); - UserId guiUser; QHash sessions; Storage *storage; QTimer _storageSyncTimer; @@ -171,6 +173,8 @@ class Core : public QObject { QHash blocksizes; QHash clientInfo; + QHash _storageBackends; + QDateTime startTime; bool configured; diff --git a/src/core/coresettings.cpp b/src/core/coresettings.cpp index 28fb7d59..b7aae856 100644 --- a/src/core/coresettings.cpp +++ b/src/core/coresettings.cpp @@ -28,12 +28,17 @@ CoreSettings::CoreSettings(const QString group) : Settings(group, Global::coreAp CoreSettings::~CoreSettings() { } -void CoreSettings::setDatabaseSettings(const QVariant &data) { - setLocalValue("DatabaseSettings", data); +void CoreSettings::setStorageSettings(const QVariant &data) { + setLocalValue("StorageSettings", data); } -QVariant CoreSettings::databaseSettings(const QVariant &def) { - return localValue("DatabaseSettings", def); +QVariant CoreSettings::storageSettings(const QVariant &def) { + return localValue("StorageSettings", def); +} + +// FIXME remove +QVariant CoreSettings::oldDbSettings() { + return localValue("DatabaseSettings"); } void CoreSettings::setPort(const uint &port) { diff --git a/src/core/coresettings.h b/src/core/coresettings.h index 0e204d11..749c8ec1 100644 --- a/src/core/coresettings.h +++ b/src/core/coresettings.h @@ -30,8 +30,10 @@ class CoreSettings : public Settings { virtual ~CoreSettings(); CoreSettings(const QString group = "Core"); - void setDatabaseSettings(const QVariant &data); - QVariant databaseSettings(const QVariant &def = QVariant()); + void setStorageSettings(const QVariant &data); + QVariant storageSettings(const QVariant &def = QVariant()); + + QVariant oldDbSettings(); // FIXME remove void setPort(const uint &port); uint port(const uint &def = Global::defaultPort); diff --git a/src/core/sqlitestorage.cpp b/src/core/sqlitestorage.cpp index 49476e9c..900a1931 100644 --- a/src/core/sqlitestorage.cpp +++ b/src/core/sqlitestorage.cpp @@ -33,17 +33,19 @@ SqliteStorage::SqliteStorage(QObject *parent) SqliteStorage::~SqliteStorage() { } -bool SqliteStorage::isAvailable() { +bool SqliteStorage::isAvailable() const { if(!QSqlDatabase::isDriverAvailable("QSQLITE")) return false; return true; } -QString SqliteStorage::displayName() { +QString SqliteStorage::displayName() const { return QString("SQLite"); } -QString SqliteStorage::engineName() { - return SqliteStorage::displayName(); +QString SqliteStorage::description() const { + return tr("SQLite is a file-based database engine that does not require any setup. It is suitable for small and medium-sized " + "databases that do not require access via network. Use SQLite if your Quassel Core should store its data on the same machine " + "it is running on, and if you only expect a few users to use your core."); } int SqliteStorage::installedSchemaVersion() { diff --git a/src/core/sqlitestorage.h b/src/core/sqlitestorage.h index adaabe7c..b6ba7482 100644 --- a/src/core/sqlitestorage.h +++ b/src/core/sqlitestorage.h @@ -33,13 +33,14 @@ class SqliteStorage : public AbstractSqlStorage { public: SqliteStorage(QObject *parent = 0); virtual ~SqliteStorage(); - + public slots: /* General */ - static bool isAvailable(); - static QString displayName(); - virtual QString engineName() ; + bool isAvailable() const; + QString displayName() const; + QString description() const; + // TODO: Add functions for configuring the backlog handling, i.e. defining auto-cleanup settings etc /* User handling */ diff --git a/src/core/storage.h b/src/core/storage.h index 6470b405..649bdc46 100644 --- a/src/core/storage.h +++ b/src/core/storage.h @@ -42,32 +42,36 @@ class Storage : public QObject { * prerequisites are in place (e.g. we have an appropriate DB driver etc.). * \return True if and only if the storage class can be successfully used. */ - static bool isAvailable() { return false; } + virtual bool isAvailable() const = 0; //! Returns the display name of the storage backend - /** \return A string that can be used by the GUI to describe the storage backend */ - static QString displayName() { return ""; } + /** \return A string that can be used by the client to name the storage backend */ + virtual QString displayName() const = 0; + + //! Returns a description of this storage backend + /** \return A string that can be displayed by the client to describe the storage backend */ + virtual QString description() const = 0; //! Setup the storage provider. /** This prepares the storage provider (e.g. create tables, etc.) for use within Quassel. * \param settings Hostname, port, username, password, ... * \return True if and only if the storage provider was initialized successfully. */ - virtual bool setup(const QVariantMap &settings = QVariantMap()) { Q_UNUSED(settings); return false; } - + virtual bool setup(const QVariantMap &settings = QVariantMap()) = 0; + //! Initialize the storage provider /** \param settings Hostname, port, username, password, ... * \return True if and only if the storage provider was initialized successfully. */ virtual bool init(const QVariantMap &settings = QVariantMap()) = 0; - //! Makes temp data persistent - /** This Method is periodically called by the Quassel Core to make temporary - * data persistant. This reduces the data loss drastically in the - * unlikely case of a Core crash. - */ + //! Makes temp data persistent + /** This Method is periodically called by the Quassel Core to make temporary + * data persistant. This reduces the data loss drastically in the + * unlikely case of a Core crash. + */ virtual void sync() = 0; - + // TODO: Add functions for configuring the backlog handling, i.e. defining auto-cleanup settings etc /* User handling */ diff --git a/src/qtui/coreconfigwizard.cpp b/src/qtui/coreconfigwizard.cpp new file mode 100644 index 00000000..792fbc0b --- /dev/null +++ b/src/qtui/coreconfigwizard.cpp @@ -0,0 +1,259 @@ +/*************************************************************************** + * Copyright (C) 2005-08 by the Quassel IRC Team * + * devel@quassel-irc.org * + * * + * 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) version 3. * + * * + * 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. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include + +#include "coreconfigwizard.h" + +//#include "client.h" +//#include "identitiessettingspage.h" + +CoreConfigWizard::CoreConfigWizard(const QList &backends, QWidget *parent) : QWizard(parent) { + foreach(QVariant v, backends) _backends[v.toMap()["DisplayName"].toString()] = v; + setPage(IntroPage, new CoreConfigWizardPages::IntroPage(this)); + setPage(AdminUserPage, new CoreConfigWizardPages::AdminUserPage(this)); + setPage(StorageSelectionPage, new CoreConfigWizardPages::StorageSelectionPage(_backends, this)); + syncPage = new CoreConfigWizardPages::SyncPage(this); + connect(syncPage, SIGNAL(setupCore(const QString &)), this, SLOT(prepareCoreSetup(const QString &))); + setPage(SyncPage, syncPage); + syncRelayPage = new CoreConfigWizardPages::SyncRelayPage(this); + connect(syncRelayPage, SIGNAL(startOver()), this, SLOT(startOver())); + setPage(SyncRelayPage, syncRelayPage); + //setPage(Page_StorageDetails, new StorageDetailsPage()); + //setPage(Page_Conclusion, new ConclusionPage(storageProviders)); + + setStartId(IntroPage); + //setStartId(StorageSelectionPage); + +#ifndef Q_WS_MAC + setWizardStyle(ModernStyle); +#endif + + setOption(HaveHelpButton, false); + setOption(NoBackButtonOnStartPage, true); + setOption(HaveNextButtonOnLastPage, false); + setOption(HaveFinishButtonOnEarlyPages, false); + setOption(NoCancelButton, true); + setOption(IndependentPages, true); + //setOption(ExtendedWatermarkPixmap, true); + + setModal(true); + + setWindowTitle(tr("Core Configuration Wizard")); + setPixmap(QWizard::LogoPixmap, QPixmap(":icons/quassel-icon.png")); +} + +QHash CoreConfigWizard::backends() const { + return _backends; +} + +void CoreConfigWizard::prepareCoreSetup(const QString &backend) { + // Prevent the user from changing any settings he already specified... + foreach(int idx, visitedPages()) page(idx)->setEnabled(false); + QVariantMap foo; + foo["AdminUser"] = field("adminUser.user").toString(); + foo["AdminPasswd"] = field("adminUser.password").toString(); + foo["Backend"] = backend; + emit setupCore(foo); +} + +void CoreConfigWizard::coreSetupSuccess() { + syncPage->setStatus(tr("Your core has been successfully configured. Logging you in...")); + syncPage->setError(false); + syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error); + QVariantMap loginData; + loginData["User"] = field("adminUser.user"); + loginData["Password"] = field("adminUser.password"); + loginData["RememberPasswd"] = field("adminUser.rememberPasswd"); + emit loginToCore(loginData); +} + +void CoreConfigWizard::coreSetupFailed(const QString &error) { + syncPage->setStatus(tr("Core configuration failed:
%1
Press Next to start over.").arg(error)); + syncPage->setError(true); + syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error); + //foreach(int idx, visitedPages()) page(idx)->setEnabled(true); + //setStartId(SyncPage); + //restart(); + +} + +void CoreConfigWizard::startOver() { + foreach(int idx, visitedPages()) page(idx)->setEnabled(true); + setStartId(CoreConfigWizard::AdminUserPage); + restart(); +} + +void CoreConfigWizard::loginSuccess() { + syncPage->setStatus(tr("Your are now logged into your freshly configured Quassel Core!
" + "Please remember to configure your identities and networks now.")); + syncPage->setComplete(true); + syncPage->setFinalPage(true); +} + +void CoreConfigWizard::syncFinished() { + // TODO: display identities and networks settings if appropriate! + // accept(); +} + +namespace CoreConfigWizardPages { + +/*** Intro Page ***/ + +IntroPage::IntroPage(QWidget *parent) : QWizardPage(parent) { + ui.setupUi(this); + setTitle(tr("Introduction")); + //setSubTitle(tr("foobar")); + //setPixmap(QWizard::WatermarkPixmap, QPixmap(":icons/quassel-icon.png")); + +} + +int IntroPage::nextId() const { + return CoreConfigWizard::AdminUserPage; + +} + +/*** Admin User Page ***/ + +AdminUserPage::AdminUserPage(QWidget *parent) : QWizardPage(parent) { + ui.setupUi(this); + setTitle(tr("Create User Account")); + setSubTitle(tr("First, we will create a user account on the core. This first user will have administrator privileges.")); + + registerField("adminUser.user*", ui.user); + registerField("adminUser.password*", ui.password); + registerField("adminUser.password2*", ui.password2); + registerField("adminUser.rememberPasswd", ui.rememberPasswd); + + //ui.user->setText("foo"); + //ui.password->setText("foo"); + //ui.password2->setText("foo"); +} + +int AdminUserPage::nextId() const { + return CoreConfigWizard::StorageSelectionPage; + +} + +bool AdminUserPage::isComplete() const { + bool ok = !ui.user->text().isEmpty() && !ui.password->text().isEmpty() && ui.password->text() == ui.password2->text(); + return ok; +} + +/*** Storage Selection Page ***/ + +StorageSelectionPage::StorageSelectionPage(const QHash &backends, QWidget *parent) : QWizardPage(parent) { + ui.setupUi(this); + _backends = backends; + + setTitle(tr("Select Storage Backend")); + setSubTitle(tr("Please select a database backend for the Quassel Core storage to store the backlog and other data in.")); + setCommitPage(true); + + registerField("storage.backend", ui.backendList); + + foreach(QString key, _backends.keys()) { + ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key); + } + + on_backendList_currentIndexChanged(); +} + +int StorageSelectionPage::nextId() const { + return CoreConfigWizard::SyncPage; +} + +QString StorageSelectionPage::selectedBackend() const { + return ui.backendList->currentText(); +} + +void StorageSelectionPage::on_backendList_currentIndexChanged() { + QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString(); + ui.description->setText(_backends[backend].toMap()["Description"].toString()); +} + +/*** Sync Page ***/ + +SyncPage::SyncPage(QWidget *parent) : QWizardPage(parent) { + ui.setupUi(this); + setTitle(tr("Storing Your Settings")); + setSubTitle(tr("Your settings are now stored in the core, and you will be logged in automatically.")); +} + +void SyncPage::initializePage() { + complete = false; + hasError = false; + QString backend = qobject_cast(wizard()->page(CoreConfigWizard::StorageSelectionPage))->selectedBackend(); + Q_ASSERT(!backend.isEmpty()); + ui.user->setText(wizard()->field("adminUser.user").toString()); + ui.backend->setText(backend); + emit setupCore(backend); +} + +int SyncPage::nextId() const { + if(!hasError) return -1; + return CoreConfigWizard::SyncRelayPage; +} + +bool SyncPage::isComplete() const { + return complete; +} + +void SyncPage::setStatus(const QString &status) { + ui.status->setText(status); +} + +void SyncPage::setError(bool e) { + hasError = e; +} + +void SyncPage::setComplete(bool c) { + complete = c; + completeChanged(); +} + +/*** Sync Relay Page ***/ + +SyncRelayPage::SyncRelayPage(QWidget *parent) : QWizardPage(parent) { + mode = Success; +} + +void SyncRelayPage::setMode(Mode m) { + mode = m; +} + +/* +void SyncRelayPage::initializePage() { + return; + if(mode == Success) { + wizard()->accept(); + } else { + emit startOver(); + } +} +*/ + +int SyncRelayPage::nextId() const { + emit startOver(); + return 0; +} + +}; /* namespace CoreConfigWizardPages */ diff --git a/src/qtui/coreconfigwizard.h b/src/qtui/coreconfigwizard.h new file mode 100644 index 00000000..1940fee4 --- /dev/null +++ b/src/qtui/coreconfigwizard.h @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (C) 2005-08 by the Quassel IRC Team * + * devel@quassel-irc.org * + * * + * 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) version 3. * + * * + * 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. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef _CORECONFIGWIZARD_H_ +#define _CORECONFIGWIZARD_H_ + +#include +#include +#include + +#include "ui_coreconfigwizardintropage.h" +#include "ui_coreconfigwizardadminuserpage.h" +#include "ui_coreconfigwizardstorageselectionpage.h" +#include "ui_coreconfigwizardsyncpage.h" + +namespace CoreConfigWizardPages { + class SyncPage; + class SyncRelayPage; +}; + +class CoreConfigWizard : public QWizard { + Q_OBJECT + + public: + enum { + IntroPage, + AdminUserPage, + StorageSelectionPage, + SyncPage, + SyncRelayPage, + StorageDetailsPage, + ConclusionPage + }; + + CoreConfigWizard(const QList &backends, QWidget *parent = 0); + QHash backends() const; + + signals: + void setupCore(const QVariant &setupData); + void loginToCore(const QVariantMap &loginData); + + public slots: + void loginSuccess(); + void syncFinished(); + + private slots: + void prepareCoreSetup(const QString &backend); + void coreSetupSuccess(); + void coreSetupFailed(const QString &); + void startOver(); + + private: + QHash _backends; + CoreConfigWizardPages::SyncPage *syncPage; + CoreConfigWizardPages::SyncRelayPage *syncRelayPage; +}; + +namespace CoreConfigWizardPages { + + class IntroPage : public QWizardPage { + Q_OBJECT + + public: + IntroPage(QWidget *parent = 0); + int nextId() const; + private: + Ui::CoreConfigWizardIntroPage ui; + }; + + class AdminUserPage : public QWizardPage { + Q_OBJECT + + public: + AdminUserPage(QWidget *parent = 0); + int nextId() const; + bool isComplete() const; + private: + Ui::CoreConfigWizardAdminUserPage ui; + }; + + class StorageSelectionPage : public QWizardPage { + Q_OBJECT + + public: + StorageSelectionPage(const QHash &backends, QWidget *parent = 0); + int nextId() const; + QString selectedBackend() const; + private slots: + void on_backendList_currentIndexChanged(); + private: + Ui::CoreConfigWizardStorageSelectionPage ui; + QHash _backends; + }; + + class SyncPage : public QWizardPage { + Q_OBJECT + + public: + SyncPage(QWidget *parent = 0); + void initializePage(); + int nextId() const; + bool isComplete() const; + + public slots: + void setStatus(const QString &status); + void setError(bool); + void setComplete(bool); + + signals: + void setupCore(const QString &backend); + + private: + Ui::CoreConfigWizardSyncPage ui; + bool complete; + bool hasError; + }; + + class SyncRelayPage : public QWizardPage { + Q_OBJECT + + public: + SyncRelayPage(QWidget *parent = 0); + int nextId() const; + enum Mode { Success, Error }; + + public slots: + void setMode(Mode); + + signals: + void startOver() const; + + private: + Mode mode; + }; + +} + +#endif diff --git a/src/qtui/coreconnectdlg.cpp b/src/qtui/coreconnectdlg.cpp index 250c0085..a8dc4820 100644 --- a/src/qtui/coreconnectdlg.cpp +++ b/src/qtui/coreconnectdlg.cpp @@ -25,11 +25,13 @@ #include "clientsettings.h" #include "clientsyncer.h" +#include "coreconfigwizard.h" CoreConnectDlg::CoreConnectDlg(QWidget *parent, bool autoconnect) : QDialog(parent) { ui.setupUi(this); clientSyncer = new ClientSyncer(this); + wizard = 0; setAttribute(Qt::WA_DeleteOnClose); @@ -62,11 +64,12 @@ CoreConnectDlg::CoreConnectDlg(QWidget *parent, bool autoconnect) : QDialog(pare connect(clientSyncer, SIGNAL(startLogin()), this, SLOT(startLogin())); connect(clientSyncer, SIGNAL(loginFailed(const QString &)), this, SLOT(loginFailed(const QString &))); connect(clientSyncer, SIGNAL(loginSuccess()), this, SLOT(startSync())); + connect(clientSyncer, SIGNAL(startCoreSetup(const QVariantList &)), this, SLOT(startCoreConfig(const QVariantList &))); connect(clientSyncer, SIGNAL(sessionProgress(quint32, quint32)), this, SLOT(coreSessionProgress(quint32, quint32))); connect(clientSyncer, SIGNAL(networksProgress(quint32, quint32)), this, SLOT(coreNetworksProgress(quint32, quint32))); connect(clientSyncer, SIGNAL(channelsProgress(quint32, quint32)), this, SLOT(coreChannelsProgress(quint32, quint32))); connect(clientSyncer, SIGNAL(ircUsersProgress(quint32, quint32)), this, SLOT(coreIrcUsersProgress(quint32, quint32))); - connect(clientSyncer, SIGNAL(syncFinished()), this, SLOT(accept())); + connect(clientSyncer, SIGNAL(syncFinished()), this, SLOT(syncFinished())); connect(ui.user, SIGNAL(textChanged(const QString &)), this, SLOT(setLoginWidgetStates())); connect(ui.password, SIGNAL(textChanged(const QString &)), this, SLOT(setLoginWidgetStates())); @@ -283,18 +286,33 @@ void CoreConnectDlg::startLogin() { } void CoreConnectDlg::doLogin() { + QVariantMap loginData; + loginData["User"] = ui.user->text(); + loginData["Password"] = ui.password->text(); + loginData["RememberPasswd"] = ui.rememberPasswd->isChecked(); + doLogin(loginData); +} + +void CoreConnectDlg::doLogin(const QVariantMap &loginData) { + disconnect(ui.loginButtonBox, 0, this, 0); + connect(ui.loginButtonBox, SIGNAL(accepted()), this, SLOT(doLogin())); + connect(ui.loginButtonBox, SIGNAL(rejected()), this, SLOT(restartPhaseNull())); + ui.loginStack->setCurrentWidget(ui.loginCredentialsPage); ui.loginGroup->setTitle(tr("Logging in...")); ui.user->setDisabled(true); ui.password->setDisabled(true); ui.rememberPasswd->setDisabled(true); ui.loginButtonBox->button(QDialogButtonBox::Ok)->setDisabled(true); - accountData["User"] = ui.user->text(); - accountData["RememberPasswd"] = ui.rememberPasswd->isChecked(); - if(ui.rememberPasswd->isChecked()) accountData["Password"] = ui.password->text(); + accountData["User"] = loginData["User"]; + accountData["RememberPasswd"] = loginData["RememberPasswd"]; + if(loginData["RememberPasswd"].toBool()) accountData["Password"] = loginData["Password"]; else accountData.remove("Password"); + ui.user->setText(loginData["User"].toString()); + ui.password->setText(loginData["Password"].toString()); + ui.rememberPasswd->setChecked(loginData["RememberPasswd"].toBool()); CoreAccountSettings s; s.storeAccountData(account, accountData); - clientSyncer->loginToCore(ui.user->text(), ui.password->text()); + clientSyncer->loginToCore(loginData["User"].toString(), loginData["Password"].toString()); } void CoreConnectDlg::setLoginWidgetStates() { @@ -302,6 +320,10 @@ void CoreConnectDlg::setLoginWidgetStates() { } void CoreConnectDlg::loginFailed(const QString &error) { + if(wizard) { + wizard->reject(); + } + ui.loginStack->setCurrentWidget(ui.loginCredentialsPage); ui.loginGroup->setTitle(tr("Login")); ui.user->setEnabled(true); ui.password->setEnabled(true); @@ -312,6 +334,42 @@ void CoreConnectDlg::loginFailed(const QString &error) { doingAutoConnect = false; } +void CoreConnectDlg::startCoreConfig(const QVariantList &backends) { + storageBackends = backends; + ui.loginStack->setCurrentWidget(ui.coreConfigPage); + + //on_launchCoreConfigWizard_clicked(); + +} + +void CoreConnectDlg::on_launchCoreConfigWizard_clicked() { + Q_ASSERT(!wizard); + wizard = new CoreConfigWizard(storageBackends, this); + connect(wizard, SIGNAL(setupCore(const QVariant &)), clientSyncer, SLOT(doCoreSetup(const QVariant &))); + connect(wizard, SIGNAL(loginToCore(const QVariantMap &)), this, SLOT(doLogin(const QVariantMap &))); + connect(clientSyncer, SIGNAL(coreSetupSuccess()), wizard, SLOT(coreSetupSuccess())); + connect(clientSyncer, SIGNAL(coreSetupFailed(const QString &)), wizard, SLOT(coreSetupFailed(const QString &))); + connect(wizard, SIGNAL(accepted()), this, SLOT(configWizardAccepted())); + connect(wizard, SIGNAL(rejected()), this, SLOT(configWizardRejected())); + connect(clientSyncer, SIGNAL(loginSuccess()), wizard, SLOT(loginSuccess())); + connect(clientSyncer, SIGNAL(syncFinished()), wizard, SLOT(syncFinished())); + wizard->show(); +} + +void CoreConnectDlg::configWizardAccepted() { + + wizard->deleteLater(); + wizard = 0; +} + +void CoreConnectDlg::configWizardRejected() { + + wizard->deleteLater(); + wizard = 0; + //exit(1); // FIXME +} + + /************************************************************ * Phase Three: Syncing ************************************************************/ @@ -335,7 +393,6 @@ void CoreConnectDlg::startSync() { ui.loginButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } - void CoreConnectDlg::coreSessionProgress(quint32 val, quint32 max) { ui.sessionProgress->setRange(0, max); ui.sessionProgress->setValue(val); @@ -379,6 +436,15 @@ void CoreConnectDlg::coreIrcUsersProgress(quint32 val, quint32 max) { } } +void CoreConnectDlg::syncFinished() { + if(!wizard) accept(); + else { + hide(); + disconnect(wizard, 0, this, 0); + connect(wizard, SIGNAL(finished(int)), this, SLOT(accept())); + } +} + /***************************************************************************************** * CoreAccountEditDlg *****************************************************************************************/ diff --git a/src/qtui/coreconnectdlg.h b/src/qtui/coreconnectdlg.h index 69e3839b..42638e51 100644 --- a/src/qtui/coreconnectdlg.h +++ b/src/qtui/coreconnectdlg.h @@ -29,6 +29,7 @@ #include "ui_coreaccounteditdlg.h" class ClientSyncer; +class CoreConfigWizard; class CoreConnectDlg : public QDialog { Q_OBJECT @@ -64,12 +65,18 @@ class CoreConnectDlg : public QDialog { /*** Phase Two: Login ***/ void startLogin(); void doLogin(); + void doLogin(const QVariantMap &loginData); void loginFailed(const QString &); + void startCoreConfig(const QVariantList &backends); + void configWizardAccepted(); + void configWizardRejected(); + void on_launchCoreConfigWizard_clicked(); void setLoginWidgetStates(); /*** Phase Three: Sync ***/ void startSync(); + void syncFinished(); void coreSessionProgress(quint32, quint32); void coreNetworksProgress(quint32, quint32); @@ -86,7 +93,10 @@ class CoreConnectDlg : public QDialog { bool doingAutoConnect; + QVariantList storageBackends; + ClientSyncer *clientSyncer; + CoreConfigWizard *wizard; }; class CoreAccountEditDlg : public QDialog { diff --git a/src/qtui/inputwidget.h b/src/qtui/inputwidget.h index 485ac671..86a76938 100644 --- a/src/qtui/inputwidget.h +++ b/src/qtui/inputwidget.h @@ -28,7 +28,7 @@ #include "buffermodel.h" #include "bufferinfo.h" #include "identity.h" -#include "network.h"; +#include "network.h" class InputWidget : public QWidget { Q_OBJECT diff --git a/src/qtui/qtui.pri b/src/qtui/qtui.pri index d1d6da2a..29c0e021 100644 --- a/src/qtui/qtui.pri +++ b/src/qtui/qtui.pri @@ -1,16 +1,17 @@ DEPMOD = uisupport common client QT_MOD = core gui network -SRCS += bufferwidget.cpp chatline-old.cpp chatwidget.cpp coreconnectdlg.cpp configwizard.cpp debugconsole.cpp inputwidget.cpp \ +SRCS += bufferwidget.cpp chatline-old.cpp chatwidget.cpp coreconfigwizard.cpp coreconnectdlg.cpp configwizard.cpp debugconsole.cpp inputwidget.cpp \ mainwin.cpp nicklistwidget.cpp qtui.cpp qtuistyle.cpp settingsdlg.cpp settingspagedlg.cpp \ topicwidget.cpp verticaldock.cpp -HDRS += bufferwidget.h chatline-old.h chatwidget.h configwizard.h debugconsole.h inputwidget.h \ +HDRS += bufferwidget.h chatline-old.h chatwidget.h coreconfigwizard.h configwizard.h debugconsole.h inputwidget.h \ coreconnectdlg.h mainwin.h nicklistwidget.h qtui.h qtuistyle.h settingsdlg.h settingspagedlg.h \ topicwidget.h verticaldock.h FORMNAMES = mainwin.ui coreaccounteditdlg.ui coreconnectdlg.ui bufferviewwidget.ui bufferwidget.ui nicklistwidget.ui settingsdlg.ui \ - settingspagedlg.ui topicwidget.ui debugconsole.ui inputwidget.ui + settingspagedlg.ui topicwidget.ui debugconsole.ui inputwidget.ui \ + coreconfigwizardintropage.ui coreconfigwizardadminuserpage.ui coreconfigwizardstorageselectionpage.ui coreconfigwizardsyncpage.ui for(ui, FORMNAMES) { FRMS += ui/$${ui} diff --git a/src/qtui/settingsdlg.cpp b/src/qtui/settingsdlg.cpp index 035993b9..b02e7117 100644 --- a/src/qtui/settingsdlg.cpp +++ b/src/qtui/settingsdlg.cpp @@ -113,7 +113,6 @@ void SettingsDlg::itemSelected() { void SettingsDlg::setButtonStates() { SettingsPage *sp = currentPage(); - ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(sp && sp->hasChanged()); ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(sp && sp->hasChanged()); ui.buttonBox->button(QDialogButtonBox::Reset)->setEnabled(sp && sp->hasChanged()); ui.buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(sp && sp->hasDefaults()); diff --git a/src/qtui/ui/coreconfigwizardadminuserpage.ui b/src/qtui/ui/coreconfigwizardadminuserpage.ui new file mode 100644 index 00000000..fcab0778 --- /dev/null +++ b/src/qtui/ui/coreconfigwizardadminuserpage.ui @@ -0,0 +1,95 @@ + + CoreConfigWizardAdminUserPage + + + + 0 + 0 + 415 + 273 + + + + Form + + + + + + + + Username: + + + + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + Repeat password: + + + + + + + QLineEdit::Password + + + + + + + Remember password + + + + + + + + + <em>Note: Adding more users and changing your username/password has not been implemented yet. + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Qt::Vertical + + + + 405 + 51 + + + + + + + + + diff --git a/src/qtui/ui/coreconfigwizardintropage.ui b/src/qtui/ui/coreconfigwizardintropage.ui new file mode 100644 index 00000000..2c14ad0c --- /dev/null +++ b/src/qtui/ui/coreconfigwizardintropage.ui @@ -0,0 +1,30 @@ + + CoreConfigWizardIntroPage + + + + 0 + 0 + 430 + 202 + + + + Form + + + + + + This wizard will guide you through the setup of your Quassel Core. + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + diff --git a/src/qtui/ui/coreconfigwizardstorageselectionpage.ui b/src/qtui/ui/coreconfigwizardstorageselectionpage.ui new file mode 100644 index 00000000..261426b8 --- /dev/null +++ b/src/qtui/ui/coreconfigwizardstorageselectionpage.ui @@ -0,0 +1,92 @@ + + CoreConfigWizardStorageSelectionPage + + + + 0 + 0 + 400 + 161 + + + + Form + + + + + + + + Storage Backend: + + + + + + + + 0 + 0 + + + + QComboBox::InsertAtBottom + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Description + + + + + + Foobar + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + + Qt::Vertical + + + + 392 + 51 + + + + + + + + + diff --git a/src/qtui/ui/coreconfigwizardsyncpage.ui b/src/qtui/ui/coreconfigwizardsyncpage.ui new file mode 100644 index 00000000..610c17ac --- /dev/null +++ b/src/qtui/ui/coreconfigwizardsyncpage.ui @@ -0,0 +1,116 @@ + + CoreConfigWizardSyncPage + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Your Choices + + + + + + + + + + + 75 + true + + + + Admin User: + + + + + + + foo + + + + + + + + 75 + true + + + + Storage Backend: + + + + + + + bar + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Please wait while your settings are being transmitted to the core... + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + diff --git a/src/qtui/ui/coreconnectdlg.ui b/src/qtui/ui/coreconnectdlg.ui index 91ce6cad..f53ea507 100644 --- a/src/qtui/ui/coreconnectdlg.ui +++ b/src/qtui/ui/coreconnectdlg.ui @@ -5,8 +5,8 @@ 0 0 - 511 - 389 + 493 + 331 @@ -21,7 +21,7 @@ - 2 + 1 @@ -46,7 +46,7 @@ Edit... - :/16x16/actions/oxygen/16x16/actions/configure.png + @@ -260,6 +260,95 @@ + + + + + + Qt::Vertical + + + + 20 + 71 + + + + + + + + Configure your Quassel Core + + + + + + The Quassel Core you are connected to is not configured yet. You may now launch a configuration wizard that helps you setting up your Core. + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Launch Wizard + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/src/uisupport/uisettings.h b/src/uisupport/uisettings.h index 2ce7ccb9..b4d653b5 100644 --- a/src/uisupport/uisettings.h +++ b/src/uisupport/uisettings.h @@ -26,7 +26,7 @@ class UiSettings : public ClientSettings { public: - UiSettings(const QString &group = "UI"); + UiSettings(const QString &group = "Ui"); void setValue(const QString &key, const QVariant &data); QVariant value(const QString &key, const QVariant &def = QVariant()); diff --git a/version.inc b/version.inc index bf713bfa..741f6a58 100644 --- a/version.inc +++ b/version.inc @@ -4,15 +4,15 @@ { using namespace Global; quasselVersion = "0.2.0-pre"; - quasselDate = "2008-02-06"; - quasselBuild = 478; + quasselDate = "2008-02-08"; + quasselBuild = 480; //! Minimum client build number the core needs - clientBuildNeeded = 464; + clientBuildNeeded = 480; clientVersionNeeded = quasselVersion; //! Minimum core build number the client needs - coreBuildNeeded = 464; + coreBuildNeeded = 480; coreVersionNeeded = quasselVersion; }