This commit is contained in:
Martin Rotter 2015-06-08 08:47:02 +02:00
parent 567f8e8dea
commit 562656ed9d
8 changed files with 155 additions and 40 deletions

View file

@ -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>

View file

@ -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
View 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
View 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();

View file

@ -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>

View file

@ -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
View 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
View 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