experimental ability to purge articles from individual feeds - available with feed context menu and main menu Feeds submenu

This commit is contained in:
Martin Rotter 2025-01-07 15:03:03 +01:00
parent a352b7551e
commit 7722065544
9 changed files with 84 additions and 0 deletions

View file

@ -15,6 +15,7 @@ void DatabaseCleaner::purgeDatabaseData(CleanerOrders which_data) {
// Inform everyone about the start of the process. // Inform everyone about the start of the process.
emit purgeStarted(); emit purgeStarted();
bool result = true; bool result = true;
const int difference = 99 / 12; const int difference = 99 / 12;
int progress = 0; int progress = 0;
@ -22,51 +23,61 @@ void DatabaseCleaner::purgeDatabaseData(CleanerOrders which_data) {
if (which_data.m_removeReadMessages) { if (which_data.m_removeReadMessages) {
progress += difference; progress += difference;
emit purgeProgress(progress, tr("Removing read articles...")); emit purgeProgress(progress, tr("Removing read articles..."));
// Remove read messages. // Remove read messages.
result &= purgeReadMessages(database); result &= purgeReadMessages(database);
progress += difference; progress += difference;
emit purgeProgress(progress, tr("Read articles purged...")); emit purgeProgress(progress, tr("Read articles purged..."));
} }
if (which_data.m_removeRecycleBin) { if (which_data.m_removeRecycleBin) {
progress += difference; progress += difference;
emit purgeProgress(progress, tr("Purging recycle bin...")); emit purgeProgress(progress, tr("Purging recycle bin..."));
// Remove read messages. // Remove read messages.
result &= purgeRecycleBin(database); result &= purgeRecycleBin(database);
progress += difference; progress += difference;
emit purgeProgress(progress, tr("Recycle bin purged...")); emit purgeProgress(progress, tr("Recycle bin purged..."));
} }
if (which_data.m_removeOldMessages) { if (which_data.m_removeOldMessages) {
progress += difference; progress += difference;
emit purgeProgress(progress, tr("Removing old articles...")); emit purgeProgress(progress, tr("Removing old articles..."));
// Remove old messages. // Remove old messages.
result &= purgeOldMessages(database, which_data.m_barrierForRemovingOldMessagesInDays); result &= purgeOldMessages(database, which_data.m_barrierForRemovingOldMessagesInDays);
progress += difference; progress += difference;
emit purgeProgress(progress, tr("Old articles purged...")); emit purgeProgress(progress, tr("Old articles purged..."));
} }
if (which_data.m_removeStarredMessages) { if (which_data.m_removeStarredMessages) {
progress += difference; progress += difference;
emit purgeProgress(progress, tr("Removing starred articles...")); emit purgeProgress(progress, tr("Removing starred articles..."));
// Remove old messages. // Remove old messages.
result &= purgeStarredMessages(database); result &= purgeStarredMessages(database);
progress += difference; progress += difference;
emit purgeProgress(progress, tr("Starred articles purged...")); emit purgeProgress(progress, tr("Starred articles purged..."));
} }
if (which_data.m_shrinkDatabase) { if (which_data.m_shrinkDatabase) {
progress += difference; progress += difference;
emit purgeProgress(progress, tr("Shrinking database file...")); emit purgeProgress(progress, tr("Shrinking database file..."));
// Call driver-specific vacuuming function. // Call driver-specific vacuuming function.
result &= qApp->database()->driver()->vacuumDatabase(); result &= qApp->database()->driver()->vacuumDatabase();
progress += difference; progress += difference;
emit purgeProgress(progress, tr("Database file shrinked...")); emit purgeProgress(progress, tr("Database file shrinked..."));
} }

View file

@ -634,6 +634,27 @@ bool DatabaseQueries::removeUnwantedArticlesFromFeed(const QSqlDatabase& db,
return rows_deleted > 0; return rows_deleted > 0;
} }
bool DatabaseQueries::purgeFeedMessages(const QSqlDatabase& database, const Feed* feed) {
QSqlQuery q(database);
q.setForwardOnly(true);
q.prepare(QSL("DELETE FROM Messages "
"WHERE "
" Messages.account_id = :account_id AND "
" Messages.feed = :feed AND "
" Messages.is_important = 0"));
q.bindValue(QSL(":feed"), feed->customId());
q.bindValue(QSL(":account_id"), feed->getParentServiceRoot()->accountId());
if (!q.exec()) {
throw ApplicationException(q.lastError().text());
}
else {
return q.numRowsAffected() > 0;
}
}
bool DatabaseQueries::purgeMessage(const QSqlDatabase& db, int message_id) { bool DatabaseQueries::purgeMessage(const QSqlDatabase& db, int message_id) {
QSqlQuery q(db); QSqlQuery q(db);

View file

@ -76,6 +76,7 @@ class RSSGUARD_DLLSPEC DatabaseQueries {
const Feed::ArticleIgnoreLimit& feed_setup, const Feed::ArticleIgnoreLimit& feed_setup,
const Feed::ArticleIgnoreLimit& app_setup); const Feed::ArticleIgnoreLimit& app_setup);
static bool purgeFeedMessages(const QSqlDatabase& database, const Feed* feed);
static bool purgeMessage(const QSqlDatabase& db, int message_id); static bool purgeMessage(const QSqlDatabase& db, int message_id);
static bool purgeImportantMessages(const QSqlDatabase& db); static bool purgeImportantMessages(const QSqlDatabase& db);
static bool purgeReadMessages(const QSqlDatabase& db); static bool purgeReadMessages(const QSqlDatabase& db);

View file

@ -195,6 +195,7 @@ QList<QAction*> FormMain::allActions() const {
actions << m_ui->m_actionMarkSelectedItemsAsRead; actions << m_ui->m_actionMarkSelectedItemsAsRead;
actions << m_ui->m_actionMarkSelectedItemsAsUnread; actions << m_ui->m_actionMarkSelectedItemsAsUnread;
actions << m_ui->m_actionClearSelectedItems; actions << m_ui->m_actionClearSelectedItems;
actions << m_ui->m_actionPurgeSelectedItems;
actions << m_ui->m_actionClearAllItems; actions << m_ui->m_actionClearAllItems;
actions << m_ui->m_actionShowOnlyUnreadItems; actions << m_ui->m_actionShowOnlyUnreadItems;
actions << m_ui->m_actionSortFeedsAlphabetically; actions << m_ui->m_actionSortFeedsAlphabetically;
@ -499,6 +500,7 @@ void FormMain::updateFeedButtonsAvailability() {
m_ui->m_actionBackupDatabaseSettings->setEnabled(!critical_action_running); m_ui->m_actionBackupDatabaseSettings->setEnabled(!critical_action_running);
m_ui->m_actionCleanupDatabase->setEnabled(!critical_action_running); m_ui->m_actionCleanupDatabase->setEnabled(!critical_action_running);
m_ui->m_actionClearSelectedItems->setEnabled(anything_selected); m_ui->m_actionClearSelectedItems->setEnabled(anything_selected);
m_ui->m_actionPurgeSelectedItems->setEnabled(feed_selected);
m_ui->m_actionDeleteSelectedItem->setEnabled(!critical_action_running && anything_selected); m_ui->m_actionDeleteSelectedItem->setEnabled(!critical_action_running && anything_selected);
m_ui->m_actionEditSelectedItem->setEnabled(!critical_action_running && anything_selected); m_ui->m_actionEditSelectedItem->setEnabled(!critical_action_running && anything_selected);
m_ui->m_actionEditChildFeeds->setEnabled(!critical_action_running && (service_selected || category_selected)); m_ui->m_actionEditChildFeeds->setEnabled(!critical_action_running && (service_selected || category_selected));
@ -605,6 +607,7 @@ void FormMain::setupIcons() {
m_ui->m_actionUpdateSelectedItemsWithCustomTimers->setIcon(icon_theme_factory->fromTheme(QSL("download"), m_ui->m_actionUpdateSelectedItemsWithCustomTimers->setIcon(icon_theme_factory->fromTheme(QSL("download"),
QSL("browser-download"))); QSL("browser-download")));
m_ui->m_actionClearSelectedItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk"))); m_ui->m_actionClearSelectedItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk")));
m_ui->m_actionPurgeSelectedItems->setIcon(icon_theme_factory->fromTheme(QSL("edit-clear")));
m_ui->m_actionClearAllItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk"))); m_ui->m_actionClearAllItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk")));
m_ui->m_actionDeleteSelectedItem->setIcon(icon_theme_factory->fromTheme(QSL("list-remove"))); m_ui->m_actionDeleteSelectedItem->setIcon(icon_theme_factory->fromTheme(QSL("list-remove")));
m_ui->m_actionDeleteSelectedMessages->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk"))); m_ui->m_actionDeleteSelectedMessages->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk")));
@ -925,6 +928,10 @@ void FormMain::createConnections() {
&QAction::triggered, &QAction::triggered,
tabWidget()->feedMessageViewer()->feedsView(), tabWidget()->feedMessageViewer()->feedsView(),
&FeedsView::clearSelectedItems); &FeedsView::clearSelectedItems);
connect(m_ui->m_actionPurgeSelectedItems,
&QAction::triggered,
tabWidget()->feedMessageViewer()->feedsView(),
&FeedsView::purgeSelectedFeeds);
connect(m_ui->m_actionClearAllItems, connect(m_ui->m_actionClearAllItems,
&QAction::triggered, &QAction::triggered,
tabWidget()->feedMessageViewer()->feedsView(), tabWidget()->feedMessageViewer()->feedsView(),

View file

@ -151,6 +151,7 @@
<addaction name="m_actionCopyUrlSelectedFeed"/> <addaction name="m_actionCopyUrlSelectedFeed"/>
<addaction name="m_actionMarkSelectedItemsAsRead"/> <addaction name="m_actionMarkSelectedItemsAsRead"/>
<addaction name="m_actionMarkSelectedItemsAsUnread"/> <addaction name="m_actionMarkSelectedItemsAsUnread"/>
<addaction name="m_actionPurgeSelectedItems"/>
<addaction name="m_actionClearSelectedItems"/> <addaction name="m_actionClearSelectedItems"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="m_actionFocusSearchFeeds"/> <addaction name="m_actionFocusSearchFeeds"/>
@ -960,6 +961,11 @@
<string>&amp;Pause automatic feed fetching</string> <string>&amp;Pause automatic feed fetching</string>
</property> </property>
</action> </action>
<action name="m_actionPurgeSelectedItems">
<property name="text">
<string>&amp;Purge selected feeds</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View file

@ -254,6 +254,23 @@ void FeedsView::clearSelectedItems() {
} }
} }
void FeedsView::purgeSelectedFeeds() {
if (MsgBox::show(nullptr,
QMessageBox::Icon::Question,
tr("Are you sure?"),
tr("Do you really want to purge all non-starred articles from selected feeds?"),
{},
{},
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
QMessageBox::StandardButton::No) != QMessageBox::StandardButton::Yes) {
return;
}
for (auto* it : selectedFeeds(true)) {
it->purgeArticles();
}
}
void FeedsView::clearAllItems() { void FeedsView::clearAllItems() {
if (MsgBox::show(nullptr, if (MsgBox::show(nullptr,
QMessageBox::Icon::Question, QMessageBox::Icon::Question,

View file

@ -61,6 +61,7 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
// Feed clearers. // Feed clearers.
void clearSelectedItems(); void clearSelectedItems();
void clearAllItems(); void clearAllItems();
void purgeSelectedFeeds();
// Base manipulators. // Base manipulators.
void editItems(const QList<RootItem*>& items); void editItems(const QList<RootItem*>& items);

View file

@ -113,6 +113,24 @@ QVariant Feed::data(int column, int role) const {
} }
} }
void Feed::purgeArticles() {
auto database = qApp->database()->driver()->connection(metaObject()->className());
try {
bool anything_purged = DatabaseQueries::purgeFeedMessages(database, this);
if (anything_purged) {
getParentServiceRoot()->updateCounts(true);
getParentServiceRoot()->itemChanged(getParentServiceRoot()->getSubTree());
getParentServiceRoot()->requestReloadMessageList(false);
}
}
catch (const ApplicationException& ex) {
qCriticalNN << LOGSEC_CORE << "Purging of articles from feed" << QUOTE_W_SPACE(customId())
<< "failed with error:" << QUOTE_W_SPACE_DOT(ex.message());
}
}
int Feed::autoUpdateInterval() const { int Feed::autoUpdateInterval() const {
return m_autoUpdateInterval; return m_autoUpdateInterval;
} }

View file

@ -71,6 +71,8 @@ class RSSGUARD_DLLSPEC Feed : public RootItem {
virtual bool isFetching() const; virtual bool isFetching() const;
virtual QVariant data(int column, int role) const; virtual QVariant data(int column, int role) const;
void purgeArticles();
void setCountOfAllMessages(int count_all_messages); void setCountOfAllMessages(int count_all_messages);
void setCountOfUnreadMessages(int count_unread_messages); void setCountOfUnreadMessages(int count_unread_messages);