Fixed #101.
This commit is contained in:
parent
567f8e8dea
commit
562656ed9d
8 changed files with 155 additions and 40 deletions
|
@ -2,6 +2,7 @@
|
||||||
<center><h2>2.4.0 (not yet released)</h2></center>
|
<center><h2>2.4.0 (not yet released)</h2></center>
|
||||||
Added:
|
Added:
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><b>Initial support for databse cleaning. See menu 'Tools -> Cleanup database'. (issue #101)</b></li>
|
||||||
<li>RSS Guard is now able to export/import feed/category icons to/from OPML 2.0 files.</li>
|
<li>RSS Guard is now able to export/import feed/category icons to/from OPML 2.0 files.</li>
|
||||||
<li>Localizations now load their titles for localization list automatically.</li>
|
<li>Localizations now load their titles for localization list automatically.</li>
|
||||||
<li>All feeds are by default checked when exporting/importing them.</li>
|
<li>All feeds are by default checked when exporting/importing them.</li>
|
||||||
|
@ -14,6 +15,7 @@ Added:
|
||||||
|
|
||||||
Fixed:
|
Fixed:
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>Marking feed(s) unread now correctly marks also selected message unread.</li>
|
||||||
<li>Threads for feed updating are created only when really needed.</li>
|
<li>Threads for feed updating are created only when really needed.</li>
|
||||||
<li>Reworked DB initialization scripts which allow to use OPML to do initial feed population.</li>
|
<li>Reworked DB initialization scripts which allow to use OPML to do initial feed population.</li>
|
||||||
<li>Titles and descriptions of feeds are now fetched correctly in feed add/edit dialog.</li>
|
<li>Titles and descriptions of feeds are now fetched correctly in feed add/edit dialog.</li>
|
||||||
|
|
|
@ -443,10 +443,12 @@ void FeedMessageViewer::showDbCleanupAssistant() {
|
||||||
QPointer<FormDatabaseCleanup> form_pointer = new FormDatabaseCleanup(this);
|
QPointer<FormDatabaseCleanup> form_pointer = new FormDatabaseCleanup(this);
|
||||||
form_pointer.data()->setCleaner(databaseCleaner());
|
form_pointer.data()->setCleaner(databaseCleaner());
|
||||||
form_pointer.data()->exec();
|
form_pointer.data()->exec();
|
||||||
|
|
||||||
delete form_pointer.data();
|
delete form_pointer.data();
|
||||||
qApp->feedUpdateLock()->unlock();
|
qApp->feedUpdateLock()->unlock();
|
||||||
|
|
||||||
m_messagesView->reloadSelections(true);
|
m_messagesView->reloadSelections(false);
|
||||||
|
m_feedsView->updateCountsOfAllFeeds(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qApp->showGuiMessage(tr("Cannot cleanup database"),
|
qApp->showGuiMessage(tr("Cannot cleanup database"),
|
||||||
|
|
4
src/gui/feedsview.h
Normal file → Executable file
4
src/gui/feedsview.h
Normal file → Executable file
|
@ -135,7 +135,9 @@ class FeedsView : public QTreeView {
|
||||||
// Notifies other components about messages
|
// Notifies other components about messages
|
||||||
// counts.
|
// counts.
|
||||||
inline void notifyWithCounts() {
|
inline void notifyWithCounts() {
|
||||||
emit messageCountsChanged(m_sourceModel->countOfUnreadMessages(), m_sourceModel->countOfAllMessages(), m_sourceModel->hasAnyFeedNewMessages());
|
emit messageCountsChanged(m_sourceModel->countOfUnreadMessages(),
|
||||||
|
m_sourceModel->countOfAllMessages(),
|
||||||
|
m_sourceModel->hasAnyFeedNewMessages());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selects next/previous item (feed/category) in the list.
|
// Selects next/previous item (feed/category) in the list.
|
||||||
|
|
1
src/gui/formdatabasecleanup.cpp
Normal file → Executable file
1
src/gui/formdatabasecleanup.cpp
Normal file → Executable file
|
@ -81,6 +81,7 @@ void FormDatabaseCleanup::updateDaysSuffix(int number) {
|
||||||
void FormDatabaseCleanup::startPurging() {
|
void FormDatabaseCleanup::startPurging() {
|
||||||
CleanerOrders orders;
|
CleanerOrders orders;
|
||||||
|
|
||||||
|
orders.m_removeRecycleBin = m_ui->m_checkRemoveRecycleBin->isChecked();
|
||||||
orders.m_removeOldMessages = m_ui->m_checkRemoveOldMessages->isChecked();
|
orders.m_removeOldMessages = m_ui->m_checkRemoveOldMessages->isChecked();
|
||||||
orders.m_barrierForRemovingOldMessagesInDays = m_ui->m_spinDays->value();
|
orders.m_barrierForRemovingOldMessagesInDays = m_ui->m_spinDays->value();
|
||||||
orders.m_removeReadMessages = m_ui->m_checkRemoveReadMessages->isChecked();
|
orders.m_removeReadMessages = m_ui->m_checkRemoveReadMessages->isChecked();
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>444</width>
|
||||||
<height>313</height>
|
<height>359</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -17,17 +17,10 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="m_grpCleanupSettings">
|
<widget class="QGroupBox" name="m_grpCleanupSettings">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Cleanup settings</string>
|
<string>Cleanup settings (all checked items are completely erased from database)</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="1" column="0">
|
<item row="3" column="2">
|
||||||
<widget class="QCheckBox" name="m_checkShrink">
|
|
||||||
<property name="text">
|
|
||||||
<string>Shrink database file</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="2">
|
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
|
@ -40,27 +33,7 @@
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="3" column="1">
|
||||||
<widget class="QCheckBox" name="m_checkRemoveOldMessages">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remove all messages older than</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QCheckBox" name="m_checkRemoveReadMessages">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remove all read messages</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QSpinBox" name="m_spinDays">
|
<widget class="QSpinBox" name="m_spinDays">
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
|
@ -73,6 +46,43 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QCheckBox" name="m_checkRemoveOldMessages">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove all messages older than</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="m_checkRemoveRecycleBin">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove all messages from recycle bin</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="m_checkRemoveReadMessages">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove all read messages (not those from recycle bin)</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="m_checkShrink">
|
||||||
|
<property name="text">
|
||||||
|
<string>Shrink database file</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -87,6 +97,9 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Database file size</string>
|
<string>Database file size</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>m_txtFileSize</cstring>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
|
@ -101,6 +114,9 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Database type</string>
|
<string>Database type</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>m_txtDatabaseType</cstring>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
|
@ -172,6 +188,15 @@
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>m_checkRemoveReadMessages</tabstop>
|
||||||
|
<tabstop>m_checkRemoveRecycleBin</tabstop>
|
||||||
|
<tabstop>m_checkShrink</tabstop>
|
||||||
|
<tabstop>m_checkRemoveOldMessages</tabstop>
|
||||||
|
<tabstop>m_spinDays</tabstop>
|
||||||
|
<tabstop>m_txtFileSize</tabstop>
|
||||||
|
<tabstop>m_txtDatabaseType</tabstop>
|
||||||
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
|
|
|
@ -89,7 +89,7 @@ void MessagesView::reloadSelections(bool mark_current_index_read) {
|
||||||
current_index = m_proxyModel->mapFromSource(m_sourceModel->index(mapped_current_index.row(), mapped_current_index.column()));
|
current_index = m_proxyModel->mapFromSource(m_sourceModel->index(mapped_current_index.row(), mapped_current_index.column()));
|
||||||
|
|
||||||
if (current_index.isValid()) {
|
if (current_index.isValid()) {
|
||||||
if (mark_current_index_read) {
|
if (!mark_current_index_read) {
|
||||||
// User selected to mark some messages as unread, if one
|
// User selected to mark some messages as unread, if one
|
||||||
// of them will be marked as current, then it will be read again.
|
// of them will be marked as current, then it will be read again.
|
||||||
m_batchUnreadSwitch = true;
|
m_batchUnreadSwitch = true;
|
||||||
|
|
84
src/miscellaneous/databasecleaner.cpp
Normal file → Executable file
84
src/miscellaneous/databasecleaner.cpp
Normal file → Executable file
|
@ -21,10 +21,13 @@
|
||||||
#include "miscellaneous/databasefactory.h"
|
#include "miscellaneous/databasefactory.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QSqlQuery>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
|
||||||
DatabaseCleaner::DatabaseCleaner(QObject *parent) : QObject(parent) {
|
DatabaseCleaner::DatabaseCleaner(QObject *parent) : QObject(parent) {
|
||||||
|
setObjectName("DatabaseCleaner");
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseCleaner::~DatabaseCleaner() {
|
DatabaseCleaner::~DatabaseCleaner() {
|
||||||
|
@ -33,20 +36,91 @@ DatabaseCleaner::~DatabaseCleaner() {
|
||||||
void DatabaseCleaner::purgeDatabaseData(const CleanerOrders &which_data) {
|
void DatabaseCleaner::purgeDatabaseData(const CleanerOrders &which_data) {
|
||||||
qDebug().nospace() << "Performing database cleanup in thread: \'" << QThread::currentThreadId() << "\'.";
|
qDebug().nospace() << "Performing database cleanup in thread: \'" << QThread::currentThreadId() << "\'.";
|
||||||
|
|
||||||
bool result = true;
|
// Inform everyone about the start of the process.
|
||||||
int progress = 0;
|
|
||||||
|
|
||||||
emit purgeStarted();
|
emit purgeStarted();
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
int difference = 99 / 8;
|
||||||
|
int progress = 0;
|
||||||
|
QSqlDatabase database = qApp->database()->connection(objectName(), DatabaseFactory::FromSettings);
|
||||||
|
|
||||||
|
if (which_data.m_removeReadMessages) {
|
||||||
|
progress += difference;
|
||||||
|
emit purgeProgress(progress, tr("Removing read messages..."));
|
||||||
|
|
||||||
|
// Remove read messages.
|
||||||
|
result &= purgeReadMessages(database);
|
||||||
|
|
||||||
|
progress += difference;
|
||||||
|
emit purgeProgress(progress, tr("Read messages purged..."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (which_data.m_removeRecycleBin) {
|
||||||
|
progress += difference;
|
||||||
|
emit purgeProgress(progress, tr("Purgin recycle bin..."));
|
||||||
|
|
||||||
|
// Remove read messages.
|
||||||
|
result &= purgeRecycleBin(database);
|
||||||
|
|
||||||
|
progress += difference;
|
||||||
|
emit purgeProgress(progress, tr("Recycle bin purged..."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (which_data.m_removeOldMessages) {
|
||||||
|
progress += difference;
|
||||||
|
emit purgeProgress(progress, tr("Removing old messages..."));
|
||||||
|
|
||||||
|
// Remove old messages.
|
||||||
|
result &= purgeOldMessages(database, which_data.m_barrierForRemovingOldMessagesInDays);
|
||||||
|
|
||||||
|
progress += difference;
|
||||||
|
emit purgeProgress(progress, tr("Read old purged..."));
|
||||||
|
}
|
||||||
|
|
||||||
if (which_data.m_shrinkDatabase) {
|
if (which_data.m_shrinkDatabase) {
|
||||||
progress += 25;
|
progress += difference;
|
||||||
emit purgeProgress(progress, tr("Shrinking database file..."));
|
emit purgeProgress(progress, tr("Shrinking database file..."));
|
||||||
|
|
||||||
|
// Call driver-specific vacuuming function.
|
||||||
result &= qApp->database()->vacuumDatabase();
|
result &= qApp->database()->vacuumDatabase();
|
||||||
|
|
||||||
progress += 25;
|
progress += difference;
|
||||||
emit purgeProgress(progress, tr("Database file shrinked..."));
|
emit purgeProgress(progress, tr("Database file shrinked..."));
|
||||||
}
|
}
|
||||||
|
|
||||||
emit purgeFinished(result);
|
emit purgeFinished(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DatabaseCleaner::purgeReadMessages(const QSqlDatabase &database) {
|
||||||
|
QSqlQuery query = QSqlQuery(database);
|
||||||
|
|
||||||
|
query.setForwardOnly(true);
|
||||||
|
query.prepare("DELETE FROM Messages WHERE is_deleted = :is_deleted AND is_read = :is_read;");
|
||||||
|
query.bindValue(":is_read", 1);
|
||||||
|
|
||||||
|
// Remove only messages which are NOT in recycle bin.
|
||||||
|
query.bindValue(":is_deleted", 0);
|
||||||
|
|
||||||
|
return query.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseCleaner::purgeOldMessages(const QSqlDatabase &database, int days) {
|
||||||
|
QSqlQuery query = QSqlQuery(database);
|
||||||
|
qint64 since_epoch = QDateTime::currentDateTimeUtc().addDays(-days).toMSecsSinceEpoch();
|
||||||
|
|
||||||
|
query.setForwardOnly(true);
|
||||||
|
query.prepare("DELETE FROM Messages WHERE date_created < :date_created;");
|
||||||
|
query.bindValue(":date_created", since_epoch);
|
||||||
|
|
||||||
|
return query.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseCleaner::purgeRecycleBin(const QSqlDatabase &database) {
|
||||||
|
QSqlQuery query = QSqlQuery(database);
|
||||||
|
|
||||||
|
query.setForwardOnly(true);
|
||||||
|
query.prepare("DELETE FROM Messages WHERE is_deleted = :is_deleted;");
|
||||||
|
query.bindValue(":is_deleted", 1);
|
||||||
|
|
||||||
|
return query.exec();
|
||||||
|
}
|
||||||
|
|
9
src/miscellaneous/databasecleaner.h
Normal file → Executable file
9
src/miscellaneous/databasecleaner.h
Normal file → Executable file
|
@ -20,11 +20,14 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
|
||||||
|
|
||||||
struct CleanerOrders {
|
struct CleanerOrders {
|
||||||
bool m_removeReadMessages;
|
bool m_removeReadMessages;
|
||||||
bool m_shrinkDatabase;
|
bool m_shrinkDatabase;
|
||||||
bool m_removeOldMessages;
|
bool m_removeOldMessages;
|
||||||
|
bool m_removeRecycleBin;
|
||||||
int m_barrierForRemovingOldMessagesInDays;
|
int m_barrierForRemovingOldMessagesInDays;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,6 +35,7 @@ class DatabaseCleaner : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Constructors.
|
||||||
explicit DatabaseCleaner(QObject *parent = 0);
|
explicit DatabaseCleaner(QObject *parent = 0);
|
||||||
virtual ~DatabaseCleaner();
|
virtual ~DatabaseCleaner();
|
||||||
|
|
||||||
|
@ -42,6 +46,11 @@ class DatabaseCleaner : public QObject {
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void purgeDatabaseData(const CleanerOrders &which_data);
|
void purgeDatabaseData(const CleanerOrders &which_data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool purgeReadMessages(const QSqlDatabase &database);
|
||||||
|
bool purgeOldMessages(const QSqlDatabase &database, int days);
|
||||||
|
bool purgeRecycleBin(const QSqlDatabase &database);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DATABASECLEANER_H
|
#endif // DATABASECLEANER_H
|
||||||
|
|
Loading…
Add table
Reference in a new issue