5119f17d88069bf8b894f2e5929873afa1534032
[quassel.git] / src / qtui / coreconfigwizard.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2016 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include <QDebug>
22 #include <QAbstractButton>
23 #include <QFormLayout>
24 #include <QIcon>
25 #include <QSpinBox>
26
27 #include "coreconfigwizard.h"
28 #include "coreconnection.h"
29
30 #include "client.h"
31
32 CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QList<QVariant> &backends, const QList<QVariant> &authenticators, QWidget *parent)
33     : QWizard(parent),
34     _connection(connection)
35 {
36     setModal(true);
37     setAttribute(Qt::WA_DeleteOnClose);
38
39     foreach(const QVariant &v, backends)
40     _backends[v.toMap()["DisplayName"].toString()] = v;
41
42     foreach(const QVariant &v, authenticators)
43     _authenticators[v.toMap()["DisplayName"].toString()] = v;
44
45     setPage(IntroPage, new CoreConfigWizardPages::IntroPage(this));
46     setPage(AdminUserPage, new CoreConfigWizardPages::AdminUserPage(this));
47     setPage(AuthenticationSelectionPage, new CoreConfigWizardPages::AuthenticationSelectionPage(_authenticators, this));
48     setPage(StorageSelectionPage, new CoreConfigWizardPages::StorageSelectionPage(_backends, this));
49     syncPage = new CoreConfigWizardPages::SyncPage(this);
50     connect(syncPage, SIGNAL(setupCore(const QString &, const QVariantMap &, const QString &, const QVariantMap &)),
51             SLOT(prepareCoreSetup(const QString &, const QVariantMap &, const QString &, const QVariantMap &)));
52     setPage(SyncPage, syncPage);
53     syncRelayPage = new CoreConfigWizardPages::SyncRelayPage(this);
54     connect(syncRelayPage, SIGNAL(startOver()), this, SLOT(startOver()));
55     setPage(SyncRelayPage, syncRelayPage);
56     //setPage(Page_StorageDetails, new StorageDetailsPage());
57     //setPage(Page_Conclusion, new ConclusionPage(storageProviders));
58
59     setStartId(IntroPage);
60     //setStartId(StorageSelectionPage);
61
62 #ifndef Q_OS_MAC
63     setWizardStyle(ModernStyle);
64 #endif
65
66     setOption(HaveHelpButton, false);
67     setOption(NoBackButtonOnStartPage, true);
68     setOption(HaveNextButtonOnLastPage, false);
69     setOption(HaveFinishButtonOnEarlyPages, false);
70     setOption(NoCancelButton, true);
71     setOption(IndependentPages, true);
72     //setOption(ExtendedWatermarkPixmap, true);
73
74     setModal(true);
75
76     setWindowTitle(tr("Core Configuration Wizard"));
77     setPixmap(QWizard::LogoPixmap, QIcon::fromTheme("quassel", QIcon(":/icons/quassel.png")).pixmap(48));
78
79     connect(connection, SIGNAL(coreSetupSuccess()), SLOT(coreSetupSuccess()));
80     connect(connection, SIGNAL(coreSetupFailed(QString)), SLOT(coreSetupFailed(QString)));
81     //connect(connection, SIGNAL(loginSuccess()), SLOT(loginSuccess()));
82     connect(connection, SIGNAL(synchronized()), SLOT(syncFinished()));
83     connect(this, SIGNAL(rejected()), connection, SLOT(disconnectFromCore()));
84 }
85
86
87 QHash<QString, QVariant> CoreConfigWizard::backends() const
88 {
89     return _backends;
90 }
91
92 QHash<QString, QVariant> CoreConfigWizard::authenticators() const
93 {
94     return _authenticators;
95 }
96
97 void CoreConfigWizard::prepareCoreSetup(const QString &backend, const QVariantMap &properties, const QString &authBackend, const QVariantMap &authProperties)
98 {
99     // Prevent the user from changing any settings he already specified...
100     foreach(int idx, visitedPages())
101     page(idx)->setEnabled(false);
102
103     // FIXME? We need to be able to set up older cores that don't have auth backend support.
104     // So if the core doesn't support that feature, don't pass those parameters.
105     if (!(Client::coreFeatures() & Quassel::AuthBackends))
106     {
107         coreConnection()->setupCore(Protocol::SetupData(field("adminUser.user").toString(), field("adminUser.password").toString(), backend, properties));
108     } else {
109         coreConnection()->setupCore(Protocol::SetupData(field("adminUser.user").toString(), field("adminUser.password").toString(), backend, properties, authBackend, authProperties));
110     }
111 }
112
113
114 void CoreConfigWizard::coreSetupSuccess()
115 {
116     syncPage->setStatus(tr("Your core has been successfully configured. Logging you in..."));
117     syncPage->setError(false);
118     syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error);
119     coreConnection()->loginToCore(field("adminUser.user").toString(), field("adminUser.password").toString(), field("adminUser.rememberPasswd").toBool());
120 }
121
122
123 void CoreConfigWizard::coreSetupFailed(const QString &error)
124 {
125     syncPage->setStatus(tr("Core configuration failed:<br><b>%1</b><br>Press <em>Next</em> to start over.").arg(error));
126     syncPage->setError(true);
127     syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error);
128     //foreach(int idx, visitedPages()) page(idx)->setEnabled(true);
129     //setStartId(SyncPage);
130     //restart();
131 }
132
133
134 void CoreConfigWizard::startOver()
135 {
136     foreach(int idx, visitedPages()) page(idx)->setEnabled(true);
137     setStartId(CoreConfigWizard::AdminUserPage);
138     restart();
139 }
140
141
142 void CoreConfigWizard::loginSuccess()
143 {
144     syncPage->setStatus(tr("Your are now logged into your freshly configured Quassel Core!<br>"
145                            "Please remember to configure your identities and networks now."));
146     syncPage->setComplete(true);
147     syncPage->setFinalPage(true);
148 }
149
150
151 void CoreConfigWizard::syncFinished()
152 {
153     accept();
154 }
155
156
157 namespace CoreConfigWizardPages {
158 /*** Intro Page ***/
159
160 IntroPage::IntroPage(QWidget *parent) : QWizardPage(parent)
161 {
162     ui.setupUi(this);
163     setTitle(tr("Introduction"));
164     //setSubTitle(tr("foobar"));
165     //setPixmap(QWizard::WatermarkPixmap, QPixmap(":icons/quassel-icon.png"));
166 }
167
168
169 int IntroPage::nextId() const
170 {
171     return CoreConfigWizard::AdminUserPage;
172 }
173
174
175 /*** Admin User Page ***/
176
177 AdminUserPage::AdminUserPage(QWidget *parent) : QWizardPage(parent)
178 {
179     ui.setupUi(this);
180     setTitle(tr("Create Admin User"));
181     setSubTitle(tr("First, we will create a user on the core. This first user will have administrator privileges."));
182
183     registerField("adminUser.user*", ui.user);
184     registerField("adminUser.password*", ui.password);
185     registerField("adminUser.password2*", ui.password2);
186     registerField("adminUser.rememberPasswd", ui.rememberPasswd);
187
188     //ui.user->setText("foo");
189     //ui.password->setText("foo");
190     //ui.password2->setText("foo");
191 }
192
193
194 int AdminUserPage::nextId() const
195 {
196     // If the core doesn't support auth backends, skip that page!
197     if (!(Client::coreFeatures() & Quassel::AuthBackends))
198     {
199         return CoreConfigWizard::StorageSelectionPage;
200     } else {
201         return CoreConfigWizard::AuthenticationSelectionPage;
202     }
203 }
204
205
206 bool AdminUserPage::isComplete() const
207 {
208     bool ok = !ui.user->text().isEmpty() && !ui.password->text().isEmpty() && ui.password->text() == ui.password2->text();
209     return ok;
210 }
211
212 /*** Authentication Selection Page ***/
213
214 AuthenticationSelectionPage::AuthenticationSelectionPage(const QHash<QString, QVariant> &backends, QWidget *parent)
215     : QWizardPage(parent),
216     _connectionBox(0),
217     _backends(backends)
218 {
219     ui.setupUi(this);
220
221     setTitle(tr("Select Authentication Backend"));
222     setSubTitle(tr("Please select a backend for Quassel Core to use for authenticating users."));
223     setCommitPage(true);
224
225     registerField("authentication.backend", ui.backendList);
226
227     foreach(QString key, _backends.keys()) {
228         ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key);
229     }
230
231     on_backendList_currentIndexChanged();
232 }
233
234
235 int AuthenticationSelectionPage::nextId() const
236 {
237     return CoreConfigWizard::StorageSelectionPage;
238 }
239
240
241 QString AuthenticationSelectionPage::selectedBackend() const
242 {
243     return ui.backendList->currentText();
244 }
245
246
247 QVariantMap AuthenticationSelectionPage::connectionProperties() const
248 {
249     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
250
251     QVariantMap properties;
252     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
253     if (!setupKeys.isEmpty()) {
254         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
255         foreach(QString key, setupKeys) {
256             QWidget *widget = _connectionBox->findChild<QWidget *>(key);
257             QVariant def;
258             if (defaults.contains(key)) {
259                 def = defaults[key];
260             }
261             switch (def.type()) {
262             case QVariant::Int:
263             {
264                 QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
265                 Q_ASSERT(spinbox);
266                 def = QVariant(spinbox->value());
267             }
268             break;
269             default:
270             {
271                 QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
272                 Q_ASSERT(lineEdit);
273                 def = QVariant(lineEdit->text());
274             }
275             }
276             properties[key] = def;
277         }
278     }
279     qDebug() << properties;
280
281 //   QVariantMap properties = _backends[backend].toMap()["ConnectionProperties"].toMap();
282 //   if(!properties.isEmpty() && _connectionBox) {
283 //     QVariantMap::iterator propertyIter = properties.begin();
284 //     while(propertyIter != properties.constEnd()) {
285 //       QWidget *widget = _connectionBox->findChild<QWidget *>(propertyIter.key());
286 //       switch(propertyIter.value().type()) {
287 //       case QVariant::Int:
288 //      {
289 //        QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
290 //        Q_ASSERT(spinbox);
291 //        propertyIter.value() = QVariant(spinbox->value());
292 //      }
293 //      break;
294 //       default:
295 //      {
296 //        QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
297 //        Q_ASSERT(lineEdit);
298 //        propertyIter.value() = QVariant(lineEdit->text());
299 //      }
300 //       }
301 //       propertyIter++;
302 //     }
303 //   }
304     return properties;
305 }
306
307
308 void AuthenticationSelectionPage::on_backendList_currentIndexChanged()
309 {
310     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
311     ui.description->setText(_backends[backend].toMap()["Description"].toString());
312
313     if (_connectionBox) {
314         layout()->removeWidget(_connectionBox);
315         _connectionBox->deleteLater();
316         _connectionBox = 0;
317     }
318
319     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
320     if (!setupKeys.isEmpty()) {
321         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
322         QGroupBox *propertyBox = new QGroupBox(this);
323         propertyBox->setTitle(tr("Connection Properties"));
324         QFormLayout *formlayout = new QFormLayout;
325
326         foreach(QString key, setupKeys) {
327             QWidget *widget = 0;
328             QVariant def;
329             if (defaults.contains(key)) {
330                 def = defaults[key];
331             }
332             switch (def.type()) {
333             case QVariant::Int:
334             {
335                 QSpinBox *spinbox = new QSpinBox(propertyBox);
336                 spinbox->setMaximum(64000);
337                 spinbox->setValue(def.toInt());
338                 widget = spinbox;
339             }
340             break;
341             default:
342             {
343                 QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox);
344                 if (key.toLower().contains("password")) {
345                     lineEdit->setEchoMode(QLineEdit::Password);
346                 }
347                 widget = lineEdit;
348             }
349             }
350             widget->setObjectName(key);
351             formlayout->addRow(key + ":", widget);
352         }
353         propertyBox->setLayout(formlayout);
354         static_cast<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox);
355         _connectionBox = propertyBox;
356     }
357 }
358
359 /*** Storage Selection Page ***/
360
361 StorageSelectionPage::StorageSelectionPage(const QHash<QString, QVariant> &backends, QWidget *parent)
362     : QWizardPage(parent),
363     _connectionBox(0),
364     _backends(backends)
365 {
366     ui.setupUi(this);
367
368     setTitle(tr("Select Storage Backend"));
369     setSubTitle(tr("Please select a database backend for the Quassel Core storage to store the backlog and other data in."));
370     setCommitPage(true);
371
372     registerField("storage.backend", ui.backendList);
373
374     int defaultIndex = 0;
375     foreach(QString key, _backends.keys()) {
376         ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key);
377         if (_backends[key].toMap()["IsDefault"].toBool()) {
378             defaultIndex = ui.backendList->count() - 1;
379         }
380     }
381
382     ui.backendList->setCurrentIndex(defaultIndex);
383
384     on_backendList_currentIndexChanged();
385 }
386
387
388 int StorageSelectionPage::nextId() const
389 {
390     return CoreConfigWizard::SyncPage;
391 }
392
393
394 QString StorageSelectionPage::selectedBackend() const
395 {
396     return ui.backendList->currentText();
397 }
398
399
400 QVariantMap StorageSelectionPage::connectionProperties() const
401 {
402     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
403
404     QVariantMap properties;
405     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
406     if (!setupKeys.isEmpty()) {
407         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
408         foreach(QString key, setupKeys) {
409             QWidget *widget = _connectionBox->findChild<QWidget *>(key);
410             QVariant def;
411             if (defaults.contains(key)) {
412                 def = defaults[key];
413             }
414             switch (def.type()) {
415             case QVariant::Int:
416             {
417                 QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
418                 Q_ASSERT(spinbox);
419                 def = QVariant(spinbox->value());
420             }
421             break;
422             default:
423             {
424                 QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
425                 Q_ASSERT(lineEdit);
426                 def = QVariant(lineEdit->text());
427             }
428             }
429             properties[key] = def;
430         }
431     }
432     qDebug() << properties;
433
434 //   QVariantMap properties = _backends[backend].toMap()["ConnectionProperties"].toMap();
435 //   if(!properties.isEmpty() && _connectionBox) {
436 //     QVariantMap::iterator propertyIter = properties.begin();
437 //     while(propertyIter != properties.constEnd()) {
438 //       QWidget *widget = _connectionBox->findChild<QWidget *>(propertyIter.key());
439 //       switch(propertyIter.value().type()) {
440 //       case QVariant::Int:
441 //      {
442 //        QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
443 //        Q_ASSERT(spinbox);
444 //        propertyIter.value() = QVariant(spinbox->value());
445 //      }
446 //      break;
447 //       default:
448 //      {
449 //        QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
450 //        Q_ASSERT(lineEdit);
451 //        propertyIter.value() = QVariant(lineEdit->text());
452 //      }
453 //       }
454 //       propertyIter++;
455 //     }
456 //   }
457     return properties;
458 }
459
460
461 void StorageSelectionPage::on_backendList_currentIndexChanged()
462 {
463     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
464     ui.description->setText(_backends[backend].toMap()["Description"].toString());
465
466     if (_connectionBox) {
467         layout()->removeWidget(_connectionBox);
468         _connectionBox->deleteLater();
469         _connectionBox = 0;
470     }
471
472     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
473     if (!setupKeys.isEmpty()) {
474         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
475         QGroupBox *propertyBox = new QGroupBox(this);
476         propertyBox->setTitle(tr("Connection Properties"));
477         QFormLayout *formlayout = new QFormLayout;
478
479         foreach(QString key, setupKeys) {
480             QWidget *widget = 0;
481             QVariant def;
482             if (defaults.contains(key)) {
483                 def = defaults[key];
484             }
485             switch (def.type()) {
486             case QVariant::Int:
487             {
488                 QSpinBox *spinbox = new QSpinBox(propertyBox);
489                 spinbox->setMaximum(64000);
490                 spinbox->setValue(def.toInt());
491                 widget = spinbox;
492             }
493             break;
494             default:
495             {
496                 QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox);
497                 if (key.toLower().contains("password")) {
498                     lineEdit->setEchoMode(QLineEdit::Password);
499                 }
500                 widget = lineEdit;
501             }
502             }
503             widget->setObjectName(key);
504             formlayout->addRow(key + ":", widget);
505         }
506         propertyBox->setLayout(formlayout);
507         static_cast<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox);
508         _connectionBox = propertyBox;
509     }
510 }
511
512
513 /*** Sync Page ***/
514
515 SyncPage::SyncPage(QWidget *parent) : QWizardPage(parent)
516 {
517     ui.setupUi(this);
518     setTitle(tr("Storing Your Settings"));
519     setSubTitle(tr("Your settings are now stored in the core, and you will be logged in automatically."));
520 }
521
522
523 void SyncPage::initializePage()
524 {
525     complete = false;
526     hasError = false;
527
528     // Fill in sync info about the storage layer.
529     StorageSelectionPage *storagePage = qobject_cast<StorageSelectionPage *>(wizard()->page(CoreConfigWizard::StorageSelectionPage));
530     QString backend = storagePage->selectedBackend();
531     QVariantMap properties = storagePage->connectionProperties();
532     Q_ASSERT(!backend.isEmpty());
533     ui.backend->setText(backend);
534
535     // Fill in sync info about the authentication layer.
536     AuthenticationSelectionPage *authPage = qobject_cast<AuthenticationSelectionPage *>(wizard()->page(CoreConfigWizard::AuthenticationSelectionPage));
537     QString authBackend = authPage->selectedBackend();
538     QVariantMap authProperties = authPage->connectionProperties();
539     Q_ASSERT(!authBackend.isEmpty());
540     ui.authBackend->setText(authBackend);
541
542     ui.user->setText(wizard()->field("adminUser.user").toString());
543
544     emit setupCore(backend, properties, authBackend, authProperties);
545 }
546
547
548 int SyncPage::nextId() const
549 {
550     if (!hasError) return -1;
551     return CoreConfigWizard::SyncRelayPage;
552 }
553
554
555 bool SyncPage::isComplete() const
556 {
557     return complete;
558 }
559
560
561 void SyncPage::setStatus(const QString &status)
562 {
563     ui.status->setText(status);
564 }
565
566
567 void SyncPage::setError(bool e)
568 {
569     hasError = e;
570 }
571
572
573 void SyncPage::setComplete(bool c)
574 {
575     complete = c;
576     completeChanged();
577 }
578
579
580 /*** Sync Relay Page ***/
581
582 SyncRelayPage::SyncRelayPage(QWidget *parent) : QWizardPage(parent)
583 {
584     mode = Success;
585 }
586
587
588 void SyncRelayPage::setMode(Mode m)
589 {
590     mode = m;
591 }
592
593
594 /*
595 void SyncRelayPage::initializePage() {
596   return;
597   if(mode == Success) {
598     wizard()->accept();
599   } else {
600     emit startOver();
601   }
602 }
603 */
604
605 int SyncRelayPage::nextId() const
606 {
607     emit startOver();
608     return 0;
609 }
610 };  /* namespace CoreConfigWizardPages */