Commit a447282b authored by Karel Slaný's avatar Karel Slaný
Browse files

Merge branch 'database-location' into 'develop'

Database Location Change

The added code allows the user to change the location where all account-related databases are located.

Addresses issue #31.

See merge request !27
parents 139ecd7a 92757db1
......@@ -244,9 +244,7 @@ Component {
}
}
Column {
/* TODO - deactivate this option */
visible: false
//----------------------------------
id: changeDatabaseLocation
spacing: 1
Label {
id: dbPathLabel
......@@ -270,8 +268,9 @@ Component {
var newLocation = settings.changeDbPath(dbPathText.text.toString(), false)
if (newLocation != "") {
if (newLocation != dbPathText.text) {
if (messages.moveOrCopyDbToNewLocation(newLocation)) {
if (messages.moveOrCreateNewDbsToNewLocation(newLocation)) {
dbPathText.text = newLocation;
dbResetPathButton.visible = true
}
}
}
......@@ -284,8 +283,9 @@ Component {
var newLocation = settings.changeDbPath(dbPathText.text.toString(), true)
if (newLocation != "") {
if (newLocation != dbPathText.text) {
if (messages.moveOrCopyDbToNewLocation(newLocation)) {
if (messages.moveOrCreateNewDbsToNewLocation(newLocation)) {
dbPathText.text = newLocation;
dbResetPathButton.visible = false
}
}
}
......@@ -314,7 +314,7 @@ Component {
color: datovkaPalette.mid
width: myWidht
wrapMode: Text.Wrap
text: "The action allows clean up local databases and reduce their size. Note: The action can take some time."
text: qsTr("The action allows clean up local databases and reduce their size. Note: The action can take some time.")
}
}
Connections {
......@@ -323,6 +323,8 @@ Component {
messageLifeSpinBox.setVal(msgLifeDays)
attachLifeSpinBox.setVal(fileLifeDays)
dbPathText.text = dbPath
changeDatabaseLocation.visible = showChangeDbLocation
dbResetPathButton.visible = showSetDefaulButton
}
}
Connections {
......
......@@ -29,6 +29,7 @@
#define APP_ORG_DOMAIN "cz.nic"
#define DEFAULT_FONT_FAMILY "System"
#define DEFAULT_FONT_SIZE 16
#define INITIAL_DB_SIZE_BYTES 1000000 /* 1 MB free disk space is required for new database */
/* HTML INFO MACRO */
......
......@@ -61,6 +61,13 @@ QString getBasePathBasedOnPlatform(void)
}
QString getDefaultDbAndConfigLocation(void)
{
return existingWritableLocation(QStandardPaths::AppDataLocation);
}
QString setFilePath(const QString &basePath, const QString &destDir)
{
QString newPath = basePath;
......
......@@ -41,6 +41,14 @@
QString getBasePathBasedOnPlatform(void);
/*!
* @brief Returns default location for database files and datovka.conf.
*
* @return Location path or empty string on error.
*/
QString getDefaultDbAndConfigLocation(void);
/*!
* @brief Set file destination path and dir.
*
......
......@@ -22,8 +22,11 @@
*/
#include <QDebug>
#include <QFile>
#include <QMessageBox>
#include <QPair>
#include <QPushButton>
#include <QStorageInfo>
#include "src/common.h"
#include "src/messages.h"
......@@ -192,101 +195,58 @@ void Messages::deleteMessageFromDbs(const QString &userName, qint64 msgId)
}
}
/* ========================================================================= */
/*
* Slot: Move or copy databases to new location.
*/
bool Messages::moveOrCopyDbToNewLocation(const QString &newLocation)
/* ========================================================================= */
bool Messages::moveOrCreateNewDbsToNewLocation(const QString &newLocation)
{
qDebug("%s()", __func__);
QString action = "";
bool success = true;
int allDbSize = 0;
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Change database location"));
msgBox.setIcon(QMessageBox::Question);
msgBox.setText(tr(
"You can move or copy the current databases to a new location or create new empty databases."));
msgBox.setInformativeText(tr(
"What do you want to do with the currently used databases?"));
QAbstractButton *moveButton = msgBox.addButton(tr("Move"),
QMessageBox::ActionRole);
QAbstractButton *copyButton = msgBox.addButton(tr("Copy"),
QMessageBox::ActionRole);
QAbstractButton *newButton = msgBox.addButton(tr("Create new"),
QMessageBox::ActionRole);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel);
msgBox.exec();
if (msgBox.clickedButton() == moveButton) {
action = "move";
} else if (msgBox.clickedButton() == copyButton) {
action = "copy";
} else if (msgBox.clickedButton() == newButton) {
action = "new";
} else {
/* Select action what to do with the currently used databases. */
enum ReloactionAction action = askAction();
switch (action) {
case RA_NONE:
/* Do nothing and exit. */
return false;
break;
case RA_RELOCATE:
allDbSize = getAllDbSize();
break;
case RA_CREATE_NEW:
allDbSize = estimateAllDbSize();
break;
default:
/* Unsupported action. */
Q_ASSERT(0);
return false;
break;
}
QStringList userNameList = AccountListModel::globAccounts.keys();
foreach (const QString &userName, userNameList) {
MessageDb *msgDb = NULL;
FileDb *fDb = NULL;
if (action == "new") {
msgDb = globMessageDbsPtr->accessMessageDb(newLocation, userName,
AccountListModel::globAccounts[userName].storeToDisk());
if (msgDb == NULL) {
qDebug() << "ERROR: Message database cannot open!" << userName;
return false;
}
fDb = globFileDbsPtr->accessFileDb(newLocation, userName,
AccountListModel::globAccounts[userName].storeToDisk());
if (fDb == NULL) {
qDebug() << "ERROR: File database cannot open!" << userName;
return false;
}
} else {
msgDb = globMessageDbsPtr->accessMessageDb(globSet.dbsLocation, userName,
AccountListModel::globAccounts[userName].storeToDisk());
if (msgDb == NULL) {
qDebug() << "ERROR: Message database cannot open!" << userName;
return false;
}
fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName,
AccountListModel::globAccounts[userName].storeToDisk());
if (fDb == NULL) {
qDebug() << "ERROR: File database cannot open!" << userName;
return false;
}
if (action == "move") {
success = success && msgDb->moveDb(newLocation);
success = success && fDb->moveDb(newLocation);
} else if (action == "copy") {
success = success && msgDb->copyDb(newLocation);
success = success && fDb->copyDb(newLocation);
} else {
return false;
}
}
if (allDbSize <= 0) {
/* Expecting a positive value. */
return false;
}
if (success) {
globSet.dbsLocation = newLocation;
saveAllSettings();
/* Check whether new location has required size for database storing. */
{
QStorageInfo si(newLocation);
if (si.bytesAvailable() < allDbSize) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("New location error"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setText(tr("It is not possible to store databases in the new location because there is enough space."));
msgBox.setInformativeText(tr("Databases size is %1 MB.")
.arg(allDbSize / (1024 * 1024)));
msgBox.exec();
return false;
}
}
return success;
return relocateDatabases(newLocation, action);
}
/* ========================================================================= */
/*
* Func: Delete messages from all message databases together with files
......@@ -333,3 +293,199 @@ void Messages::deleteExpiredMessagesFromDbs(int days)
msgDb->commitTransaction();
}
}
enum Messages::ReloactionAction Messages::askAction(void)
{
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Change database location"));
msgBox.setIcon(QMessageBox::Question);
msgBox.setText(tr(
"What do you want to do with the currently used database files?"));
msgBox.setInformativeText(tr(
"You can move current database to a new location or you can delete them and create new empty databases."));
QAbstractButton *moveButton = msgBox.addButton(tr("Move"),
QMessageBox::ActionRole);
QAbstractButton *newButton = msgBox.addButton(tr("Create new"),
QMessageBox::ActionRole);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel);
msgBox.exec();
if (msgBox.clickedButton() == moveButton) {
return RA_RELOCATE;
} else if (msgBox.clickedButton() == newButton) {
return RA_CREATE_NEW;
} else {
return RA_NONE;
}
}
int Messages::getAllDbSize(void)
{
int size = 0;
QStringList userNameList(AccountListModel::globAccounts.keys());
foreach (const QString &userName, userNameList) {
MessageDb *msgDb = globMessageDbsPtr->accessMessageDb(
globSet.dbsLocation, userName,
AccountListModel::globAccounts[userName].storeToDisk());
if (msgDb == NULL) {
qDebug("ERROR: Cannot open message database for user name '%s'.", userName.toUtf8().constData());
return -1;
}
size += msgDb->getDbSizeInBytes();
FileDb *fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation,
userName,
AccountListModel::globAccounts[userName].storeToDisk());
if (fDb == NULL) {
qDebug("ERROR: Cannot open file database for user name '%s'.", userName.toUtf8().constData());
return -1;
}
size += fDb->getDbSizeInBytes();
}
return size;
}
int Messages::estimateAllDbSize(void)
{
return
2 * AccountListModel::globAccounts.size() * INITIAL_DB_SIZE_BYTES;
}
bool Messages::relocateDatabases(const QString &newLocation,
enum ReloactionAction action)
{
if ((action != RA_RELOCATE) && (action != RA_CREATE_NEW)) {
return false;
}
/* List of message databases and old locations. */
typedef QPair<MessageDb *, QString> MsgDbListEntry;
QList< MsgDbListEntry > relocatedMsgDbs;
typedef QPair<FileDb *, QString> FileDbListEntry;
QList< FileDbListEntry > relocatedFileDbs;
bool relocationSucceeded = true;
QStringList userNameList(AccountListModel::globAccounts.keys());
foreach (const QString &userName, userNameList) {
/* Ignore accounts with data stored in memory. */
if (!AccountListModel::globAccounts[userName].storeToDisk()) {
continue;
}
MessageDb *mDb = globMessageDbsPtr->accessMessageDb(
globSet.dbsLocation, userName,
AccountListModel::globAccounts[userName].storeToDisk());
relocationSucceeded = (mDb != NULL);
if (!relocationSucceeded) {
qWarning("Cannot access message database for user name '%s'.",
userName.toUtf8().constData());
break; /* Break the for cycle. */
}
FileDb *fDb = globFileDbsPtr->accessFileDb(
globSet.dbsLocation, userName,
AccountListModel::globAccounts[userName].storeToDisk());
relocationSucceeded = (fDb != NULL);
if (!relocationSucceeded) {
qWarning("Cannot access file database for user name '%s'.",
userName.toUtf8().constData());
break; /* Break the for cycle. */
}
QString oldLocation;
/* Message database file. */
oldLocation = mDb->fileName();
switch (action) {
case RA_RELOCATE:
relocationSucceeded = mDb->copyDb(newLocation);
break;
case RA_CREATE_NEW:
relocationSucceeded = mDb->createDb(newLocation);
break;
default:
Q_ASSERT(0);
relocationSucceeded = false;
break;
}
if (relocationSucceeded) {
/*
* Remember original file name as the file still
* exists.
*/
relocatedMsgDbs.append(
MsgDbListEntry(mDb, oldLocation));
} else {
break; /* Break the for cycle. */
}
/* File database file. */
oldLocation = fDb->fileName();
switch (action) {
case RA_RELOCATE:
relocationSucceeded = fDb->copyDb(newLocation);
break;
case RA_CREATE_NEW:
relocationSucceeded = fDb->createDb(newLocation);
break;
default:
Q_ASSERT(0);
relocationSucceeded = false;
break;
}
if (relocationSucceeded) {
/*
* Remember original file name as the file still
* exists.
*/
relocatedFileDbs.append(
FileDbListEntry(fDb, oldLocation));
} else {
break; /* Break the for cycle. */
}
}
if (relocationSucceeded) {
/* Delete all original files. */
foreach (const MsgDbListEntry &entry, relocatedMsgDbs) {
QFile::remove(entry.second);
}
foreach (const FileDbListEntry &entry, relocatedFileDbs) {
QFile::remove(entry.second);
}
} else {
/*
* Revert all databases to original locations and delete the
* new locations.
*/
QString newLocation;
foreach (const MsgDbListEntry &entry, relocatedMsgDbs) {
newLocation = entry.first->fileName();
if (entry.first->openDb(entry.second, true)) {
QFile::remove(newLocation);
} else {
qCritical(
"Cannot revert to original message database file '%s'.",
entry.second.toUtf8().constData());
/* TODO -- Critical. Cannot revert. */
}
}
foreach (const FileDbListEntry &entry, relocatedFileDbs) {
newLocation = entry.first->fileName();
if (entry.first->openDb(entry.second, true)) {
QFile::remove(newLocation);
} else {
qCritical(
"Cannot revert to original file database file '%s'.",
entry.second.toUtf8().constData());
/* TODO -- Critical. Cannot revert. */
}
}
}
return relocationSucceeded;
}
......@@ -74,10 +74,13 @@ public:
qint64 msgId);
/*!
* @brief Move or copy databases to new location.
* @brief Move or create new databases to new location.
*
* @param[in] newLocation New databases location (folder).
* @return True on success, false of failure.
*/
Q_INVOKABLE
bool moveOrCopyDbToNewLocation(const QString &newLocation);
bool moveOrCreateNewDbsToNewLocation(const QString &newLocation);
/*!
* @brief Delete messages from all message databases together with files
......@@ -158,6 +161,52 @@ signals:
* @brief Send message detait html string to QML.
*/
void messageDetailChanged(QString txt);
private:
/*!
* @brief Specifies whether the files should be moved or created from
* scratch.
*/
enum ReloactionAction {
RA_NONE, /*!< Don't do anything. */
RA_RELOCATE, /*!< relocate existing ones. */
RA_CREATE_NEW /*!< Create new empty databases. .*/
};
/*!
* @brief Generates a dialogue and asks the user what he wants to do.
*
* @return The chosen action.
*/
static
enum ReloactionAction askAction(void);
/*!
* @brief Return size of all databases in bytes.
*
* @return Total size of all database files or -1 on error.
*/
static
int getAllDbSize(void);
/*!
* @brief Estimate new database size in new location.
*
* @return Estimated required size.
*/
static
int estimateAllDbSize(void);
/*!
* @brief Relocates account database files.
*
* @param[in] newLocation New location (folder).
* @param[in] action Desired action to preform.
* @return True on success, false of failure.
*/
static
bool relocateDatabases(const QString &newLocation,
enum ReloactionAction action);
};
#endif // MESSAGES_H
......@@ -84,10 +84,11 @@ void Settings::saveToSettings(QSettings &settings) const
}
settings.setValue(SETTINGS_LAST_UPDATE, globSet.lastUpdate);
/*
* TODO - temporarily disabled
settings.setValue(SETTINGS_DBS_LOCATION, globSet.dbsLocation);
*/
if (globSet.dbsLocation != getDefaultDbAndConfigLocation()) {
settings.setValue(SETTINGS_DBS_LOCATION, globSet.dbsLocation);
}
settings.endGroup();
}
......@@ -130,14 +131,12 @@ void Settings::loadFromSettings(const QSettings &settings)
globSet.lastUpdate = settings.value(
SETTINGS_GLOBAL_GROUP "/" SETTINGS_LAST_UPDATE).toString();
/*
* TODO - temporarily disabled
globSet.dbsLocation = settings.value(
SETTINGS_GLOBAL_GROUP "/" SETTINGS_DBS_LOCATION).toString();
*/
if (globSet.dbsLocation.isEmpty()) {
globSet.dbsLocation =
existingWritableLocation(QStandardPaths::AppDataLocation);
globSet.dbsLocation = getDefaultDbAndConfigLocation();
}
}
......@@ -149,8 +148,7 @@ void Settings::loadFromSettings(const QSettings &settings)
QString Settings::settingsPath(void)
/* ========================================================================= */
{
QString settingsPath(existingWritableLocation(
QStandardPaths::AppLocalDataLocation));
QString settingsPath(getDefaultDbAndConfigLocation());
if (settingsPath.isEmpty()) {
return QString();
......
......@@ -23,7 +23,7 @@
#include <QtDebug> // qCritical
#include <QFileDialog>
#include <QStandardPaths>
#include <QMessageBox>
#include "src/crypto/pin.h"
#include "src/crypto/wrapped.h"
......@@ -87,9 +87,18 @@ void GlobalSettingsQmlWrapper::loadSettings(const QString &section)
} else if (section == "storage") {
emit sendSettingsStorageData(globSet.msgLifeTimeInDays,
globSet.fileLifeTimeInDays, globSet.dbsLocation);
bool showSetDefaulButton =
(globSet.dbsLocation != getDefaultDbAndConfigLocation());
#if defined Q_OS_IOS
emit sendSettingsStorageData(globSet.msgLifeTimeInDays,
globSet.fileLifeTimeInDays, globSet.dbsLocation,
false, showSetDefaulButton);
#else
emit sendSettingsStorageData(globSet.msgLifeTimeInDays,
globSet.fileLifeTimeInDays, globSet.dbsLocation,
true, showSetDefaulButton);
#endif
}
}
......@@ -254,15 +263,37 @@ QString GlobalSettingsQmlWrapper::changeDbPath(const QString &currentLocation,
bool setDefaultLocation)
/* ========================================================================= */
{
QString newLocation;
if (setDefaultLocation) {
newLocation =
existingWritableLocation(QStandardPaths::AppDataLocation);
} else {
QString newLocation = getDefaultDbAndConfigLocation();
if (!setDefaultLocation) {
newLocation = QFileDialog::getExistingDirectory(0,
tr("Select directory"), currentLocation,
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
QFileDialog::ShowDirsOnly
| QFileDialog::DontResolveSymlinks);
if (newLocation.isEmpty()) {
return QString();
}
} else {
if (currentLocation == newLocation) {
return QString();
}
}
/* check if new location is writable */
QFileInfo fi(newLocation);
if (!fi.isWritable()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("New location error"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setText(tr("It is not possible to store databases in the new "
"location. Write permission denied."));
msgBox.setInformativeText(tr("Action will be canceled."));
msgBox.exec();
return QString();
}
return newLocation;