// This file is part of RSS Guard. // // Copyright (C) 2011-2017 by Martin Rotter // // RSS Guard is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // RSS Guard is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with RSS Guard. If not, see . #include "services/abstract/feed.h" #include "definitions/definitions.h" #include "miscellaneous/application.h" #include "miscellaneous/databasequeries.h" #include "miscellaneous/feedreader.h" #include "miscellaneous/mutex.h" #include "services/abstract/cacheforserviceroot.h" #include "services/abstract/recyclebin.h" #include "services/abstract/serviceroot.h" #include Feed::Feed(RootItem* parent) : RootItem(parent), m_url(QString()), m_status(Normal), m_autoUpdateType(DefaultAutoUpdate), m_autoUpdateInitialInterval(DEFAULT_AUTO_UPDATE_INTERVAL), m_autoUpdateRemainingInterval(DEFAULT_AUTO_UPDATE_INTERVAL), m_totalCount(0), m_unreadCount(0) { setKind(RootItemKind::Feed); setAutoDelete(false); } Feed::Feed(const Feed& other) : RootItem(other) { setCountOfAllMessages(other.countOfAllMessages()); setCountOfUnreadMessages(other.countOfUnreadMessages()); setUrl(other.url()); setStatus(other.status()); setAutoUpdateType(other.autoUpdateType()); setAutoUpdateInitialInterval(other.autoUpdateInitialInterval()); setAutoUpdateRemainingInterval(other.autoUpdateRemainingInterval()); } Feed::~Feed() {} QList Feed::undeletedMessages() const { QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); return DatabaseQueries::getUndeletedMessagesForFeed(database, customId(), getParentServiceRoot()->accountId()); } QVariant Feed::data(int column, int role) const { switch (role) { case Qt::ToolTipRole: if (column == FDS_MODEL_TITLE_INDEX) { QString auto_update_string; switch (autoUpdateType()) { case DontAutoUpdate: //: Describes feed auto-update status. auto_update_string = tr("does not use auto-update"); break; case DefaultAutoUpdate: //: Describes feed auto-update status. auto_update_string = tr("uses global settings"); break; case SpecificAutoUpdate: default: //: Describes feed auto-update status. auto_update_string = tr("uses specific settings " "(%n minute(s) to next auto-update)", 0, autoUpdateRemainingInterval()); break; } //: Tooltip for feed. return tr("%1" "%2\n\n" "Auto-update status: %3").arg(title(), description().isEmpty() ? QString() : QString('\n') + description(), auto_update_string); } else { return RootItem::data(column, role); } case Qt::ForegroundRole: switch (status()) { case NewMessages: return QColor(Qt::blue); case NetworkError: case ParsingError: case OtherError: return QColor(Qt::red); default: return QVariant(); } default: return RootItem::data(column, role); } } int Feed::autoUpdateInitialInterval() const { return m_autoUpdateInitialInterval; } int Feed::countOfAllMessages() const { return m_totalCount; } int Feed::countOfUnreadMessages() const { return m_unreadCount; } void Feed::setCountOfAllMessages(int count_all_messages) { m_totalCount = count_all_messages; } void Feed::setCountOfUnreadMessages(int count_unread_messages) { if (status() == NewMessages && count_unread_messages < countOfUnreadMessages()) { setStatus(Normal); } m_unreadCount = count_unread_messages; } void Feed::setAutoUpdateInitialInterval(int auto_update_interval) { // If new initial auto-update interval is set, then // we should reset time that remains to the next auto-update. m_autoUpdateInitialInterval = auto_update_interval; m_autoUpdateRemainingInterval = auto_update_interval; } Feed::AutoUpdateType Feed::autoUpdateType() const { return m_autoUpdateType; } void Feed::setAutoUpdateType(Feed::AutoUpdateType auto_update_type) { m_autoUpdateType = auto_update_type; } int Feed::autoUpdateRemainingInterval() const { return m_autoUpdateRemainingInterval; } void Feed::setAutoUpdateRemainingInterval(int auto_update_remaining_interval) { m_autoUpdateRemainingInterval = auto_update_remaining_interval; } Feed::Status Feed::status() const { return m_status; } void Feed::setStatus(const Feed::Status& status) { m_status = status; } QString Feed::url() const { return m_url; } void Feed::setUrl(const QString& url) { m_url = url; } void Feed::updateCounts(bool including_total_count) { bool is_main_thread = QThread::currentThread() == qApp->thread(); QSqlDatabase database = is_main_thread ? qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings) : qApp->database()->connection(QSL("feed_upd"), DatabaseFactory::FromSettings); int account_id = getParentServiceRoot()->accountId(); if (including_total_count) { setCountOfAllMessages(DatabaseQueries::getMessageCountsForFeed(database, customId(), account_id, true)); } setCountOfUnreadMessages(DatabaseQueries::getMessageCountsForFeed(database, customId(), account_id, false)); } void Feed::run() { qDebug().nospace() << "Downloading new messages for feed " << customId() << " in thread: \'" << QThread::currentThreadId() << "\'."; // Save all cached data first. getParentServiceRoot()->saveAllCachedData(); bool error_during_obtaining; QList msgs = obtainNewMessages(&error_during_obtaining); qDebug().nospace() << "Downloaded " << msgs.size() << " messages for feed " << customId() << " in thread: \'" << QThread::currentThreadId() << "\'."; // Now, do some general operations on messages (tweak encoding etc.). for (int i = 0; i < msgs.size(); i++) { // Also, make sure that HTML encoding, encoding of special characters, etc., is fixed. msgs[i].m_contents = QUrl::fromPercentEncoding(msgs[i].m_contents.toUtf8()); msgs[i].m_author = msgs[i].m_author.toUtf8(); // Sanitize title. Remove newlines etc. msgs[i].m_title = QUrl::fromPercentEncoding(msgs[i].m_title.toUtf8()) // Replace all continuous white space. .replace(QRegExp(QSL("[\\s]{2,}")), QSL(" ")) // Remove all newlines and leading white space. .remove(QRegExp(QSL("([\\n\\r])|(^\\s)"))); } emit messagesObtained(msgs, error_during_obtaining); } bool Feed::cleanMessages(bool clean_read_only) { return getParentServiceRoot()->cleanFeeds(QList() << this, clean_read_only); } bool Feed::markAsReadUnread(RootItem::ReadStatus status) { ServiceRoot* service = getParentServiceRoot(); CacheForServiceRoot* cache = dynamic_cast(service); if (cache != nullptr) { cache->addMessageStatesToCache(service->customIDSOfMessagesForItem(this), status); } return service->markFeedsReadUnread(QList() << this, status); } int Feed::updateMessages(const QList& messages, bool error_during_obtaining) { QList items_to_update; int updated_messages = 0; bool is_main_thread = QThread::currentThread() == qApp->thread(); qDebug("Updating messages in DB. Main thread: '%s'.", qPrintable(is_main_thread ? "true" : "false")); if (!error_during_obtaining) { bool anything_updated = false; bool ok = true; if (!messages.isEmpty()) { int custom_id = customId(); int account_id = getParentServiceRoot()->accountId(); QSqlDatabase database = is_main_thread ? qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings) : qApp->database()->connection(QSL("feed_upd"), DatabaseFactory::FromSettings); updated_messages = DatabaseQueries::updateMessages(database, messages, custom_id, account_id, url(), &anything_updated, &ok); } if (ok) { setStatus(updated_messages > 0 ? NewMessages : Normal); updateCounts(true); if (getParentServiceRoot()->recycleBin() != nullptr && anything_updated) { getParentServiceRoot()->recycleBin()->updateCounts(true); items_to_update.append(getParentServiceRoot()->recycleBin()); } } } items_to_update.append(this); getParentServiceRoot()->itemChanged(items_to_update); return updated_messages; } QString Feed::getAutoUpdateStatusDescription() const { QString auto_update_string; switch (autoUpdateType()) { case DontAutoUpdate: //: Describes feed auto-update status. auto_update_string = tr("does not use auto-update"); break; case DefaultAutoUpdate: //: Describes feed auto-update status. auto_update_string = tr("uses global settings (%n minute(s) to next auto-update)", 0, qApp->feedReader()->autoUpdateRemainingInterval()); break; case SpecificAutoUpdate: default: //: Describes feed auto-update status. auto_update_string = tr("uses specific settings (%n minute(s) to next auto-update)", 0, autoUpdateRemainingInterval()); break; } return auto_update_string; }