99fc3540cd5d5203e0555f062cda565c39295961
[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
93 QHash<QString, QVariant> CoreConfigWizard::authenticators() const
94 {
95     return _authenticators;
96 }
97
98
99 void CoreConfigWizard::prepareCoreSetup(const QString &backend, const QVariantMap &properties, const QString &authenticator, const QVariantMap &authProperties)
100 {
101     // Prevent the user from changing any settings he already specified...
102     foreach(int idx, visitedPages())
103     page(idx)->setEnabled(false);
104
105     // FIXME? We need to be able to set up older cores that don't have auth backend support.
106     // So if the core doesn't support that feature, don't pass those parameters.
107     if (!(Client::coreFeatures() & Quassel::Authenticators)) {
108         coreConnection()->setupCore(Protocol::SetupData(field("adminUser.user").toString(), field("adminUser.password").toString(), backend, properties));
109     }
110     else {
111         coreConnection()->setupCore(Protocol::SetupData(field("adminUser.user").toString(), field("adminUser.password").toString(), backend, properties, authenticator, authProperties));
112     }
113 }
114
115
116 void CoreConfigWizard::coreSetupSuccess()
117 {
118     syncPage->setStatus(tr("Your core has been successfully configured. Logging you in..."));
119     syncPage->setError(false);
120     syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error);
121     coreConnection()->loginToCore(field("adminUser.user").toString(), field("adminUser.password").toString(), field("adminUser.rememberPasswd").toBool());
122 }
123
124
125 void CoreConfigWizard::coreSetupFailed(const QString &error)
126 {
127     syncPage->setStatus(tr("Core configuration failed:<br><b>%1</b><br>Press <em>Next</em> to start over.").arg(error));
128     syncPage->setError(true);
129     syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error);
130     //foreach(int idx, visitedPages()) page(idx)->setEnabled(true);
131     //setStartId(SyncPage);
132     //restart();
133 }
134
135
136 void CoreConfigWizard::startOver()
137 {
138     foreach(int idx, visitedPages()) page(idx)->setEnabled(true);
139     setStartId(CoreConfigWizard::AdminUserPage);
140     restart();
141 }
142
143
144 void CoreConfigWizard::loginSuccess()
145 {
146     syncPage->setStatus(tr("Your are now logged into your freshly configured Quassel Core!<br>"
147                            "Please remember to configure your identities and networks now."));
148     syncPage->setComplete(true);
149     syncPage->setFinalPage(true);
150 }
151
152
153 void CoreConfigWizard::syncFinished()
154 {
155     accept();
156 }
157
158
159 namespace CoreConfigWizardPages {
160 /*** Intro Page ***/
161
162 IntroPage::IntroPage(QWidget *parent) : QWizardPage(parent)
163 {
164     ui.setupUi(this);
165     setTitle(tr("Introduction"));
166     //setSubTitle(tr("foobar"));
167     //setPixmap(QWizard::WatermarkPixmap, QPixmap(":icons/quassel-icon.png"));
168 }
169
170
171 int IntroPage::nextId() const
172 {
173     return CoreConfigWizard::AdminUserPage;
174 }
175
176
177 /*** Admin User Page ***/
178
179 AdminUserPage::AdminUserPage(QWidget *parent) : QWizardPage(parent)
180 {
181     ui.setupUi(this);
182     setTitle(tr("Create Admin User"));
183     setSubTitle(tr("First, we will create a user on the core. This first user will have administrator privileges."));
184
185     registerField("adminUser.user*", ui.user);
186     registerField("adminUser.password*", ui.password);
187     registerField("adminUser.password2*", ui.password2);
188     registerField("adminUser.rememberPasswd", ui.rememberPasswd);
189
190     //ui.user->setText("foo");
191     //ui.password->setText("foo");
192     //ui.password2->setText("foo");
193 }
194
195
196 int AdminUserPage::nextId() const
197 {
198     // If the core doesn't support auth backends, skip that page!
199     if (!(Client::coreFeatures() & Quassel::Authenticators)) {
200         return CoreConfigWizard::StorageSelectionPage;
201     }
202     else {
203         return CoreConfigWizard::AuthenticationSelectionPage;
204     }
205 }
206
207
208 bool AdminUserPage::isComplete() const
209 {
210     bool ok = !ui.user->text().isEmpty() && !ui.password->text().isEmpty() && ui.password->text() == ui.password2->text();
211     return ok;
212 }
213
214 /*** Authentication Selection Page ***/
215
216 AuthenticationSelectionPage::AuthenticationSelectionPage(const QHash<QString, QVariant> &backends, QWidget *parent)
217     : QWizardPage(parent),
218     _connectionBox(0),
219     _backends(backends)
220 {
221     ui.setupUi(this);
222
223     setTitle(tr("Select Authentication Backend"));
224     setSubTitle(tr("Please select a backend for Quassel Core to use for authenticating users."));
225     setCommitPage(true);
226
227     registerField("authentication.backend", ui.backendList);
228
229     foreach(QString key, _backends.keys()) {
230         ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key);
231     }
232
233     on_backendList_currentIndexChanged();
234 }
235
236
237 int AuthenticationSelectionPage::nextId() const
238 {
239     return CoreConfigWizard::StorageSelectionPage;
240 }
241
242
243 QString AuthenticationSelectionPage::selectedBackend() const
244 {
245     return ui.backendList->currentText();
246 }
247
248
249 QVariantMap AuthenticationSelectionPage::authProperties() const
250 {
251     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
252
253     QVariantMap properties;
254     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
255     if (!setupKeys.isEmpty()) {
256         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
257         foreach(QString key, setupKeys) {
258             QWidget *widget = _connectionBox->findChild<QWidget *>(key);
259             QVariant def;
260             if (defaults.contains(key)) {
261                 def = defaults[key];
262             }
263             switch (def.type()) {
264             case QVariant::Int:
265             {
266                 QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
267                 Q_ASSERT(spinbox);
268                 def = QVariant(spinbox->value());
269             }
270             break;
271             default:
272             {
273                 QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
274                 Q_ASSERT(lineEdit);
275                 def = QVariant(lineEdit->text());
276             }
277             }
278             properties[key] = def;
279         }
280     }
281     return properties;
282 }
283
284
285 void AuthenticationSelectionPage::on_backendList_currentIndexChanged()
286 {
287     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
288     ui.description->setText(_backends[backend].toMap()["Description"].toString());
289
290     if (_connectionBox) {
291         layout()->removeWidget(_connectionBox);
292         _connectionBox->deleteLater();
293         _connectionBox = 0;
294     }
295
296     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
297     if (!setupKeys.isEmpty()) {
298         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
299         QGroupBox *propertyBox = new QGroupBox(this);
300         propertyBox->setTitle(tr("Connection Properties"));
301         QFormLayout *formlayout = new QFormLayout;
302
303         foreach(QString key, setupKeys) {
304             QWidget *widget = 0;
305             QVariant def;
306             if (defaults.contains(key)) {
307                 def = defaults[key];
308             }
309             switch (def.type()) {
310             case QVariant::Int:
311             {
312                 QSpinBox *spinbox = new QSpinBox(propertyBox);
313                 spinbox->setMaximum(64000);
314                 spinbox->setValue(def.toInt());
315                 widget = spinbox;
316             }
317             break;
318             default:
319             {
320                 QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox);
321                 if (key.toLower().contains("password")) {
322                     lineEdit->setEchoMode(QLineEdit::Password);
323                 }
324                 widget = lineEdit;
325             }
326             }
327             widget->setObjectName(key);
328             formlayout->addRow(key + ":", widget);
329         }
330         propertyBox->setLayout(formlayout);
331         static_cast<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox);
332         _connectionBox = propertyBox;
333     }
334 }
335
336 /*** Storage Selection Page ***/
337
338 StorageSelectionPage::StorageSelectionPage(const QHash<QString, QVariant> &backends, QWidget *parent)
339     : QWizardPage(parent),
340     _connectionBox(0),
341     _backends(backends)
342 {
343     ui.setupUi(this);
344
345     setTitle(tr("Select Storage Backend"));
346     setSubTitle(tr("Please select a database backend for the Quassel Core storage to store the backlog and other data in."));
347     setCommitPage(true);
348
349     registerField("storage.backend", ui.backendList);
350
351     int defaultIndex = 0;
352     foreach(QString key, _backends.keys()) {
353         ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key);
354         if (_backends[key].toMap()["IsDefault"].toBool()) {
355             defaultIndex = ui.backendList->count() - 1;
356         }
357     }
358
359     ui.backendList->setCurrentIndex(defaultIndex);
360
361     on_backendList_currentIndexChanged();
362 }
363
364
365 int StorageSelectionPage::nextId() const
366 {
367     return CoreConfigWizard::SyncPage;
368 }
369
370
371 QString StorageSelectionPage::selectedBackend() const
372 {
373     return ui.backendList->currentText();
374 }
375
376
377 QVariantMap StorageSelectionPage::connectionProperties() const
378 {
379     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
380
381     QVariantMap properties;
382     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
383     if (!setupKeys.isEmpty()) {
384         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
385         foreach(QString key, setupKeys) {
386             QWidget *widget = _connectionBox->findChild<QWidget *>(key);
387             QVariant def;
388             if (defaults.contains(key)) {
389                 def = defaults[key];
390             }
391             switch (def.type()) {
392             case QVariant::Int:
393             {
394                 QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
395                 Q_ASSERT(spinbox);
396                 def = QVariant(spinbox->value());
397             }
398             break;
399             default:
400             {
401                 QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
402                 Q_ASSERT(lineEdit);
403                 def = QVariant(lineEdit->text());
404             }
405             }
406             properties[key] = def;
407         }
408     }
409     qDebug() << properties;
410
411 //   QVariantMap properties = _backends[backend].toMap()["ConnectionProperties"].toMap();
412 //   if(!properties.isEmpty() && _connectionBox) {
413 //     QVariantMap::iterator propertyIter = properties.begin();
414 //     while(propertyIter != properties.constEnd()) {
415 //       QWidget *widget = _connectionBox->findChild<QWidget *>(propertyIter.key());
416 //       switch(propertyIter.value().type()) {
417 //       case QVariant::Int:
418 //      {
419 //        QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
420 //        Q_ASSERT(spinbox);
421 //        propertyIter.value() = QVariant(spinbox->value());
422 //      }
423 //      break;
424 //       default:
425 //      {
426 //        QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
427 //        Q_ASSERT(lineEdit);
428 //        propertyIter.value() = QVariant(lineEdit->text());
429 //      }
430 //       }
431 //       propertyIter++;
432 //     }
433 //   }
434     return properties;
435 }
436
437
438 void StorageSelectionPage::on_backendList_currentIndexChanged()
439 {
440     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
441     ui.description->setText(_backends[backend].toMap()["Description"].toString());
442
443     if (_connectionBox) {
444         layout()->removeWidget(_connectionBox);
445         _connectionBox->deleteLater();
446         _connectionBox = 0;
447     }
448
449     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
450     if (!setupKeys.isEmpty()) {
451         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
452         QGroupBox *propertyBox = new QGroupBox(this);
453         propertyBox->setTitle(tr("Connection Properties"));
454         QFormLayout *formlayout = new QFormLayout;
455
456         foreach(QString key, setupKeys) {
457             QWidget *widget = 0;
458             QVariant def;
459             if (defaults.contains(key)) {
460                 def = defaults[key];
461             }
462             switch (def.type()) {
463             case QVariant::Int:
464             {
465                 QSpinBox *spinbox = new QSpinBox(propertyBox);
466                 spinbox->setMaximum(64000);
467                 spinbox->setValue(def.toInt());
468                 widget = spinbox;
469             }
470             break;
471             default:
472             {
473                 QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox);
474                 if (key.toLower().contains("password")) {
475                     lineEdit->setEchoMode(QLineEdit::Password);
476                 }
477                 widget = lineEdit;
478             }
479             }
480             widget->setObjectName(key);
481             formlayout->addRow(key + ":", widget);
482         }
483         propertyBox->setLayout(formlayout);
484         static_cast<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox);
485         _connectionBox = propertyBox;
486     }
487 }
488
489
490 /*** Sync Page ***/
491
492 SyncPage::SyncPage(QWidget *parent) : QWizardPage(parent)
493 {
494     ui.setupUi(this);
495     setTitle(tr("Storing Your Settings"));
496     setSubTitle(tr("Your settings are now stored in the core, and you will be logged in automatically."));
497 }
498
499
500 void SyncPage::initializePage()
501 {
502     complete = false;
503     hasError = false;
504
505     // Fill in sync info about the storage layer.
506     StorageSelectionPage *storagePage = qobject_cast<StorageSelectionPage *>(wizard()->page(CoreConfigWizard::StorageSelectionPage));
507     QString backend = storagePage->selectedBackend();
508     QVariantMap properties = storagePage->connectionProperties();
509     Q_ASSERT(!backend.isEmpty());
510     ui.backend->setText(backend);
511
512     // Fill in sync info about the authentication layer.
513     AuthenticationSelectionPage *authPage = qobject_cast<AuthenticationSelectionPage *>(wizard()->page(CoreConfigWizard::AuthenticationSelectionPage));
514     QString authenticator = authPage->selectedBackend();
515     QVariantMap authProperties = authPage->authProperties();
516     Q_ASSERT(!authenticator.isEmpty());
517     ui.authenticator->setText(authenticator);
518
519     ui.user->setText(wizard()->field("adminUser.user").toString());
520
521     emit setupCore(backend, properties, authenticator, authProperties);
522 }
523
524
525 int SyncPage::nextId() const
526 {
527     if (!hasError) return -1;
528     return CoreConfigWizard::SyncRelayPage;
529 }
530
531
532 bool SyncPage::isComplete() const
533 {
534     return complete;
535 }
536
537
538 void SyncPage::setStatus(const QString &status)
539 {
540     ui.status->setText(status);
541 }
542
543
544 void SyncPage::setError(bool e)
545 {
546     hasError = e;
547 }
548
549
550 void SyncPage::setComplete(bool c)
551 {
552     complete = c;
553     completeChanged();
554 }
555
556
557 /*** Sync Relay Page ***/
558
559 SyncRelayPage::SyncRelayPage(QWidget *parent) : QWizardPage(parent)
560 {
561     mode = Success;
562 }
563
564
565 void SyncRelayPage::setMode(Mode m)
566 {
567     mode = m;
568 }
569
570
571 /*
572 void SyncRelayPage::initializePage() {
573   return;
574   if(mode == Success) {
575     wizard()->accept();
576   } else {
577     emit startOver();
578   }
579 }
580 */
581
582 int SyncRelayPage::nextId() const
583 {
584     emit startOver();
585     return 0;
586 }
587 };  /* namespace CoreConfigWizardPages */