aboutsummaryrefslogtreecommitdiffstats
path: root/Plugins/ConfigMigration
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2014-12-06 17:33:25 -0500
committerLibravatarUnit 193 <unit193@ubuntu.com>2014-12-06 17:33:25 -0500
commit7167ce41b61d2ba2cdb526777a4233eb84a3b66a (patch)
treea35c14143716e1f2c98f808c81f89426045a946f /Plugins/ConfigMigration
Imported Upstream version 2.99.6upstream/2.99.6
Diffstat (limited to 'Plugins/ConfigMigration')
-rw-r--r--Plugins/ConfigMigration/ConfigMigration.pro31
-rw-r--r--Plugins/ConfigMigration/config_migration.pngbin0 -> 24844 bytes
-rw-r--r--Plugins/ConfigMigration/configmigartion.json0
-rw-r--r--Plugins/ConfigMigration/configmigration.cpp188
-rw-r--r--Plugins/ConfigMigration/configmigration.h49
-rw-r--r--Plugins/ConfigMigration/configmigration.json8
-rw-r--r--Plugins/ConfigMigration/configmigration.qrc5
-rw-r--r--Plugins/ConfigMigration/configmigration_global.h12
-rw-r--r--Plugins/ConfigMigration/configmigrationitem.h20
-rw-r--r--Plugins/ConfigMigration/configmigrationwizard.cpp407
-rw-r--r--Plugins/ConfigMigration/configmigrationwizard.h52
-rw-r--r--Plugins/ConfigMigration/configmigrationwizard.ui109
12 files changed, 881 insertions, 0 deletions
diff --git a/Plugins/ConfigMigration/ConfigMigration.pro b/Plugins/ConfigMigration/ConfigMigration.pro
new file mode 100644
index 0000000..965cfeb
--- /dev/null
+++ b/Plugins/ConfigMigration/ConfigMigration.pro
@@ -0,0 +1,31 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2014-09-15T15:02:05
+#
+#-------------------------------------------------
+
+include($$PWD/../../SQLiteStudio3/plugins.pri)
+
+QT += widgets
+
+TARGET = ConfigMigration
+TEMPLATE = lib
+
+DEFINES += CONFIGMIGRATION_LIBRARY
+
+SOURCES += configmigration.cpp \
+ configmigrationwizard.cpp
+
+HEADERS += configmigration.h\
+ configmigration_global.h \
+ configmigrationwizard.h \
+ configmigrationitem.h
+
+OTHER_FILES += \
+ configmigration.json
+
+FORMS += \
+ configmigrationwizard.ui
+
+RESOURCES += \
+ configmigration.qrc
diff --git a/Plugins/ConfigMigration/config_migration.png b/Plugins/ConfigMigration/config_migration.png
new file mode 100644
index 0000000..eb75995
--- /dev/null
+++ b/Plugins/ConfigMigration/config_migration.png
Binary files differ
diff --git a/Plugins/ConfigMigration/configmigartion.json b/Plugins/ConfigMigration/configmigartion.json
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigartion.json
diff --git a/Plugins/ConfigMigration/configmigration.cpp b/Plugins/ConfigMigration/configmigration.cpp
new file mode 100644
index 0000000..cd49d24
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigration.cpp
@@ -0,0 +1,188 @@
+#include "configmigration.h"
+#include "services/notifymanager.h"
+#include "sqlitestudio.h"
+#include "mainwindow.h"
+#include "statusfield.h"
+#include "configmigrationwizard.h"
+#include "db/dbsqlite3.h"
+#include <QCoreApplication>
+#include <QDir>
+#include <QFileInfo>
+#include <QDebug>
+
+ConfigMigration::ConfigMigration()
+{
+}
+
+bool ConfigMigration::init()
+{
+ Q_INIT_RESOURCE(configmigration);
+
+ if (cfg.CfgMigration.Migrated.get())
+ {
+ qDebug() << "ConfigMigration: already migrated. Skipping.";
+ return true;
+ }
+
+ QString oldCfg = findOldConfig();
+ if (!oldCfg.isNull())
+ {
+ db = new DbSqlite3("Old SQLiteStudio settings", oldCfg, {{DB_PURE_INIT, true}});
+ if (db->open())
+ {
+ itemsToMigrate = findItemsToMigrate();
+ notifyInfo(tr("A configuration from old SQLiteStudio 2.x.x has been detected. "
+ "Would you like to migrate old settings into the current version? "
+ "<a href=\"%1\">Click here to do that</a>.").arg(ACTION_LINK));
+
+ connect(MAINWINDOW->getStatusField(), SIGNAL(linkActivated(QString)), this, SLOT(linkActivated(QString)));
+ db->close();
+ }
+ }
+
+ return true;
+}
+
+void ConfigMigration::deinit()
+{
+ Q_CLEANUP_RESOURCE(configmigration);
+ safe_delete(db);
+
+ for (ConfigMigrationItem* item : itemsToMigrate)
+ delete item;
+
+ itemsToMigrate.clear();
+ GenericPlugin::deinit();
+}
+
+QString ConfigMigration::findOldConfig()
+{
+ QString output;
+ QString dirPath;
+
+ // Portable path 1 check
+ dirPath = QDir::currentPath() + "/sqlitestudio-cfg";
+ if (checkOldDir(dirPath, output))
+ return output;
+
+ // Portable path 2 check
+ dirPath = QCoreApplication::applicationDirPath() + "/sqlitestudio-cfg";
+ if (checkOldDir(dirPath, output))
+ return output;
+
+ // Portable path 3 check
+ dirPath = QCoreApplication::applicationDirPath() + "/../sqlitestudio-cfg";
+ if (checkOldDir(dirPath, output))
+ return output;
+
+ if (getDistributionType() == DistributionType::OSX_BOUNDLE)
+ {
+ // Portable path 4 check
+ dirPath = QCoreApplication::applicationDirPath() + "/../../sqlitestudio-cfg";
+ if (checkOldDir(dirPath, output))
+ return output;
+
+ // Portable path 5 check
+ dirPath = QCoreApplication::applicationDirPath() + "/../../../sqlitestudio-cfg";
+ if (checkOldDir(dirPath, output))
+ return output;
+ }
+
+ // Global path check
+#ifdef Q_OS_WIN
+ if (QSysInfo::windowsVersion() & QSysInfo::WV_NT_based)
+ dirPath = SQLITESTUDIO->getEnv("APPDATA")+"/sqlitestudio";
+ else
+ dirPath = SQLITESTUDIO->getEnv("HOME")+"/sqlitestudio";
+#else
+ dirPath = SQLITESTUDIO->getEnv("HOME")+"/.sqlitestudio";
+#endif
+
+ if (checkOldDir(dirPath, output))
+ return output;
+
+ return QString();
+}
+
+bool ConfigMigration::checkOldDir(const QString &dir, QString &output)
+{
+ QFileInfo fi(dir + "/settings");
+ if (fi.exists() && fi.isReadable())
+ {
+ output = fi.absoluteFilePath();
+ return true;
+ }
+
+ return false;
+}
+
+QList<ConfigMigrationItem*> ConfigMigration::findItemsToMigrate()
+{
+ static_qstring(bugsHistoryQuery, "SELECT count(*) FROM bugs");
+ static_qstring(dbListQuery, "SELECT count(*) FROM dblist");
+ static_qstring(funcListQuery, "SELECT count(*) FROM functions");
+ static_qstring(sqlHistoryQuery, "SELECT count(*) FROM history");
+
+ ConfigMigrationItem* item = nullptr;
+ QList<ConfigMigrationItem*> results;
+
+ int bugReports = db->exec(bugsHistoryQuery)->getSingleCell().toInt();
+ if (bugReports > 0)
+ {
+ item = new ConfigMigrationItem;
+ item->type = ConfigMigrationItem::Type::BUG_REPORTS;
+ item->label = tr("Bug reports history (%1)").arg(bugReports);
+ results << item;
+ }
+
+ int dbCount = db->exec(dbListQuery)->getSingleCell().toInt();
+ if (dbCount > 0)
+ {
+ item = new ConfigMigrationItem;
+ item->type = ConfigMigrationItem::Type::DATABASES;
+ item->label = tr("Database list (%1)").arg(dbCount);
+ results << item;
+ }
+
+ int funcCount = db->exec(funcListQuery)->getSingleCell().toInt();
+ if (funcCount > 0)
+ {
+ item = new ConfigMigrationItem;
+ item->type = ConfigMigrationItem::Type::FUNCTION_LIST;
+ item->label = tr("Custom SQL functions (%1)").arg(funcCount);
+ results << item;
+ }
+
+ int sqlHistory = db->exec(sqlHistoryQuery)->getSingleCell().toInt();
+ if (sqlHistory > 0)
+ {
+ item = new ConfigMigrationItem;
+ item->type = ConfigMigrationItem::Type::SQL_HISTORY;
+ item->label = tr("SQL queries history (%1)").arg(sqlHistory);
+ results << item;
+ }
+
+ return results;
+}
+
+Db* ConfigMigration::getOldCfgDb() const
+{
+ return db;
+}
+
+QList<ConfigMigrationItem*> ConfigMigration::getItemsToMigrate() const
+{
+ return itemsToMigrate;
+}
+
+void ConfigMigration::linkActivated(const QString &link)
+{
+ if (link != ACTION_LINK)
+ return;
+
+ ConfigMigrationWizard wizard(MAINWINDOW, this);
+ wizard.exec();
+
+ if (wizard.didMigrate())
+ cfg.CfgMigration.Migrated.set(true);
+}
diff --git a/Plugins/ConfigMigration/configmigration.h b/Plugins/ConfigMigration/configmigration.h
new file mode 100644
index 0000000..828de3d
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigration.h
@@ -0,0 +1,49 @@
+#ifndef CONFIGMIGRATION_H
+#define CONFIGMIGRATION_H
+
+#include "configmigration_global.h"
+#include "plugins/generalpurposeplugin.h"
+#include "plugins/genericplugin.h"
+#include "configmigrationitem.h"
+#include "config_builder.h"
+#include <QList>
+
+class Db;
+
+CFG_CATEGORIES(ConfigMigration,
+ CFG_CATEGORY(CfgMigration,
+ CFG_ENTRY(bool, Migrated, false);
+ )
+)
+
+class CONFIGMIGRATIONSHARED_EXPORT ConfigMigration : public GenericPlugin, public GeneralPurposePlugin
+{
+ Q_OBJECT
+ SQLITESTUDIO_PLUGIN("configmigration.json")
+
+ public:
+ ConfigMigration();
+
+ bool init();
+ void deinit();
+
+ QList<ConfigMigrationItem*> getItemsToMigrate() const;
+
+ Db* getOldCfgDb() const;
+
+ private:
+ QString findOldConfig();
+ bool checkOldDir(const QString& dir, QString& output);
+ QList<ConfigMigrationItem*> findItemsToMigrate();
+
+ static const constexpr char* ACTION_LINK = "migrateOldConfig";
+
+ Db* db = nullptr;
+ QList<ConfigMigrationItem*> itemsToMigrate;
+ CFG_LOCAL_PERSISTABLE(ConfigMigration, cfg)
+
+ private slots:
+ void linkActivated(const QString& link);
+};
+
+#endif // CONFIGMIGRATION_H
diff --git a/Plugins/ConfigMigration/configmigration.json b/Plugins/ConfigMigration/configmigration.json
new file mode 100644
index 0000000..14e5d4b
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigration.json
@@ -0,0 +1,8 @@
+{
+ "type": "GeneralPurposePlugin",
+ "title": "Configuration migration",
+ "description": "Performs migration from SQLiteStudio 2.1.x configuration to version 3.0.0.",
+ "version": 10001,
+ "author": "SalSoft",
+ "gui": true
+}
diff --git a/Plugins/ConfigMigration/configmigration.qrc b/Plugins/ConfigMigration/configmigration.qrc
new file mode 100644
index 0000000..9df7d5e
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigration.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/icons">
+ <file>config_migration.png</file>
+ </qresource>
+</RCC>
diff --git a/Plugins/ConfigMigration/configmigration_global.h b/Plugins/ConfigMigration/configmigration_global.h
new file mode 100644
index 0000000..7c43a97
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigration_global.h
@@ -0,0 +1,12 @@
+#ifndef CONFIGMIGRATION_GLOBAL_H
+#define CONFIGMIGRATION_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(CONFIGMIGRATION_LIBRARY)
+# define CONFIGMIGRATIONSHARED_EXPORT Q_DECL_EXPORT
+#else
+# define CONFIGMIGRATIONSHARED_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // CONFIGMIGRATION_GLOBAL_H
diff --git a/Plugins/ConfigMigration/configmigrationitem.h b/Plugins/ConfigMigration/configmigrationitem.h
new file mode 100644
index 0000000..ef08065
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigrationitem.h
@@ -0,0 +1,20 @@
+#ifndef CONFIGMIGRATIONITEM_H
+#define CONFIGMIGRATIONITEM_H
+
+#include <QString>
+
+struct ConfigMigrationItem
+{
+ enum class Type
+ {
+ SQL_HISTORY,
+ DATABASES,
+ FUNCTION_LIST,
+ BUG_REPORTS
+ };
+
+ QString label;
+ Type type;
+};
+
+#endif // CONFIGMIGRATIONITEM_H
diff --git a/Plugins/ConfigMigration/configmigrationwizard.cpp b/Plugins/ConfigMigration/configmigrationwizard.cpp
new file mode 100644
index 0000000..afdf705
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigrationwizard.cpp
@@ -0,0 +1,407 @@
+#include "configmigrationwizard.h"
+#include "ui_configmigrationwizard.h"
+#include "configmigration.h"
+#include "configmigrationitem.h"
+#include "iconmanager.h"
+#include "uiutils.h"
+#include "dbtree/dbtree.h"
+#include "dbtree/dbtreemodel.h"
+#include "services/config.h"
+#include "sqlitestudio.h"
+#include "db/dbsqlite3.h"
+#include "services/notifymanager.h"
+#include "services/dbmanager.h"
+
+ConfigMigrationWizard::ConfigMigrationWizard(QWidget *parent, ConfigMigration* cfgMigration) :
+ QWizard(parent),
+ ui(new Ui::ConfigMigrationWizard),
+ cfgMigration(cfgMigration)
+{
+ init();
+}
+
+ConfigMigrationWizard::~ConfigMigrationWizard()
+{
+ clearFunctions();
+ delete ui;
+}
+
+bool ConfigMigrationWizard::didMigrate()
+{
+ return migrated;
+}
+
+void ConfigMigrationWizard::accept()
+{
+ migrate();
+ QWizard::accept();
+}
+
+void ConfigMigrationWizard::init()
+{
+ ui->setupUi(this);
+
+#ifdef Q_OS_MACX
+ resize(width() + 150, height());
+ setPixmap(QWizard::BackgroundPixmap, addOpacity(ICONMANAGER->getIcon("config_migration")->pixmap(180, 180), 0.4));
+#endif
+
+ ui->optionsPage->setValidator([=]() -> bool
+ {
+ QString grpName = ui->groupNameEdit->text();
+
+ bool grpOk = true;
+ QString grpErrorMsg;
+ if (ui->dbGroup->isEnabled() && ui->dbGroup->isChecked())
+ {
+ if (grpName.isEmpty())
+ {
+ grpOk = false;
+ grpErrorMsg = tr("Enter a non-empty name.");
+ }
+ else
+ {
+ DbTreeItem* item = DBTREE->getModel()->findItem(DbTreeItem::Type::DIR, grpName);
+ if (item && !item->parentDbTreeItem())
+ {
+ grpOk = false;
+ grpErrorMsg = tr("Top level group named '%1' already exists. Enter a group name that does not exist yet.").arg(grpName);
+ }
+ }
+ }
+
+ setValidState(ui->groupNameEdit, grpOk, grpErrorMsg);
+
+ return grpOk;
+ });
+
+
+ QTreeWidgetItem* treeItem = nullptr;
+ for (ConfigMigrationItem* cfgItem : cfgMigration->getItemsToMigrate())
+ {
+ treeItem = new QTreeWidgetItem({cfgItem->label});
+ treeItem->setData(0, Qt::UserRole, static_cast<int>(cfgItem->type));
+ treeItem->setFlags(treeItem->flags() | Qt::ItemIsUserCheckable);
+ treeItem->setCheckState(0, Qt::Checked);
+ ui->itemsTree->addTopLevelItem(treeItem);
+ }
+
+ connect(ui->dbGroup, SIGNAL(clicked()), ui->optionsPage, SIGNAL(completeChanged()));
+ connect(ui->groupNameEdit, SIGNAL(textChanged(QString)), ui->optionsPage, SIGNAL(completeChanged()));
+ connect(this, SIGNAL(updateOptionsValidation()), ui->optionsPage, SIGNAL(completeChanged()));
+ connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(updateOptions()));
+
+ emit updateOptionsValidation();
+}
+
+void ConfigMigrationWizard::migrate()
+{
+ Db* oldCfgDb = cfgMigration->getOldCfgDb();
+ if (!oldCfgDb->open())
+ {
+ notifyError(tr("Could not open old configuration file in order to migrate settings from it."));
+ return;
+ }
+
+ QString cfgFilePath = SQLITESTUDIO->getConfig()->getConfigFilePath();
+ Db* newCfgDb = new DbSqlite3("Config migration connection", cfgFilePath, {{DB_PURE_INIT, true}});
+ if (!newCfgDb->open())
+ {
+ notifyError(tr("Could not open current configuration file in order to migrate settings from old configuration file."));
+ delete newCfgDb;
+ return;
+ }
+
+ newCfgDb->begin();
+ bool migrated = migrateSelected(oldCfgDb, newCfgDb);
+ if (migrated && !newCfgDb->commit())
+ {
+ notifyError(tr("Could not commit migrated data into new configuration file: %1").arg(newCfgDb->getErrorText()));
+ newCfgDb->rollback();
+ }
+ else if (!migrated)
+ {
+ newCfgDb->rollback();
+ }
+ else
+ {
+ finalize();
+ }
+ oldCfgDb->close();
+ newCfgDb->close();
+ delete newCfgDb;
+ clearFunctions();
+}
+
+bool ConfigMigrationWizard::migrateSelected(Db* oldCfgDb, Db* newCfgDb)
+{
+ if (checkedTypes.contains(ConfigMigrationItem::Type::BUG_REPORTS) && !migrateBugReports(oldCfgDb, newCfgDb))
+ return false;
+
+ if (checkedTypes.contains(ConfigMigrationItem::Type::DATABASES) && !migrateDatabases(oldCfgDb, newCfgDb))
+ return false;
+
+ if (checkedTypes.contains(ConfigMigrationItem::Type::FUNCTION_LIST) && !migrateFunction(oldCfgDb, newCfgDb))
+ return false;
+
+ if (checkedTypes.contains(ConfigMigrationItem::Type::SQL_HISTORY) && !migrateSqlHistory(oldCfgDb, newCfgDb))
+ return false;
+
+ return true;
+}
+
+bool ConfigMigrationWizard::migrateBugReports(Db* oldCfgDb, Db* newCfgDb)
+{
+ static_qstring(oldBugsQuery, "SELECT created_on, brief, url, type FROM bugs");
+ static_qstring(newBugsInsert, "INSERT INTO reports_history (timestamp, feature_request, title, url) VALUES (?, ?, ?, ?)");
+
+ SqlQueryPtr insertResults;
+ SqlResultsRowPtr row;
+ SqlQueryPtr results = oldCfgDb->exec(oldBugsQuery);
+ if (results->isError())
+ {
+ notifyError(tr("Could not read bug reports history from old configuration file in order to migrate it: %1").arg(results->getErrorText()));
+ return false;
+ }
+
+ bool feature;
+ QString url;
+ while (results->hasNext())
+ {
+ row = results->next();
+ feature = (row->value("type").toString().toUpper() == "FEATURE");
+ url = row->value("url").toString().trimmed();
+ if (url.startsWith("http://") && url.contains("sqlitestudio.one.pl"))
+ url.replace("sqlitestudio.one.pl", "sqlitestudio.pl").replace("report_bug.rvt", "report_bug3.rvt");
+
+ insertResults = newCfgDb->exec(newBugsInsert, {row->value("created_on"), feature, row->value("brief"), url});
+ if (insertResults->isError())
+ {
+ notifyError(tr("Could not insert a bug reports history entry into new configuration file: %1").arg(insertResults->getErrorText()));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ConfigMigrationWizard::migrateDatabases(Db* oldCfgDb, Db* newCfgDb)
+{
+ static_qstring(oldDbListQuery, "SELECT name, path FROM dblist");
+ static_qstring(newDbListInsert, "INSERT INTO dblist (name, path) VALUES (?, ?)");
+ static_qstring(groupOrderQuery, "SELECT max([order]) + 1 FROM groups WHERE parent %1");
+ static_qstring(groupInsert, "INSERT INTO groups (name, [order], parent, open, dbname) VALUES (?, ?, ?, ?, ?)");
+
+ SqlQueryPtr groupResults;
+ SqlQueryPtr insertResults;
+ SqlResultsRowPtr row;
+ SqlQueryPtr results = oldCfgDb->exec(oldDbListQuery);
+ if (results->isError())
+ {
+ notifyError(tr("Could not read database list from old configuration file in order to migrate it: %1").arg(results->getErrorText()));
+ return false;
+ }
+
+ // Creating containing group
+ bool putInGroup = ui->dbGroup->isEnabled() && ui->dbGroup->isChecked();
+ qint64 groupId = -1;
+ int order;
+ if (putInGroup)
+ {
+ // Query order
+ groupResults = newCfgDb->exec(groupOrderQuery.arg("IS NULL"));
+ if (groupResults->isError())
+ {
+ notifyError(tr("Could query for available order for containing group in new configuration file in order to migrate the database list: %1")
+ .arg(groupResults->getErrorText()));
+ return false;
+ }
+
+ order = groupResults->getSingleCell().toInt();
+
+ // Insert group
+ groupResults = newCfgDb->exec(groupInsert, {ui->groupNameEdit->text(), order, QVariant(), 1, QVariant()});
+ if (groupResults->isError())
+ {
+ notifyError(tr("Could not create containing group in new configuration file in order to migrate the database list: %1").arg(groupResults->getErrorText()));
+ return false;
+ }
+ groupId = groupResults->getRegularInsertRowId();
+ }
+
+ // Migrating the list
+ QString name;
+ QString path;
+ while (results->hasNext())
+ {
+ row = results->next();
+ name = row->value("name").toString();
+ path = row->value("path").toString();
+
+ if (DBLIST->getByName(name) || DBLIST->getByPath(path)) // already on the new list
+ continue;
+
+ insertResults = newCfgDb->exec(newDbListInsert, {name, path});
+ if (insertResults->isError())
+ {
+ notifyError(tr("Could not insert a database entry into new configuration file: %1").arg(insertResults->getErrorText()));
+ return false;
+ }
+
+ // Query order
+ if (putInGroup)
+ groupResults = newCfgDb->exec(groupOrderQuery.arg("= ?"), {groupId});
+ else
+ groupResults = newCfgDb->exec(groupOrderQuery.arg("IS NULL"));
+
+ if (groupResults->isError())
+ {
+ notifyError(tr("Could query for available order for next database in new configuration file in order to migrate the database list: %1")
+ .arg(groupResults->getErrorText()));
+ return false;
+ }
+
+ order = groupResults->getSingleCell().toInt();
+
+ // Insert group
+ groupResults = newCfgDb->exec(groupInsert, {QVariant(), order, putInGroup ? QVariant(groupId) : QVariant(), 0, name});
+ if (groupResults->isError())
+ {
+ notifyError(tr("Could not create group referencing the database in new configuration file: %1").arg(groupResults->getErrorText()));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ConfigMigrationWizard::migrateFunction(Db* oldCfgDb, Db* newCfgDb)
+{
+ UNUSED(newCfgDb);
+
+ static_qstring(oldFunctionsQuery, "SELECT name, type, code FROM functions");
+
+ SqlResultsRowPtr row;
+ SqlQueryPtr results = oldCfgDb->exec(oldFunctionsQuery);
+ if (results->isError())
+ {
+ notifyError(tr("Could not read function list from old configuration file in order to migrate it: %1").arg(results->getErrorText()));
+ return false;
+ }
+
+ clearFunctions();
+ for (FunctionManager::ScriptFunction* fn : FUNCTIONS->getAllScriptFunctions())
+ fnList << new FunctionManager::ScriptFunction(*fn);
+
+ FunctionManager::ScriptFunction* fn = nullptr;
+ while (results->hasNext())
+ {
+ row = results->next();
+
+ fn = new FunctionManager::ScriptFunction();
+ fn->type = FunctionManager::ScriptFunction::SCALAR;
+ fn->lang = row->value("type").toString();
+ fn->name = row->value("name").toString();
+ fn->code = row->value("code").toString();
+ fnList << fn;
+ }
+
+ return true;
+}
+
+bool ConfigMigrationWizard::migrateSqlHistory(Db* oldCfgDb, Db* newCfgDb)
+{
+ static_qstring(historyIdQuery, "SELECT CASE WHEN max(id) IS NULL THEN 0 ELSE max(id) + 1 END FROM sqleditor_history");
+ static_qstring(oldHistoryQuery, "SELECT dbname, date, time, rows, sql FROM history");
+ static_qstring(newHistoryInsert, "INSERT INTO sqleditor_history (id, dbname, date, time_spent, rows, sql) VALUES (?, ?, ?, ?, ?, ?)");
+
+ SqlQueryPtr insertResults;
+ SqlResultsRowPtr row;
+ SqlQueryPtr results = oldCfgDb->exec(oldHistoryQuery);
+ if (results->isError())
+ {
+ notifyError(tr("Could not read SQL queries history from old configuration file in order to migrate it: %1").arg(results->getErrorText()));
+ return false;
+ }
+
+ SqlQueryPtr idResults = newCfgDb->exec(historyIdQuery);
+ if (idResults->isError())
+ {
+ notifyError(tr("Could not read next ID for SQL queries history in new configuration file: %1").arg(idResults->getErrorText()));
+ return false;
+ }
+ qint64 nextId = idResults->getSingleCell().toLongLong();
+
+ int timeSpent;
+ int date;
+ while (results->hasNext())
+ {
+ row = results->next();
+ timeSpent = qRound(row->value("time").toDouble() * 1000);
+ date = QDateTime::fromString(row->value("date").toString(), "yyyy-MM-dd HH:mm").toTime_t();
+
+ insertResults = newCfgDb->exec(newHistoryInsert, {nextId++, row->value("dbname"), date, timeSpent, row->value("rows"), row->value("sql")});
+ if (insertResults->isError())
+ {
+ notifyError(tr("Could not insert SQL history entry into new configuration file: %1").arg(insertResults->getErrorText()));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ConfigMigrationWizard::finalize()
+{
+ if (checkedTypes.contains(ConfigMigrationItem::Type::FUNCTION_LIST))
+ {
+ FUNCTIONS->setScriptFunctions(fnList);
+ fnList.clear();
+ }
+
+ if (checkedTypes.contains(ConfigMigrationItem::Type::SQL_HISTORY))
+ CFG->refreshSqlHistory();
+
+ if (checkedTypes.contains(ConfigMigrationItem::Type::DATABASES))
+ {
+ bool ignore = DBTREE->getModel()->getIgnoreDbLoadedSignal();
+ DBTREE->getModel()->setIgnoreDbLoadedSignal(true);
+ DBLIST->scanForNewDatabasesInConfig();
+ DBTREE->getModel()->setIgnoreDbLoadedSignal(ignore);
+ DBTREE->getModel()->loadDbList();
+ }
+
+ migrated = true;
+}
+
+void ConfigMigrationWizard::collectCheckedTypes()
+{
+ checkedTypes.clear();
+
+ QTreeWidgetItem* item = nullptr;
+ for (int i = 0, total = ui->itemsTree->topLevelItemCount(); i < total; ++i)
+ {
+ item = ui->itemsTree->topLevelItem(i);
+ if (item->checkState(0) != Qt::Checked)
+ continue;
+
+ checkedTypes << static_cast<ConfigMigrationItem::Type>(item->data(0, Qt::UserRole).toInt());
+ }
+}
+
+void ConfigMigrationWizard::clearFunctions()
+{
+ for (FunctionManager::ScriptFunction* fn : fnList)
+ delete fn;
+
+ fnList.clear();
+}
+
+void ConfigMigrationWizard::updateOptions()
+{
+ if (currentPage() == ui->optionsPage)
+ {
+ collectCheckedTypes();
+ ui->dbGroup->setEnabled(checkedTypes.contains(ConfigMigrationItem::Type::DATABASES));
+ }
+}
diff --git a/Plugins/ConfigMigration/configmigrationwizard.h b/Plugins/ConfigMigration/configmigrationwizard.h
new file mode 100644
index 0000000..44a9f16
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigrationwizard.h
@@ -0,0 +1,52 @@
+#ifndef CONFIGMIGRATIONWIZARD_H
+#define CONFIGMIGRATIONWIZARD_H
+
+#include "configmigrationitem.h"
+#include "services/functionmanager.h"
+#include <QWizard>
+
+namespace Ui {
+class ConfigMigrationWizard;
+}
+
+class ConfigMigration;
+class Db;
+
+class ConfigMigrationWizard : public QWizard
+{
+ Q_OBJECT
+
+ public:
+ ConfigMigrationWizard(QWidget *parent, ConfigMigration* cfgMigration);
+ ~ConfigMigrationWizard();
+ bool didMigrate();
+
+ private:
+ void init();
+ void migrate();
+ bool migrateSelected(Db* oldCfgDb, Db* newCfgDb);
+ bool migrateBugReports(Db* oldCfgDb, Db* newCfgDb);
+ bool migrateDatabases(Db* oldCfgDb, Db* newCfgDb);
+ bool migrateFunction(Db* oldCfgDb, Db* newCfgDb);
+ bool migrateSqlHistory(Db* oldCfgDb, Db* newCfgDb);
+ void finalize();
+ void collectCheckedTypes();
+ void clearFunctions();
+
+ Ui::ConfigMigrationWizard *ui = nullptr;
+ ConfigMigration* cfgMigration = nullptr;
+ QList<ConfigMigrationItem::Type> checkedTypes;
+ QList<FunctionManager::ScriptFunction*> fnList;
+ bool migrated = false;
+
+ private slots:
+ void updateOptions();
+
+ public slots:
+ void accept();
+
+ signals:
+ void updateOptionsValidation();
+};
+
+#endif // CONFIGMIGRATIONWIZARD_H
diff --git a/Plugins/ConfigMigration/configmigrationwizard.ui b/Plugins/ConfigMigration/configmigrationwizard.ui
new file mode 100644
index 0000000..ce35430
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigrationwizard.ui
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigMigrationWizard</class>
+ <widget class="QWizard" name="ConfigMigrationWizard">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>392</width>
+ <height>282</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configuration migration</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QTreeView::item {margin-top: 5px;}</string>
+ </property>
+ <property name="options">
+ <set>QWizard::NoDefaultButton</set>
+ </property>
+ <widget class="QWizardPage" name="itemsPage">
+ <property name="title">
+ <string>Items to migrate</string>
+ </property>
+ <property name="subTitle">
+ <string>This is a list of items found in the old configuration file, which can be migrated into the current configuration.</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTreeWidget" name="itemsTree">
+ <property name="indentation">
+ <number>0</number>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ <property name="expandsOnDoubleClick">
+ <bool>false</bool>
+ </property>
+ <attribute name="headerVisible">
+ <bool>false</bool>
+ </attribute>
+ <column>
+ <property name="text">
+ <string notr="true">1</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="VerifiableWizardPage" name="optionsPage">
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="dbGroup">
+ <property name="title">
+ <string>Put imported databases into separate group</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QLineEdit" name="groupNameEdit">
+ <property name="placeholderText">
+ <string>Group name</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>205</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>VerifiableWizardPage</class>
+ <extends>QWizardPage</extends>
+ <header>common/verifiablewizardpage.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>