some minor enhancements for OPML import + gmail email viewer uses skin styling, same as classic articles

This commit is contained in:
Martin Rotter 2023-03-16 07:37:49 +01:00
parent 163f012b71
commit 932222bf58
5 changed files with 93 additions and 89 deletions

View File

@ -52,7 +52,7 @@ void EmailPreviewer::loadMessage(const Message& msg, RootItem* selected_item) {
Q_UNUSED(selected_item)
m_message = msg;
m_webView->setHtml(msg.m_contents);
m_webView->loadMessages({msg}, selected_item);
m_ui.m_tbFrom->setText(msg.m_author);
m_ui.m_tbSubject->setText(msg.m_title);

View File

@ -130,22 +130,21 @@ void FormStandardImportExport::onParsingFinished(int count_failed, int count_suc
m_ui->m_progressBar->setValue(0);
m_model->checkAllItems();
if (count_failed > 0 && count_succeeded == 0) {
m_ui->m_groupFeeds->setEnabled(false);
m_ui->m_groupFetchMetadata->setEnabled(false);
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Error,
tr("Some feeds were not loaded properly or import file is corrupted."),
tr("Some feeds were not loaded properly or import file is corrupted."));
if (count_failed > 0) {
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Warning,
tr("Some feeds were not loaded properly. Check log for more information."),
tr("Some feeds were not loaded properly. Check log for more information."));
}
else {
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Ok, tr("Feeds were loaded."), tr("Feeds were loaded."));
m_ui->m_groupFeeds->setEnabled(true);
m_ui->m_groupFetchMetadata->setEnabled(true);
m_ui->m_btnSelectFile->setEnabled(true);
m_ui->m_treeFeeds->setModel(m_model);
m_ui->m_treeFeeds->expandAll();
}
m_ui->m_groupFeeds->setEnabled(true);
m_ui->m_groupFetchMetadata->setEnabled(true);
m_ui->m_btnSelectFile->setEnabled(true);
m_ui->m_treeFeeds->setModel(m_model);
m_ui->m_treeFeeds->expandAll();
m_ui->m_buttonBox->button(QDialogButtonBox::StandardButton::Ok)->setEnabled(true);
}
@ -215,13 +214,13 @@ void FormStandardImportExport::selectExportFile(bool without_dialog) {
void FormStandardImportExport::selectImportFile() {
const QString filter_opml20 = tr("OPML 2.0 files (*.opml *.xml)");
const QString filter_txt_url_per_line = tr("TXT files [one URL per line] (*.txt)");
QString filter;
QString selected_filter;
// Add more filters here.
filter += filter_opml20;
filter += QSL(";;");
filter += filter_txt_url_per_line;
filter += filter_opml20 + QSL(";;") + filter_txt_url_per_line;
const QString selected_file = QFileDialog::getOpenFileName(this,
tr("Select file for feeds import"),
qApp->homeFolder(),
@ -240,13 +239,24 @@ void FormStandardImportExport::selectImportFile() {
QDir::toNativeSeparators(selected_file),
tr("File is selected."));
parseImportFile(selected_file, m_ui->m_groupFetchMetadata->isChecked());
try {
parseImportFile(selected_file, m_ui->m_groupFetchMetadata->isChecked());
}
catch (const ApplicationException& ex) {
m_ui->m_btnSelectFile->setEnabled(true);
m_ui->m_progressBar->setVisible(false);
m_ui->m_progressBar->setValue(0);
m_ui->m_groupFeeds->setEnabled(false);
m_ui->m_groupFetchMetadata->setEnabled(true);
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Error, ex.message(), ex.message());
}
}
}
void FormStandardImportExport::parseImportFile(const QString& file_name, bool fetch_metadata_online) {
QFile input_file(file_name);
QByteArray input_data;
QFile input_file(file_name);
if (input_file.open(QIODevice::OpenModeFlag::Text | QIODevice::OpenModeFlag::Unbuffered |
QIODevice::OpenModeFlag::ReadOnly)) {
@ -254,10 +264,7 @@ void FormStandardImportExport::parseImportFile(const QString& file_name, bool fe
input_file.close();
}
else {
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Error,
tr("Cannot open source file."),
tr("Cannot open source file."));
return;
throw ApplicationException(tr("cannot open file"));
}
switch (m_conversionType) {

View File

@ -16,6 +16,8 @@
#include <QDomDocument>
#include <QDomElement>
#include <QLocale>
#include <QSqlDatabase>
#include <QSqlError>
#include <QStack>
#include <QtConcurrent/QtConcurrentMap>
@ -192,7 +194,7 @@ bool FeedsImportExportModel::produceFeed(const FeedLookup& feed_lookup) {
else {
new_feed = new StandardFeed();
if (feed_lookup.opml_element.isNull()) {
if (feed_lookup.custom_data.isEmpty()) {
new_feed->setSource(feed_lookup.url);
new_feed->setTitle(feed_lookup.url);
new_feed->setIcon(qApp->icons()->fromTheme(QSL("application-rss+xml")));
@ -200,15 +202,13 @@ bool FeedsImportExportModel::produceFeed(const FeedLookup& feed_lookup) {
new_feed->setPostProcessScript(feed_lookup.post_process_script);
}
else {
QString feed_title = feed_lookup.opml_element.attribute(QSL("text"));
QString feed_encoding = feed_lookup.opml_element.attribute(QSL("encoding"), QSL(DEFAULT_FEED_ENCODING));
QString feed_type = feed_lookup.opml_element.attribute(QSL("version"), QSL(DEFAULT_FEED_TYPE)).toUpper();
QString feed_description = feed_lookup.opml_element.attribute(QSL("description"));
QIcon feed_icon =
qApp->icons()->fromByteArray(feed_lookup.opml_element.attribute(QSL("rssguard:icon")).toLocal8Bit());
StandardFeed::SourceType source_type =
StandardFeed::SourceType(feed_lookup.opml_element.attribute(QSL("rssguard:xmlUrlType")).toInt());
QString post_process = feed_lookup.opml_element.attribute(QSL("rssguard:postProcess"));
QString feed_title = feed_lookup.custom_data[QSL("title")].toString();
QString feed_encoding = feed_lookup.custom_data.value(QSL("encoding"), QSL(DEFAULT_FEED_ENCODING)).toString();
QString feed_type = feed_lookup.custom_data.value(QSL("feedType"), QSL(DEFAULT_FEED_TYPE)).toString().toUpper();
QString feed_description = feed_lookup.custom_data[QSL("description")].toString();
QIcon feed_icon = feed_lookup.custom_data[QSL("icon")].value<QIcon>();
StandardFeed::SourceType source_type = feed_lookup.custom_data["sourceType"].value<StandardFeed::SourceType>();
QString post_process = feed_lookup.custom_data[QSL("postProcessScript")].toString();
new_feed->setTitle(feed_title);
new_feed->setDescription(feed_description);
@ -265,13 +265,12 @@ void FeedsImportExportModel::importAsOPML20(const QByteArray& data,
QDomDocument opml_document;
if (!opml_document.setContent(data)) {
emit parsingFinished(0, 0);
throw ApplicationException(tr("OPML document contains errors"));
}
if (opml_document.documentElement().isNull() || opml_document.documentElement().tagName() != QSL("opml") ||
opml_document.documentElement().elementsByTagName(QSL("body")).size() != 1) {
// This really is not an OPML file.
emit parsingFinished(0, 0);
throw ApplicationException(tr("this is likely not OPML document"));
}
int completed = 0, total = 0;
@ -311,10 +310,22 @@ void FeedsImportExportModel::importAsOPML20(const QByteArray& data,
if (!feed_url.isEmpty()) {
FeedLookup f;
QVariantMap feed_data;
feed_data["title"] = child_element.attribute(QSL("text"));
feed_data["encoding"] = child_element.attribute(QSL("encoding"), QSL(DEFAULT_FEED_ENCODING));
feed_data["type"] = child_element.attribute(QSL("version"), QSL(DEFAULT_FEED_TYPE)).toUpper();
feed_data["description"] = child_element.attribute(QSL("description"));
feed_data["icon"] =
qApp->icons()->fromByteArray(child_element.attribute(QSL("rssguard:icon")).toLocal8Bit());
feed_data["sourceType"] =
QVariant::fromValue(StandardFeed::SourceType(child_element.attribute(QSL("rssguard:xmlUrlType"))
.toInt()));
feed_data["postProcessScript"] = child_element.attribute(QSL("rssguard:postProcess"));
f.custom_proxy = custom_proxy;
f.fetch_metadata_online = fetch_metadata_online;
f.opml_element = child_element;
f.custom_data = feed_data;
f.parent = active_model_item;
f.post_process_script = post_process_script;
f.url = feed_url;

View File

@ -13,7 +13,7 @@ class StandardFeed;
struct FeedLookup {
RootItem* parent;
QDomElement opml_element;
QVariantMap custom_data;
QString url;
bool fetch_metadata_online;
QNetworkProxy custom_proxy;

View File

@ -38,8 +38,7 @@
#include <QStack>
#include <QTextCodec>
StandardServiceRoot::StandardServiceRoot(RootItem* parent)
: ServiceRoot(parent) {
StandardServiceRoot::StandardServiceRoot(RootItem* parent) : ServiceRoot(parent) {
setTitle(qApp->system()->loggedInUser() + QSL(" (RSS/ATOM/JSON)"));
setIcon(StandardServiceEntryPoint().icon());
setDescription(tr("This is obligatory service account for standard RSS/RDF/ATOM feeds."));
@ -54,10 +53,13 @@ void StandardServiceRoot::start(bool freshly_activated) {
if (freshly_activated && getSubTreeFeeds().isEmpty()) {
// In other words, if there are no feeds or categories added.
if (MsgBox::show(qApp->mainFormWidget(), QMessageBox::Question, QObject::tr("Load initial set of feeds"),
if (MsgBox::show(qApp->mainFormWidget(),
QMessageBox::Question,
QObject::tr("Load initial set of feeds"),
tr("This new account does not include any feeds. You can now add default set of feeds."),
tr("Do you want to load initial set of feeds?"),
QString(), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
QString(),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
QString target_opml_file = APP_INITIAL_FEEDS_PATH + QDir::separator() + FEED_INITIAL_OPML_PATTERN;
QString current_locale = qApp->localization()->loadedLanguage();
QString file_to_load;
@ -81,11 +83,14 @@ void StandardServiceRoot::start(bool freshly_activated) {
}
}
catch (ApplicationException& ex) {
MsgBox::show(qApp->mainFormWidget(), QMessageBox::Critical, tr("Error when loading initial feeds"), ex.message());
MsgBox::show(qApp->mainFormWidget(),
QMessageBox::Critical,
tr("Error when loading initial feeds"),
ex.message());
}
}
else {
requestItemExpand({ this }, true);
requestItemExpand({this}, true);
}
}
}
@ -122,10 +127,10 @@ void StandardServiceRoot::addNewFeed(RootItem* selected_item, const QString& url
// Lock was not obtained because
// it is used probably by feed updater or application
// is quitting.
qApp->showGuiMessage(Notification::Event::GeneralEvent, {
tr("Cannot add item"),
tr("Cannot add feed because another critical operation is ongoing."),
QSystemTrayIcon::MessageIcon::Warning });
qApp->showGuiMessage(Notification::Event::GeneralEvent,
{tr("Cannot add item"),
tr("Cannot add feed because another critical operation is ongoing."),
QSystemTrayIcon::MessageIcon::Warning});
return;
}
@ -144,7 +149,8 @@ Qt::ItemFlags StandardServiceRoot::additionalFlags() const {
}
QList<Message> StandardServiceRoot::obtainNewMessages(Feed* feed,
const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<ServiceRoot::BagOfMessages, QStringList>&
stated_messages,
const QHash<QString, QStringList>& tagged_messages) {
Q_UNUSED(stated_messages)
Q_UNUSED(tagged_messages)
@ -154,10 +160,7 @@ QList<Message> StandardServiceRoot::obtainNewMessages(Feed* feed,
int download_timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
if (f->sourceType() == StandardFeed::SourceType::Url) {
qDebugNN << LOGSEC_CORE
<< "Downloading URL"
<< QUOTE_W_SPACE(feed->source())
<< "to obtain feed data.";
qDebugNN << LOGSEC_CORE << "Downloading URL" << QUOTE_W_SPACE(feed->source()) << "to obtain feed data.";
QByteArray feed_contents;
QList<QPair<QByteArray, QByteArray>> headers;
@ -173,14 +176,12 @@ QList<Message> StandardServiceRoot::obtainNewMessages(Feed* feed,
false,
{},
{},
networkProxy()).m_networkError;
networkProxy())
.m_networkError;
if (network_result != QNetworkReply::NetworkError::NoError) {
qWarningNN << LOGSEC_CORE
<< "Error"
<< QUOTE_W_SPACE(network_result)
<< "during fetching of new messages for feed"
<< QUOTE_W_SPACE_DOT(feed->source());
qWarningNN << LOGSEC_CORE << "Error" << QUOTE_W_SPACE(network_result)
<< "during fetching of new messages for feed" << QUOTE_W_SPACE_DOT(feed->source());
throw FeedFetchException(Feed::Status::NetworkError, NetworkFactory::networkErrorText(network_result));
}
@ -197,38 +198,29 @@ QList<Message> StandardServiceRoot::obtainNewMessages(Feed* feed,
}
}
else {
qDebugNN << LOGSEC_CORE
<< "Running custom script"
<< QUOTE_W_SPACE(feed->source())
<< "to obtain feed data.";
qDebugNN << LOGSEC_CORE << "Running custom script" << QUOTE_W_SPACE(feed->source()) << "to obtain feed data.";
// Use script to generate feed file.
try {
formatted_feed_contents = StandardFeed::generateFeedFileWithScript(feed->source(), download_timeout);
}
catch (const ScriptException& ex) {
qCriticalNN << LOGSEC_CORE
<< "Custom script for generating feed file failed:"
<< QUOTE_W_SPACE_DOT(ex.message());
qCriticalNN << LOGSEC_CORE << "Custom script for generating feed file failed:" << QUOTE_W_SPACE_DOT(ex.message());
throw FeedFetchException(Feed::Status::OtherError, ex.message());
}
}
if (!f->postProcessScript().simplified().isEmpty()) {
qDebugNN << LOGSEC_CORE
<< "We will process feed data with post-process script"
qDebugNN << LOGSEC_CORE << "We will process feed data with post-process script"
<< QUOTE_W_SPACE_DOT(f->postProcessScript());
try {
formatted_feed_contents = StandardFeed::postProcessFeedFileWithScript(f->postProcessScript(),
formatted_feed_contents,
download_timeout);
formatted_feed_contents =
StandardFeed::postProcessFeedFileWithScript(f->postProcessScript(), formatted_feed_contents, download_timeout);
}
catch (const ScriptException& ex) {
qCriticalNN << LOGSEC_CORE
<< "Post-processing script for feed file failed:"
<< QUOTE_W_SPACE_DOT(ex.message());
qCriticalNN << LOGSEC_CORE << "Post-processing script for feed file failed:" << QUOTE_W_SPACE_DOT(ex.message());
throw FeedFetchException(Feed::Status::OtherError, ex.message());
}
@ -270,9 +262,8 @@ QList<Message> StandardServiceRoot::obtainNewMessages(Feed* feed,
QList<QAction*> StandardServiceRoot::getContextMenuForFeed(StandardFeed* feed) {
if (m_feedContextMenu.isEmpty()) {
// Initialize.
auto* action_metadata = new QAction(qApp->icons()->fromTheme(QSL("download"), QSL("emblem-downloads")),
tr("Fetch metadata"),
this);
auto* action_metadata =
new QAction(qApp->icons()->fromTheme(QSL("download"), QSL("emblem-downloads")), tr("Fetch metadata"), this);
m_feedContextMenu.append(action_metadata);
@ -297,6 +288,8 @@ bool StandardServiceRoot::mergeImportExportModel(FeedsImportExportModel* model,
new_parents.push(model->sourceModel()->rootItem());
bool some_feed_category_error = false;
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
// Iterate all new items we would like to merge into current model.
while (!new_parents.isEmpty()) {
RootItem* target_parent = original_parents.pop();
@ -318,8 +311,6 @@ bool StandardServiceRoot::mergeImportExportModel(FeedsImportExportModel* model,
// Add category to model.
new_category->clearChildren();
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
try {
DatabaseQueries::createOverwriteCategory(database,
new_category,
@ -350,9 +341,7 @@ bool StandardServiceRoot::mergeImportExportModel(FeedsImportExportModel* model,
else {
some_feed_category_error = true;
qCriticalNN << LOGSEC_CORE
<< "Cannot import category:"
<< QUOTE_W_SPACE_DOT(ex.message());
qCriticalNN << LOGSEC_CORE << "Cannot import category:" << QUOTE_W_SPACE_DOT(ex.message());
}
}
}
@ -360,7 +349,7 @@ bool StandardServiceRoot::mergeImportExportModel(FeedsImportExportModel* model,
auto* source_feed = qobject_cast<StandardFeed*>(source_item);
const auto* feed_with_same_url = target_root_node->getItemFromSubTree([source_feed](const RootItem* it) {
return it->kind() == RootItem::Kind::Feed &&
it->toFeed()->source().toLower() == source_feed->source().toLower();
it->toFeed()->source().toLower() == source_feed->source().toLower();
});
if (feed_with_same_url != nullptr) {
@ -368,7 +357,6 @@ bool StandardServiceRoot::mergeImportExportModel(FeedsImportExportModel* model,
}
auto* new_feed = new StandardFeed(*source_feed);
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
try {
DatabaseQueries::createOverwriteFeed(database,
@ -378,9 +366,7 @@ bool StandardServiceRoot::mergeImportExportModel(FeedsImportExportModel* model,
requestItemReassignment(new_feed, target_parent);
}
catch (const ApplicationException& ex) {
qCriticalNN << LOGSEC_CORE
<< "Cannot import feed:"
<< QUOTE_W_SPACE_DOT(ex.message());
qCriticalNN << LOGSEC_CORE << "Cannot import feed:" << QUOTE_W_SPACE_DOT(ex.message());
some_feed_category_error = true;
}
}
@ -402,10 +388,10 @@ void StandardServiceRoot::addNewCategory(RootItem* selected_item) {
// Lock was not obtained because
// it is used probably by feed updater or application
// is quitting.
qApp->showGuiMessage(Notification::Event::GeneralEvent, {
tr("Cannot add category"),
tr("Cannot add category because another critical operation is ongoing."),
QSystemTrayIcon::MessageIcon::Warning });
qApp->showGuiMessage(Notification::Event::GeneralEvent,
{tr("Cannot add category"),
tr("Cannot add category because another critical operation is ongoing."),
QSystemTrayIcon::MessageIcon::Warning});
// Thus, cannot delete and quit the method.
return;