diff --git a/plugins/FeedReader/FeedReader.pro b/plugins/FeedReader/FeedReader.pro index 769797a66..3974acbae 100644 --- a/plugins/FeedReader/FeedReader.pro +++ b/plugins/FeedReader/FeedReader.pro @@ -6,25 +6,34 @@ SOURCES = FeedReaderPlugin.cpp \ services/p3FeedReader.cc \ services/p3FeedReaderThread.cc \ services/rsFeedReaderItems.cc \ - services/util/CURLWrapper.cc \ gui/FeedReaderDialog.cpp \ gui/AddFeedDialog.cpp \ + gui/PreviewFeedDialog.cpp \ gui/FeedReaderNotify.cpp \ - gui/FeedReaderConfig.cpp + gui/FeedReaderConfig.cpp \ + gui/FeedReaderStringDefs.cpp \ + util/CURLWrapper.cpp \ + util/XMLWrapper.cpp \ + util/HTMLWrapper.cpp HEADERS = FeedReaderPlugin.h \ interface/rsFeedReader.h \ services/p3FeedReader.h \ services/p3FeedReaderThread.h \ services/rsFeedReaderItems.h \ - services/util/CURLWrapper.h \ gui/FeedReaderDialog.h \ gui/AddFeedDialog.h \ + gui/PreviewFeedDialog.h \ gui/FeedReaderNotify.h \ - gui/FeedReaderConfig.h + gui/FeedReaderConfig.h \ + gui/FeedReaderStringDefs.h \ + util/CURLWrapper.h \ + util/XMLWrapper.h \ + util/HTMLWrapper.h FORMS = gui/FeedReaderDialog.ui \ gui/AddFeedDialog.ui \ + gui/PreviewFeedDialog.ui \ gui/FeedReaderConfig.ui TARGET = FeedReader diff --git a/plugins/FeedReader/gui/AddFeedDialog.cpp b/plugins/FeedReader/gui/AddFeedDialog.cpp index ac0b6106d..f4f4064b1 100644 --- a/plugins/FeedReader/gui/AddFeedDialog.cpp +++ b/plugins/FeedReader/gui/AddFeedDialog.cpp @@ -25,6 +25,9 @@ #include "AddFeedDialog.h" #include "ui_AddFeedDialog.h" +#include "PreviewFeedDialog.h" +#include "FeedReaderStringDefs.h" + #include "retroshare/rsforums.h" bool sortForumInfo(const ForumInfo& info1, const ForumInfo& info2) @@ -32,8 +35,8 @@ bool sortForumInfo(const ForumInfo& info1, const ForumInfo& info2) return QString::fromStdWString(info1.forumName).compare(QString::fromStdWString(info2.forumName), Qt::CaseInsensitive); } -AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, QWidget *parent) - : QDialog(parent), mFeedReader(feedReader), ui(new Ui::AddFeedDialog) +AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent) + : QDialog(parent), mFeedReader(feedReader), mNotify(notify), ui(new Ui::AddFeedDialog) { ui->setupUi(this); @@ -45,6 +48,7 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, QWidget *parent) connect(ui->useStandardUpdateInterval, SIGNAL(toggled(bool)), this, SLOT(useStandardUpdateIntervalToggled())); connect(ui->useStandardProxyCheckBox, SIGNAL(toggled(bool)), this, SLOT(useStandardProxyToggled())); connect(ui->typeForumRadio, SIGNAL(toggled(bool)), this, SLOT(typeForumToggled())); + connect(ui->previewButton, SIGNAL(clicked()), this, SLOT(preview())); /* currently only for loacl feeds */ connect(ui->saveCompletePageCheckBox, SIGNAL(toggled(bool)), this, SLOT(denyForumToggled())); @@ -149,6 +153,9 @@ void AddFeedDialog::validate() if (ui->nameLineEdit->text().isEmpty() && !ui->useInfoFromFeedCheckBox->isChecked()) { ok = false; } + + ui->previewButton->setEnabled(ok); + if (!ui->typeLocalRadio->isChecked() && !ui->typeForumRadio->isChecked()) { ok = false; } @@ -156,38 +163,6 @@ void AddFeedDialog::validate() ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok); } -bool AddFeedDialog::showError(QWidget *parent, RsFeedAddResult result, const QString &title, const QString &text) -{ - QString error; - - switch (result) { - case RS_FEED_ADD_RESULT_SUCCESS: - /* no error */ - return false; - case RS_FEED_ADD_RESULT_FEED_NOT_FOUND: - error = tr("Feed not found."); - break; - case RS_FEED_ADD_RESULT_PARENT_NOT_FOUND: - error = tr("Parent not found."); - break; - case RS_FEED_ADD_RESULT_PARENT_IS_NO_FOLDER: - error = tr("Parent is no folder."); - break; - case RS_FEED_ADD_RESULT_FEED_IS_FOLDER: - error = tr("Feed is a folder."); - break; - case RS_FEED_ADD_RESULT_FEED_IS_NO_FOLDER: - error = tr("Feed is no folder."); - break; - default: - error = tr("Unknown error occured."); - } - - QMessageBox::critical(parent, title, text + "\n" + error); - - return true; -} - void AddFeedDialog::setParent(const std::string &parentId) { mParentId = parentId; @@ -264,16 +239,8 @@ bool AddFeedDialog::fillFeed(const std::string &feedId) return true; } -void AddFeedDialog::createFeed() +void AddFeedDialog::getFeedInfo(FeedInfo &feedInfo) { - FeedInfo feedInfo; - if (!mFeedId.empty()) { - if (!mFeedReader->getFeedInfo(mFeedId, feedInfo)) { - QMessageBox::critical(this, tr("Edit feed"), tr("Can't edit feed. Feed does not exist.")); - return; - } - } - feedInfo.parentId = mParentId; feedInfo.name = ui->nameLineEdit->text().toUtf8().constData(); @@ -307,19 +274,41 @@ void AddFeedDialog::createFeed() feedInfo.flag.standardStorageTime = ui->useStandardStorageTimeCheckBox->isChecked(); feedInfo.storageTime = ui->storageTimeSpinBox->value() * 60 *60 * 24; +} + +void AddFeedDialog::createFeed() +{ + FeedInfo feedInfo; + if (!mFeedId.empty()) { + if (!mFeedReader->getFeedInfo(mFeedId, feedInfo)) { + QMessageBox::critical(this, tr("Edit feed"), tr("Can't edit feed. Feed does not exist.")); + return; + } + } + + getFeedInfo(feedInfo); if (mFeedId.empty()) { /* add new feed */ RsFeedAddResult result = mFeedReader->addFeed(feedInfo, mFeedId); - if (showError(this, result, tr("Create feed"), tr("Cannot create feed."))) { + if (FeedReaderStringDefs::showError(this, result, tr("Create feed"), tr("Cannot create feed."))) { return; } } else { RsFeedAddResult result = mFeedReader->setFeed(mFeedId, feedInfo); - if (showError(this, result, tr("Edit feed"), tr("Cannot change feed."))) { + if (FeedReaderStringDefs::showError(this, result, tr("Edit feed"), tr("Cannot change feed."))) { return; } } close(); } + +void AddFeedDialog::preview() +{ + FeedInfo feedInfo; + getFeedInfo(feedInfo); + + PreviewFeedDialog dialog(mFeedReader, mNotify, feedInfo, this); + dialog.exec(); +} diff --git a/plugins/FeedReader/gui/AddFeedDialog.h b/plugins/FeedReader/gui/AddFeedDialog.h index f08d64611..92841c93b 100644 --- a/plugins/FeedReader/gui/AddFeedDialog.h +++ b/plugins/FeedReader/gui/AddFeedDialog.h @@ -30,17 +30,16 @@ class AddFeedDialog; } class RsFeedReader; +class FeedReaderNotify; class AddFeedDialog : public QDialog { Q_OBJECT public: - AddFeedDialog(RsFeedReader *feedReader, QWidget *parent); + AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent); ~AddFeedDialog(); - static bool showError(QWidget *parent, RsFeedAddResult result, const QString &title, const QString &text); - void setParent(const std::string &parentId); bool fillFeed(const std::string &feedId); @@ -53,9 +52,13 @@ private slots: void denyForumToggled(); void validate(); void createFeed(); + void preview(); private: + void getFeedInfo(FeedInfo &feedInfo); + RsFeedReader *mFeedReader; + FeedReaderNotify *mNotify; std::string mFeedId; std::string mParentId; diff --git a/plugins/FeedReader/gui/AddFeedDialog.ui b/plugins/FeedReader/gui/AddFeedDialog.ui index b09be53a8..a6a4cea84 100644 --- a/plugins/FeedReader/gui/AddFeedDialog.ui +++ b/plugins/FeedReader/gui/AddFeedDialog.ui @@ -278,6 +278,13 @@ p, li { white-space: pre-wrap; } + + + + Preview + + + diff --git a/plugins/FeedReader/gui/FeedReaderDialog.cpp b/plugins/FeedReader/gui/FeedReaderDialog.cpp index ef4438e1a..7d72a6b23 100644 --- a/plugins/FeedReader/gui/FeedReaderDialog.cpp +++ b/plugins/FeedReader/gui/FeedReaderDialog.cpp @@ -33,9 +33,11 @@ #include "ui_FeedReaderDialog.h" #include "FeedReaderNotify.h" #include "AddFeedDialog.h" +#include "FeedReaderStringDefs.h" #include "gui/common/RSTreeWidgetItem.h" #include "util/HandleRichText.h" #include "gui/settings/rsharesettings.h" + #include "interface/rsFeedReader.h" #include "retroshare/rsiface.h" @@ -513,7 +515,6 @@ void FeedReaderDialog::calculateFeedItems() void FeedReaderDialog::updateFeedItem(QTreeWidgetItem *item, FeedInfo &info) { - QString workState; QIcon icon; if (info.flag.folder) { @@ -532,26 +533,9 @@ void FeedReaderDialog::updateFeedItem(QTreeWidgetItem *item, FeedInfo &info) item->setData(COLUMN_FEED_DATA, ROLE_FEED_ICON, icon); - switch (info.workstate) { - case FeedInfo::WAITING: - break; - case FeedInfo::WAITING_TO_DOWNLOAD: - workState = tr("waiting for download"); - break; - case FeedInfo::DOWNLOADING: - workState = tr("downloading"); - break; - case FeedInfo::WAITING_TO_PROCESS: - workState = tr("waiting for process"); - break; - case FeedInfo::PROCESSING: - workState = tr("processing"); - break; - } - QString name = QString::fromUtf8(info.name.c_str()); item->setData(COLUMN_FEED_DATA, ROLE_FEED_NAME, name.isEmpty() ? tr("No name") : name); - item->setData(COLUMN_FEED_DATA, ROLE_FEED_WORKSTATE, workState); + item->setData(COLUMN_FEED_DATA, ROLE_FEED_WORKSTATE, FeedReaderStringDefs::workState(info.workstate)); uint32_t unreadCount; mFeedReader->getMessageCount(info.feedId, NULL, NULL, &unreadCount); @@ -562,8 +546,8 @@ void FeedReaderDialog::updateFeedItem(QTreeWidgetItem *item, FeedInfo &info) item->setData(COLUMN_FEED_DATA, ROLE_FEED_ID, QString::fromStdString(info.feedId)); item->setData(COLUMN_FEED_DATA, ROLE_FEED_FOLDER, info.flag.folder); item->setData(COLUMN_FEED_DATA, ROLE_FEED_DEACTIVATED, info.flag.deactivated); - item->setData(COLUMN_FEED_DATA, ROLE_FEED_ERROR, info.error); - item->setToolTip(COLUMN_FEED_NAME, info.error ? QString::fromUtf8(info.errorString.c_str()) : ""); + item->setData(COLUMN_FEED_DATA, ROLE_FEED_ERROR, (bool) (info.errorState != RS_FEED_ERRORSTATE_OK)); + item->setToolTip(COLUMN_FEED_NAME, (info.errorState != RS_FEED_ERRORSTATE_OK) ? FeedReaderStringDefs::errorString(info) : ""); } void FeedReaderDialog::updateMsgs(const std::string &feedId) @@ -689,6 +673,10 @@ void FeedReaderDialog::feedChanged(const QString &feedId, int type) if (!mFeedReader->getFeedInfo(feedId.toStdString(), feedInfo)) { return; } + + if (feedInfo.flag.preview) { + return; + } } if (type == NOTIFY_TYPE_MOD || type == NOTIFY_TYPE_DEL) { @@ -950,13 +938,13 @@ void FeedReaderDialog::newFolder() if (dialog.exec() == QDialog::Accepted && !dialog.textValue().isEmpty()) { std::string feedId; RsFeedAddResult result = mFeedReader->addFolder(currentFeedId(), dialog.textValue().toUtf8().constData(), feedId); - AddFeedDialog::showError(this, result, tr("Create folder"), tr("Cannot create folder.")); + FeedReaderStringDefs::showError(this, result, tr("Create folder"), tr("Cannot create folder.")); } } void FeedReaderDialog::newFeed() { - AddFeedDialog dialog(mFeedReader, this); + AddFeedDialog dialog(mFeedReader, mNotify, this); dialog.setParent(currentFeedId()); dialog.exec(); } @@ -1003,10 +991,10 @@ void FeedReaderDialog::editFeed() if (dialog.exec() == QDialog::Accepted && !dialog.textValue().isEmpty()) { RsFeedAddResult result = mFeedReader->setFolder(feedId, dialog.textValue().toUtf8().constData()); - AddFeedDialog::showError(this, result, tr("Create folder"), tr("Cannot create folder.")); + FeedReaderStringDefs::showError(this, result, tr("Create folder"), tr("Cannot create folder.")); } } else { - AddFeedDialog dialog(mFeedReader, this); + AddFeedDialog dialog(mFeedReader, mNotify, this); if (!dialog.fillFeed(feedId)) { return; } diff --git a/plugins/FeedReader/gui/FeedReaderDialog.h b/plugins/FeedReader/gui/FeedReaderDialog.h index 7eae19f3e..e60f218cb 100644 --- a/plugins/FeedReader/gui/FeedReaderDialog.h +++ b/plugins/FeedReader/gui/FeedReaderDialog.h @@ -47,9 +47,9 @@ protected: bool eventFilter(QObject *obj, QEvent *ev); private slots: - void feedTreeCustomPopupMenu(QPoint point); - void msgTreeCustomPopupMenu(QPoint point); - void feedItemChanged(QTreeWidgetItem *item); + void feedTreeCustomPopupMenu(QPoint point); + void msgTreeCustomPopupMenu(QPoint point); + void feedItemChanged(QTreeWidgetItem *item); void msgItemChanged(); void msgItemClicked(QTreeWidgetItem *item, int column); void filterColumnChanged(); diff --git a/plugins/FeedReader/gui/FeedReaderStringDefs.cpp b/plugins/FeedReader/gui/FeedReaderStringDefs.cpp new file mode 100644 index 000000000..37dc1dc78 --- /dev/null +++ b/plugins/FeedReader/gui/FeedReaderStringDefs.cpp @@ -0,0 +1,130 @@ +/**************************************************************** + * RetroShare GUI is distributed under the following license: + * + * Copyright (C) 2012 by Thunder + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#include +#include + +#include "FeedReaderStringDefs.h" + +bool FeedReaderStringDefs::showError(QWidget *parent, RsFeedAddResult result, const QString &title, const QString &text) +{ + QString error; + + switch (result) { + case RS_FEED_ADD_RESULT_SUCCESS: + /* no error */ + return false; + case RS_FEED_ADD_RESULT_FEED_NOT_FOUND: + error = QApplication::translate("FeedReaderStringDefs", "Feed not found."); + break; + case RS_FEED_ADD_RESULT_PARENT_NOT_FOUND: + error = QApplication::translate("FeedReaderStringDefs", "Parent not found."); + break; + case RS_FEED_ADD_RESULT_PARENT_IS_NO_FOLDER: + error = QApplication::translate("FeedReaderStringDefs", "Parent is no folder."); + break; + case RS_FEED_ADD_RESULT_FEED_IS_FOLDER: + error = QApplication::translate("FeedReaderStringDefs", "Feed is a folder."); + break; + case RS_FEED_ADD_RESULT_FEED_IS_NO_FOLDER: + error = QApplication::translate("FeedReaderStringDefs", "Feed is no folder."); + break; + default: + error = QApplication::translate("FeedReaderStringDefs", "Unknown error occured."); + } + + QMessageBox::critical(parent, title, text + "\n" + error); + + return true; +} + +QString FeedReaderStringDefs::workState(FeedInfo::WorkState state) +{ + switch (state) { + case FeedInfo::WAITING: + return ""; + case FeedInfo::WAITING_TO_DOWNLOAD: + return QApplication::translate("FeedReaderStringDefs", "Waiting for download"); + case FeedInfo::DOWNLOADING: + return QApplication::translate("FeedReaderStringDefs", "Downloading"); + case FeedInfo::WAITING_TO_PROCESS: + return QApplication::translate("FeedReaderStringDefs", "Waiting for process"); + case FeedInfo::PROCESSING: + return QApplication::translate("FeedReaderStringDefs", "Processing"); + } + + return ""; +} + +QString FeedReaderStringDefs::errorString(const FeedInfo &feedInfo) +{ + QString errorState; + switch (feedInfo.errorState) { + case RS_FEED_ERRORSTATE_OK: + break; + + /* download */ + case RS_FEED_ERRORSTATE_DOWNLOAD_INTERNAL_ERROR: + errorState = QApplication::translate("FeedReaderStringDefs", "Internal download error"); + break; + case RS_FEED_ERRORSTATE_DOWNLOAD_ERROR: + errorState = QApplication::translate("FeedReaderStringDefs", "Download error"); + break; + case RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE: + errorState = QApplication::translate("FeedReaderStringDefs", "Unknown content type"); + break; + case RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND: + errorState = QApplication::translate("FeedReaderStringDefs", "Download not found"); + break; + case RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE: + errorState = QApplication::translate("FeedReaderStringDefs", "Unknown response code"); + break; + + /* process */ + case RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR: + errorState = QApplication::translate("FeedReaderStringDefs", "Internal process error"); + break; + case RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT: + errorState = QApplication::translate("FeedReaderStringDefs", "Unknown XML format"); + break; + case RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE: + errorState = QApplication::translate("FeedReaderStringDefs", "Can't create forum"); + break; + case RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_FOUND: + errorState = QApplication::translate("FeedReaderStringDefs", "Forum not found"); + break; + case RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_ADMIN: + errorState = QApplication::translate("FeedReaderStringDefs", "You are not admin of the forum"); + break; + case RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_ANONYMOUS: + errorState = QApplication::translate("FeedReaderStringDefs", "The forum is no anonymous forum"); + break; + + default: + errorState = QApplication::translate("FeedReaderStringDefs", "Unknown error"); + } + + if (!feedInfo.errorString.empty()) { + errorState += QString(" (%1)").arg(QString::fromUtf8(feedInfo.errorString.c_str())); + } + + return errorState; +} diff --git a/plugins/FeedReader/gui/FeedReaderStringDefs.h b/plugins/FeedReader/gui/FeedReaderStringDefs.h new file mode 100644 index 000000000..d94103cfc --- /dev/null +++ b/plugins/FeedReader/gui/FeedReaderStringDefs.h @@ -0,0 +1,39 @@ +/**************************************************************** + * RetroShare GUI is distributed under the following license: + * + * Copyright (C) 2012 by Thunder + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#ifndef FEEDREADER_STRINGDEFS_H +#define FEEDREADER_STRINGDEFS_H + +#include + +#include "interface/rsFeedReader.h" + +class QWidget; + +class FeedReaderStringDefs +{ +public: + static bool showError(QWidget *parent, RsFeedAddResult result, const QString &title, const QString &text); + static QString workState(FeedInfo::WorkState state); + static QString errorString(const FeedInfo &feedInfo); +}; + +#endif diff --git a/plugins/FeedReader/gui/PreviewFeedDialog.cpp b/plugins/FeedReader/gui/PreviewFeedDialog.cpp new file mode 100644 index 000000000..76bd2c7da --- /dev/null +++ b/plugins/FeedReader/gui/PreviewFeedDialog.cpp @@ -0,0 +1,427 @@ +/**************************************************************** + * RetroShare GUI is distributed under the following license: + * + * Copyright (C) 2012 by Thunder + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#include + +#include "PreviewFeedDialog.h" +#include "ui_PreviewFeedDialog.h" +#include "FeedReaderNotify.h" +#include "FeedReaderStringDefs.h" +#include "util/HandleRichText.h" + +#include "interface/rsFeedReader.h" +#include "retroshare/rsiface.h" +#include "util/HTMLWrapper.h" + +#include + +// not yet functional +//PreviewItemDelegate::PreviewItemDelegate(QTreeWidget *parent) : QItemDelegate(parent) +//{ +// connect(parent->header(), SIGNAL(sectionResized(int,int,int)), SLOT(sectionResized(int,int,int))); +//} + +//void PreviewItemDelegate::sectionResized(int logicalIndex, int /*oldSize*/, int /*newSize*/) +//{ +// QHeaderView *header = dynamic_cast(sender()); +// if (header) { +// QTreeWidget *treeWidget = dynamic_cast(header->parent()); +// if (treeWidget) { +// QModelIndex index = treeWidget->model()->index(0, logicalIndex, QModelIndex()); +// emit sizeHintChanged(index); +// } +// } +//} + +//void PreviewItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QString &text) const +//{ +// QPen pen(painter->pen()); +// QFont font(painter->font()); +// QPalette::ColorGroup colorgroup(option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled); + +// QTextOption textOption; +// textOption.setWrapMode(QTextOption::WrapAnywhere); +// textOption.setAlignment(option.displayAlignment); + +// QRect textRect = rect.adjusted(5, 0, -5, 0); // remove width padding +// textRect.setTop(qMin(rect.top(), option.rect.top())); +// textRect.setHeight(qMax(rect.height(), option.rect.height())); + +// if (option.state & QStyle::State_Selected) { +// painter->fillRect(rect, option.palette.brush(colorgroup, QPalette::Highlight)); +// painter->setPen(option.palette.color(colorgroup, QPalette::HighlightedText)); +// } else { +// painter->setPen(option.palette.color(colorgroup, QPalette::Text)); +// } + +// if (option.state & QStyle::State_Editing) { +// painter->save(); +// painter->setPen(option.palette.color(colorgroup, QPalette::Text)); +// painter->drawRect(rect.adjusted( 0, 0, -1, -1)); +// painter->restore(); +// } + +// painter->setFont(option.font); +// painter->drawText(textRect, text, textOption); + +// // reset painter +// painter->setFont(font); +// painter->setPen(pen); +//} + +//void PreviewItemDelegate::drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const +//{ +// if (option.state & QStyle::State_HasFocus) { +// QRect textRect(rect); +// textRect.setTop(qMin(rect.top(), option.rect.top())); +// textRect.setHeight(qMax(rect.height(), option.rect.height())); + +// QStyleOptionFocusRect optionFocusRect; +// optionFocusRect.QStyleOption::operator=(option); +// optionFocusRect.rect = textRect; +// optionFocusRect.state |= QStyle::State_KeyboardFocusChange; +// QPalette::ColorGroup colorgroup = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; +// optionFocusRect.backgroundColor = option.palette.color(colorgroup, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Background); +// QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &optionFocusRect, painter); +// } +//} + +//QSize PreviewItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +//{ +//// static bool inSizeHint = false; + +//// if (inSizeHint) { +//// return QSize(); +//// } +//// inSizeHint = true; + +// //d->viewport->width() +// QSize size = QItemDelegate::sizeHint(option, index); +// size.setHeight(50); + +//// QTreeWidget *treeWidget = dynamic_cast(parent()); +//// if (treeWidget) { +//// size.setWidth(treeWidget->header()->sectionSize(index.column())); + +//// QString text = index.data(Qt::DisplayRole).toString(); +//// QRect displayRect = textRectangle(NULL, QRect(0, 0, size.width(), size.height()), option.font, text); +//// QRect displayRect = treeWidget->visualRect(index); +//// int width = treeWidget->columnWidth(index.column()); +//// int height = option.fontMetrics.boundingRect(QRect(0, 0, size.width(), 0), Qt::TextWrapAnywhere | Qt::TextLongestVariant, text).height(); + +//// if (height > size.height()) { +//// size.setHeight(height); +//// } +//// } + +//// inSizeHint = false; + +// return size; +//} + +PreviewFeedDialog::PreviewFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, const FeedInfo &feedInfo, QWidget *parent) : + QDialog(parent), mFeedReader(feedReader), mNotify(notify), ui(new Ui::PreviewFeedDialog) +{ + ui->setupUi(this); + + ui->feedNameLabel->clear(); + ui->feedInfoLabel->clear(); + + /* connect signals */ + connect(ui->previousPushButton, SIGNAL(clicked()), this, SLOT(previousMsg())); + connect(ui->nextPushButton, SIGNAL(clicked()), this, SLOT(nextMsg())); + connect(ui->documentButton, SIGNAL(toggled(bool)), this, SLOT(showDocumentFrame(bool))); + + connect(mNotify, SIGNAL(notifyFeedChanged(QString,int)), this, SLOT(feedChanged(QString,int))); + connect(mNotify, SIGNAL(notifyMsgChanged(QString,QString,int)), this, SLOT(msgChanged(QString,QString,int))); + +// ui->documentTreeWidget->setItemDelegate(new PreviewItemDelegate(ui->documentTreeWidget)); + showDocumentFrame(false); + + if (!mFeedReader->addPreviewFeed(feedInfo, mFeedId)) { + setInfo(tr("Cannot create preview")); + } + + updateMsgCount(); +} + +PreviewFeedDialog::~PreviewFeedDialog() +{ + disconnect(mNotify); + disconnect(mNotify); + + if (!mFeedId.empty()) { + mFeedReader->removeFeed(mFeedId); + } + + delete ui; +} + +void PreviewFeedDialog::feedChanged(const QString &feedId, int type) +{ + if (feedId.isEmpty()) { + return; + } + + if (feedId.toStdString() != mFeedId) { + return; + } + + if (type == NOTIFY_TYPE_DEL) { + /* feed deleted */ + mFeedId.clear(); + return; + } + + if (type == NOTIFY_TYPE_ADD || type == NOTIFY_TYPE_MOD) { + FeedInfo feedInfo; + if (!mFeedReader->getFeedInfo(mFeedId, feedInfo)) { + return; + } + fillFeedInfo(feedInfo); + } +} + +void PreviewFeedDialog::msgChanged(const QString &feedId, const QString &msgId, int type) +{ + if (feedId.isEmpty() || msgId.isEmpty()) { + return; + } + + if (feedId.toStdString() != mFeedId) { + return; + } + + switch (type) { + case NOTIFY_TYPE_ADD: + if (mMsgId.empty()) { + mMsgId = msgId.toStdString(); + updateMsg(); + } + break; + case NOTIFY_TYPE_MOD: + if (mMsgId == msgId.toStdString()) { + updateMsg(); + } + break; + case NOTIFY_TYPE_DEL: + if (mMsgId == msgId.toStdString()) { + std::list::iterator it = std::find(mMsgIds.begin(), mMsgIds.end(), mMsgId); + if (it != mMsgIds.end()) { + ++it; + if (it != mMsgIds.end()) { + mMsgId = *it; + } else { + --it; + if (it != mMsgIds.begin()) { + --it; + mMsgId = *it; + } else { + mMsgId.clear(); + } + } + updateMsg(); + } + } + break; + } + + /* calculate message count */ + mMsgIds.clear(); + mFeedReader->getFeedMsgIdList(mFeedId, mMsgIds); + + updateMsgCount(); +} + +void PreviewFeedDialog::showDocumentFrame(bool show) +{ + ui->documentFrame->setVisible(show); + ui->documentButton->setChecked(show); + + if (show) { + ui->documentButton->setToolTip(tr("Hide tree")); + ui->documentButton->setIcon(QIcon(":images/hide_toolbox_frame.png")); + + fillDocumentTree(); + } else { + ui->documentButton->setToolTip(tr("Show tree")); + ui->documentButton->setIcon(QIcon(":images/show_toolbox_frame.png")); + } +} + +int PreviewFeedDialog::getMsgPos() +{ + int pos = -1; + std::list::iterator it; + for (it = mMsgIds.begin(); it != mMsgIds.end(); ++it) { + ++pos; + if (*it == mMsgId) { + break; + } + } + + return pos; +} + +void PreviewFeedDialog::setInfo(const QString &info) +{ + ui->feedInfoLabel->setText(info); + + ui->infoLabel->setVisible(!info.isEmpty()); + ui->feedInfoLabel->setVisible(!info.isEmpty()); +} + +void PreviewFeedDialog::fillFeedInfo(const FeedInfo &feedInfo) +{ + QString name = feedInfo.name.empty() ? tr("No name") : QString::fromUtf8(feedInfo.name.c_str()); + + QString workState = FeedReaderStringDefs::workState(feedInfo.workstate); + if (!workState.isEmpty()) { + name += QString(" (%1)").arg(workState); + } + ui->feedNameLabel->setText(name); + + setInfo(FeedReaderStringDefs::errorString(feedInfo)); +} + +void PreviewFeedDialog::previousMsg() +{ + std::list::iterator it = std::find(mMsgIds.begin(), mMsgIds.end(), mMsgId); + if (it != mMsgIds.end()) { + if (it != mMsgIds.begin()) { + --it; + mMsgId = *it; + updateMsg(); + updateMsgCount(); + } + } +} + +void PreviewFeedDialog::nextMsg() +{ + std::list::iterator it = std::find(mMsgIds.begin(), mMsgIds.end(), mMsgId); + if (it != mMsgIds.end()) { + ++it; + if (it != mMsgIds.end()) { + mMsgId = *it; + updateMsg(); + updateMsgCount(); + } + } +} + +void PreviewFeedDialog::updateMsgCount() +{ + int pos = getMsgPos(); + ui->messageCountLabel->setText(QString("%1/%2").arg(pos + 1).arg(mMsgIds.size())); + + ui->previousPushButton->setEnabled(pos > 0); + ui->nextPushButton->setEnabled(pos + 1 < (int) mMsgIds.size()); +} + +void PreviewFeedDialog::updateMsg() +{ + FeedMsgInfo msgInfo; + if (mMsgId.empty() || !mFeedReader->getMsgInfo(mFeedId, mMsgId, msgInfo)) { + ui->msgTitle->clear(); + ui->msgText->clear(); + mDescription.clear(); + return; + } + + mDescription = msgInfo.description; + QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8(mDescription.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS); + + ui->msgText->setHtml(msgTxt); + ui->msgTitle->setText(QString::fromUtf8(msgInfo.title.c_str())); + + ui->documentTreeWidget->clear(); + fillDocumentTree(); +} + +static void examineChildElements(QTreeWidget *treeWidget, HTMLWrapper &html, xmlNodePtr node, QTreeWidgetItem *parentItem) +{ + QTreeWidgetItem *item = new QTreeWidgetItem; + QString text; + if (node->type == XML_ELEMENT_NODE) { + text = QString("<%1 ").arg(QString::fromUtf8(html.nodeName(node).c_str())); + + for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) { + QString value = QString::fromUtf8(html.getAttr(node, attr).c_str()); + if (value.length() > 100) { + value = value.left(100) + "..."; + } + text += QString("%1=\"%2\" ").arg(QString::fromUtf8(html.attrName(attr).c_str()), value); + } + text = text.trimmed() + ">"; + } else { + std::string content; + if (html.getContent(node, content)) { + text = QString::fromUtf8(content.c_str()); + } else { + text = QApplication::translate("PreviewFeedDialog", "Error getting content"); + } + } + item->setText(0, text); + parentItem->addChild(item); + +// QLabel *label = new QLabel(text); +// label->setTextFormat(Qt::PlainText); +// label->setWordWrap(true); +// treeWidget->setItemWidget(item, 0, label); + + item->setExpanded(true); + + for (xmlNodePtr child = node->children; child; child = child->next) { + examineChildElements(treeWidget, html, child, item); + } +} + +void PreviewFeedDialog::fillDocumentTree() +{ + if (!ui->documentTreeWidget->isVisible()) { + return; + } + + if (ui->documentTreeWidget->topLevelItemCount() > 0) { + return; + } + + if (mDescription.empty()) { + return; + } + + HTMLWrapper html; + if (!html.readHTML(mDescription.c_str(), "")) { + QTreeWidgetItem *item = new QTreeWidgetItem; + item->setText(0, tr("Error parsing document")); + ui->documentTreeWidget->addTopLevelItem(item); + + return; + } + + xmlNodePtr root = html.getRootElement(); + if (!root) { + return; + } + + examineChildElements(ui->documentTreeWidget, html, root, ui->documentTreeWidget->invisibleRootItem()); +} diff --git a/plugins/FeedReader/gui/PreviewFeedDialog.h b/plugins/FeedReader/gui/PreviewFeedDialog.h new file mode 100644 index 000000000..a801577bc --- /dev/null +++ b/plugins/FeedReader/gui/PreviewFeedDialog.h @@ -0,0 +1,89 @@ +/**************************************************************** + * RetroShare GUI is distributed under the following license: + * + * Copyright (C) 2012 by Thunder + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#ifndef PREVIEWFEEDDIALOG_H +#define PREVIEWFEEDDIALOG_H + +#include +#include + +namespace Ui { +class PreviewFeedDialog; +} + +class QTreeWidget; +class RsFeedReader; +class FeedReaderNotify; +class FeedInfo; + +// not yet functional +//class PreviewItemDelegate : public QItemDelegate +//{ +// Q_OBJECT + +//public: +// PreviewItemDelegate(QTreeWidget *parent); + +//private slots: +// void sectionResized(int logicalIndex, int oldSize, int newSize); + +//protected: +// virtual void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QString &text) const; +// virtual void drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const; +// virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; +//}; + +class PreviewFeedDialog : public QDialog +{ + Q_OBJECT + +public: + PreviewFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, const FeedInfo &feedInfo, QWidget *parent = 0); + ~PreviewFeedDialog(); + +private slots: + void previousMsg(); + void nextMsg(); + void showDocumentFrame(bool show); + + /* FeedReaderNotify */ + void feedChanged(const QString &feedId, int type); + void msgChanged(const QString &feedId, const QString &msgId, int type); + +private: + int getMsgPos(); + void setInfo(const QString &info); + void fillFeedInfo(const FeedInfo &feedInfo); + void updateMsgCount(); + void updateMsg(); + void fillDocumentTree(); + + RsFeedReader *mFeedReader; + FeedReaderNotify *mNotify; + std::string mFeedId; + std::string mMsgId; + std::list mMsgIds; + std::string mDescription; + + Ui::PreviewFeedDialog *ui; +}; + +#endif // PREVIEWFEEDDIALOG_H diff --git a/plugins/FeedReader/gui/PreviewFeedDialog.ui b/plugins/FeedReader/gui/PreviewFeedDialog.ui new file mode 100644 index 000000000..9b5d92570 --- /dev/null +++ b/plugins/FeedReader/gui/PreviewFeedDialog.ui @@ -0,0 +1,314 @@ + + + PreviewFeedDialog + + + + 0 + 0 + 500 + 350 + + + + Preview + + + true + + + + 0 + + + + + + + + 0 + 0 + + + + Name: + + + + + + + Feed name + + + true + + + + + + + Information: + + + + + + + Information + + + true + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Previous + + + + + + + + 0 + 0 + + + + 0/0 + + + + + + + + 0 + 0 + + + + Next + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 0 + + + + Titel: + + + + + + + + 0 + 24 + + + + QLabel#msgTitle{ +border: 2px solid #CCCCCC; +border-radius: 6px; +background: white;} + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + 0 + + + + + + + + 14 + 31 + + + + + 14 + 31 + + + + + + + + 16 + 31 + + + + true + + + + + + + Qt::Vertical + + + + 12 + 329 + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + + + true + + + false + + + + 1 + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + PreviewFeedDialog + accept() + + + 258 + 339 + + + 157 + 274 + + + + + buttonBox + rejected() + PreviewFeedDialog + reject() + + + 326 + 339 + + + 286 + 274 + + + + + diff --git a/plugins/FeedReader/interface/rsFeedReader.h b/plugins/FeedReader/interface/rsFeedReader.h index 42be3e148..4ea33cac4 100644 --- a/plugins/FeedReader/interface/rsFeedReader.h +++ b/plugins/FeedReader/interface/rsFeedReader.h @@ -26,6 +26,26 @@ #include #include +enum RsFeedReaderErrorState { + RS_FEED_ERRORSTATE_OK = 0, + + /* download */ + RS_FEED_ERRORSTATE_DOWNLOAD_INTERNAL_ERROR = 1, + RS_FEED_ERRORSTATE_DOWNLOAD_ERROR = 2, + RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE = 3, + RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND = 4, + RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE = 5, + + /* process */ + RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR = 50, + RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT = 51, + RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE = 100, + RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_FOUND = 101, + RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_ADMIN = 102, + RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_ANONYMOUS = 103 +}; + + class RsFeedReader; extern RsFeedReader *rsFeedReader; @@ -58,7 +78,7 @@ public: updateInterval = 0; lastUpdate = 0; storageTime = 0; - error = false; + errorState = RS_FEED_ERRORSTATE_OK; flag.folder = false; flag.infoFromFeed = false; flag.standardStorageTime = false; @@ -70,25 +90,26 @@ public: flag.updateForumInfo = false; flag.embedImages = false; flag.saveCompletePage = false; + flag.preview = false; } - std::string feedId; - std::string parentId; - std::string url; - std::string name; - std::string description; - std::string icon; - std::string user; - std::string password; - std::string proxyAddress; - uint16_t proxyPort; - uint32_t updateInterval; - time_t lastUpdate; - uint32_t storageTime; - std::string forumId; - WorkState workstate; - bool error; - std::string errorString; + std::string feedId; + std::string parentId; + std::string url; + std::string name; + std::string description; + std::string icon; + std::string user; + std::string password; + std::string proxyAddress; + uint16_t proxyPort; + uint32_t updateInterval; + time_t lastUpdate; + uint32_t storageTime; + std::string forumId; + WorkState workstate; + RsFeedReaderErrorState errorState; + std::string errorString; struct { bool folder : 1; @@ -102,6 +123,7 @@ public: bool updateForumInfo : 1; bool embedImages : 1; bool saveCompletePage : 1; + bool preview : 1; } flag; }; @@ -159,6 +181,7 @@ public: virtual RsFeedAddResult addFeed(const FeedInfo &feedInfo, std::string &feedId) = 0; virtual RsFeedAddResult setFeed(const std::string &feedId, const FeedInfo &feedInfo) = 0; virtual bool removeFeed(const std::string &feedId) = 0; + virtual bool addPreviewFeed(const FeedInfo &feedInfo, std::string &feedId) = 0; virtual void getFeedList(const std::string &parentId, std::list &feedInfos) = 0; virtual bool getFeedInfo(const std::string &feedId, FeedInfo &feedInfo) = 0; virtual bool getMsgInfo(const std::string &feedId, const std::string &msgId, FeedMsgInfo &msgInfo) = 0; @@ -166,20 +189,9 @@ public: virtual bool removeMsgs(const std::string &feedId, const std::list &msgIds) = 0; virtual bool getMessageCount(const std::string &feedId, uint32_t *msgCount, uint32_t *newCount, uint32_t *unreadCount) = 0; virtual bool getFeedMsgList(const std::string &feedId, std::list &msgInfos) = 0; + virtual bool getFeedMsgIdList(const std::string &feedId, std::list &msgIds) = 0; virtual bool processFeed(const std::string &feedId) = 0; virtual bool setMessageRead(const std::string &feedId, const std::string &msgId, bool read) = 0; - - /* get Ids */ -// virtual uint32_t getRankingsCount() = 0; -// virtual float getMaxRank() = 0; -// virtual bool getRankings(uint32_t first, uint32_t count, std::list &rids) = 0; -// virtual bool getRankDetails(std::string rid, RsRankDetails &details) = 0; - - /* Add New Comment / Msg */ -// virtual std::string newRankMsg(std::wstring link, std::wstring title, std::wstring comment, int32_t score) = 0; -// virtual bool updateComment(std::string rid, std::wstring comment, int32_t score) = 0; - -// virtual std::string anonRankMsg(std::string rid, std::wstring link, std::wstring title) = 0; }; #endif diff --git a/plugins/FeedReader/services/p3FeedReader.cc b/plugins/FeedReader/services/p3FeedReader.cc index 76b16ea65..6aaad1003 100644 --- a/plugins/FeedReader/services/p3FeedReader.cc +++ b/plugins/FeedReader/services/p3FeedReader.cc @@ -38,10 +38,12 @@ RsFeedReader *rsFeedReader = NULL; p3FeedReader::p3FeedReader(RsPluginHandler* pgHandler) : RsPQIService(RS_PKT_TYPE_FEEDREADER_CONFIG, CONFIG_TYPE_FEEDREADER, 5, pgHandler), - mFeedReaderMtx("p3FeedReader"), mDownloadMutex("p3FeedReaderDownload"), mProcessMutex("p3FeedReaderProcess") + mFeedReaderMtx("p3FeedReader"), mDownloadMutex("p3FeedReaderDownload"), mProcessMutex("p3FeedReaderProcess"), mPreviewMutex("p3FeedReaderPreview") { mNextFeedId = 1; mNextMsgId = 1; + mNextPreviewFeedId = -1; // use negative values + mNextPreviewMsgId = -1; // use negative values mStandardUpdateInterval = 60 * 60; // 60 minutes mStandardStorageTime = 30 * 60 * 60 * 24; // 30 days mStandardUseProxy = false; @@ -49,13 +51,16 @@ p3FeedReader::p3FeedReader(RsPluginHandler* pgHandler) mLastClean = 0; mNotify = NULL; + mPreviewDownloadThread = NULL; + mPreviewProcessThread = NULL; + /* start download thread */ - p3FeedReaderThread *frt = new p3FeedReaderThread(this, p3FeedReaderThread::DOWNLOAD); + p3FeedReaderThread *frt = new p3FeedReaderThread(this, p3FeedReaderThread::DOWNLOAD, ""); mThreads.push_back(frt); frt->start(); /* start process thread */ - frt = new p3FeedReaderThread(this, p3FeedReaderThread::PROCESS); + frt = new p3FeedReaderThread(this, p3FeedReaderThread::PROCESS, ""); mThreads.push_back(frt); frt->start(); } @@ -80,7 +85,7 @@ static void feedToInfo(const RsFeedReaderFeed *feed, FeedInfo &info) info.lastUpdate = feed->lastUpdate; info.forumId = feed->forumId; info.storageTime = feed->storageTime; - info.error = (feed->errorState != RS_FEED_ERRORSTATE_OK); // currently only as bool + info.errorState = feed->errorState; info.errorString = feed->errorString; info.flag.folder = (feed->flag & RS_FEED_FLAG_FOLDER); @@ -95,6 +100,8 @@ static void feedToInfo(const RsFeedReaderFeed *feed, FeedInfo &info) info.flag.embedImages = (feed->flag & RS_FEED_FLAG_EMBED_IMAGES); info.flag.saveCompletePage = (feed->flag & RS_FEED_FLAG_SAVE_COMPLETE_PAGE); + info.flag.preview = feed->preview; + switch (feed->workstate) { case RsFeedReaderFeed::WAITING: info.workstate = FeedInfo::WAITING; @@ -134,6 +141,8 @@ static void infoToFeed(const FeedInfo &info, RsFeedReaderFeed *feed, bool add) feed->forumId = info.forumId; } +// feed->preview = info.flag.preview; + uint32_t oldFlag = feed->flag; feed->flag = 0; if (info.flag.infoFromFeed) { @@ -199,12 +208,14 @@ void p3FeedReader::setNotify(RsFeedReaderNotify *notify) uint32_t p3FeedReader::getStandardStorageTime() { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ + return mStandardStorageTime; } void p3FeedReader::setStandardStorageTime(uint32_t storageTime) { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ + if (mStandardStorageTime != storageTime) { mStandardStorageTime = storageTime; IndicateConfigChanged(); @@ -214,12 +225,14 @@ void p3FeedReader::setStandardStorageTime(uint32_t storageTime) uint32_t p3FeedReader::getStandardUpdateInterval() { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ + return mStandardUpdateInterval; } void p3FeedReader::setStandardUpdateInterval(uint32_t updateInterval) { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ + if (mStandardUpdateInterval != updateInterval) { mStandardUpdateInterval = updateInterval; IndicateConfigChanged(); @@ -229,14 +242,17 @@ void p3FeedReader::setStandardUpdateInterval(uint32_t updateInterval) bool p3FeedReader::getStandardProxy(std::string &proxyAddress, uint16_t &proxyPort) { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ + proxyAddress = mStandardProxyAddress; proxyPort = mStandardProxyPort; + return mStandardUseProxy; } void p3FeedReader::setStandardProxy(bool useProxy, const std::string &proxyAddress, uint16_t proxyPort) { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ + if (useProxy != mStandardUseProxy || proxyAddress != mStandardProxyAddress || proxyPort != mStandardProxyPort) { mStandardProxyAddress = proxyAddress; mStandardProxyPort = proxyPort; @@ -247,12 +263,37 @@ void p3FeedReader::setStandardProxy(bool useProxy, const std::string &proxyAddre void p3FeedReader::stop() { - /* stop threads */ - std::list::iterator it; - for (it = mThreads.begin(); it != mThreads.end(); ++it) { - (*it)->join(); + { + RsStackMutex stack(mPreviewMutex); /******* LOCK STACK MUTEX *********/ + + stopPreviewThreads_locked(); + } + + { + RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ + + /* stop threads */ + std::list::iterator it; + for (it = mThreads.begin(); it != mThreads.end(); ++it) { + (*it)->join(); + delete(*it); + } + mThreads.clear(); + } +} + +void p3FeedReader::stopPreviewThreads_locked() +{ + if (mPreviewDownloadThread) { + mPreviewDownloadThread->join(); + delete mPreviewDownloadThread; + mPreviewDownloadThread = NULL; + } + if (mPreviewProcessThread) { + mPreviewProcessThread->join(); + delete mPreviewProcessThread; + mPreviewProcessThread = NULL; } - mThreads.clear(); } RsFeedAddResult p3FeedReader::addFolder(const std::string parentId, const std::string &name, std::string &feedId) @@ -481,6 +522,8 @@ void p3FeedReader::deleteAllMsgs_locked(RsFeedReaderFeed *fi) bool p3FeedReader::removeFeed(const std::string &feedId) { std::list removedFeedIds; + bool changed = false; + bool preview = false; { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ @@ -497,6 +540,8 @@ bool p3FeedReader::removeFeed(const std::string &feedId) RsFeedReaderFeed *fi = feedIt->second; mFeeds.erase(feedIt); + changed = !fi->preview; + preview = fi->preview; if (fi->flag & RS_FEED_FLAG_FOLDER) { std::list feedIds; @@ -534,7 +579,18 @@ bool p3FeedReader::removeFeed(const std::string &feedId) delete(fi); } - IndicateConfigChanged(); + if (changed) { + IndicateConfigChanged(); + } + + if (preview) { + RsStackMutex stack(mPreviewMutex); /******* LOCK STACK MUTEX *********/ + + /* only check download thread */ + if (mPreviewDownloadThread && mPreviewDownloadThread->getFeedId() == feedId) { + stopPreviewThreads_locked(); + } + } if (mNotify) { /* only notify remove of feed */ @@ -547,6 +603,63 @@ bool p3FeedReader::removeFeed(const std::string &feedId) return true; } +bool p3FeedReader::addPreviewFeed(const FeedInfo &feedInfo, std::string &feedId) +{ + { + RsStackMutex stack(mPreviewMutex); /******* LOCK STACK MUTEX *********/ + + stopPreviewThreads_locked(); + } + + feedId.clear(); + + { + RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ + +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReader::addPreviewFeed - add feed " << feedInfo.name << ", url " << feedInfo.url << std::endl; +#endif + + RsFeedReaderFeed *fi = new RsFeedReaderFeed; + infoToFeed(feedInfo, fi, true); + rs_sprintf(fi->feedId, "preview%d", mNextPreviewFeedId--); + fi->preview = true; + + /* process feed */ + fi->workstate = RsFeedReaderFeed::WAITING_TO_DOWNLOAD; + fi->content.clear(); + + /* clear not needed members */ + fi->parentId.clear(); + fi->updateInterval = 0; + fi->lastUpdate = 0; + fi->forumId.clear(); + fi->storageTime = 0; + + mFeeds[fi->feedId] = fi; + + feedId = fi->feedId; + } + + if (mNotify) { + mNotify->feedChanged(feedId, NOTIFY_TYPE_ADD); + } + + { + RsStackMutex stack(mPreviewMutex); /******* LOCK STACK MUTEX *********/ + + /* start download thread for preview */ + mPreviewDownloadThread = new p3FeedReaderThread(this, p3FeedReaderThread::DOWNLOAD, feedId); + mPreviewDownloadThread->start(); + + /* start process thread for preview */ + mPreviewProcessThread = new p3FeedReaderThread(this, p3FeedReaderThread::PROCESS, feedId); + mPreviewProcessThread->start(); + } + + return true; +} + void p3FeedReader::getFeedList(const std::string &parentId, std::list &feedInfos) { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ @@ -555,6 +668,10 @@ void p3FeedReader::getFeedList(const std::string &parentId, std::list for (feedIt = mFeeds.begin(); feedIt != mFeeds.end(); ++feedIt) { RsFeedReaderFeed *fi = feedIt->second; + if (fi->preview) { + continue; + } + if (fi->parentId == parentId) { FeedInfo feedInfo; feedToInfo(fi, feedInfo); @@ -610,6 +727,8 @@ bool p3FeedReader::getMsgInfo(const std::string &feedId, const std::string &msgI bool p3FeedReader::removeMsg(const std::string &feedId, const std::string &msgId) { + bool changed = false; + { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ @@ -622,6 +741,7 @@ bool p3FeedReader::removeMsg(const std::string &feedId, const std::string &msgId } RsFeedReaderFeed *fi = feedIt->second; + changed = !fi->preview; std::map::iterator msgIt; msgIt = fi->mMsgs.find(msgId); @@ -635,7 +755,10 @@ bool p3FeedReader::removeMsg(const std::string &feedId, const std::string &msgId msgIt->second->flag |= RS_FEEDMSG_FLAG_DELETED; } - IndicateConfigChanged(); + if (changed) { + IndicateConfigChanged(); + } + if (mNotify) { mNotify->feedChanged(feedId, NOTIFY_TYPE_MOD); mNotify->msgChanged(feedId, msgId, NOTIFY_TYPE_DEL); @@ -647,6 +770,7 @@ bool p3FeedReader::removeMsg(const std::string &feedId, const std::string &msgId bool p3FeedReader::removeMsgs(const std::string &feedId, const std::list &msgIds) { std::list removedMsgs; + bool changed = false; { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ @@ -660,6 +784,7 @@ bool p3FeedReader::removeMsgs(const std::string &feedId, const std::listsecond; + changed = !fi->preview; std::list::const_iterator idIt; for (idIt = msgIds.begin(); idIt != msgIds.end(); ++idIt) { @@ -678,6 +803,10 @@ bool p3FeedReader::removeMsgs(const std::string &feedId, const std::listfeedChanged(feedId, NOTIFY_TYPE_MOD); @@ -758,8 +887,41 @@ bool p3FeedReader::getFeedMsgList(const std::string &feedId, std::list &msgIds) +{ + RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ + + std::map::iterator feedIt = mFeeds.find(feedId); + if (feedIt == mFeeds.end()) { +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReader::getFeedMsgList - feed " << feedId << " not found" << std::endl; +#endif + return false; + } + + RsFeedReaderFeed *fi = feedIt->second; + + std::map::iterator msgIt; + for (msgIt = fi->mMsgs.begin(); msgIt != fi->mMsgs.end(); ++msgIt) { + RsFeedReaderMsg *mi = msgIt->second; + + if (mi->flag & RS_FEEDMSG_FLAG_DELETED) { + continue; + } + + msgIds.push_back(mi->msgId); + } + + return true; +} + static bool canProcessFeed(RsFeedReaderFeed *fi) { + if (fi->preview) { + /* preview feed */ + return false; + } + if (fi->flag & RS_FEED_FLAG_DEACTIVATED) { /* deactivated */ return false; @@ -939,6 +1101,7 @@ int p3FeedReader::tick() /* clean feeds */ cleanFeeds(); + /* check feeds for update interval */ time_t currentTime = time(NULL); std::list feedToDownload; std::map::iterator feedIt; @@ -1105,6 +1268,9 @@ bool p3FeedReader::saveList(bool &cleanup, std::list & saveData) std::map::iterator it1; for (it1 = mFeeds.begin(); it1 != mFeeds.end(); ++it1) { RsFeedReaderFeed *fi = it1->second; + if (fi->preview) { + continue; + } saveData.push_back(fi); std::map::iterator it2; @@ -1227,11 +1393,11 @@ bool p3FeedReader::loadList(std::list& load) /****************************** internal ***********************************/ /***************************************************************************/ -bool p3FeedReader::getFeedToDownload(RsFeedReaderFeed &feed) +bool p3FeedReader::getFeedToDownload(RsFeedReaderFeed &feed, const std::string &neededFeedId) { - std::string feedId; + std::string feedId = neededFeedId; - { + if (feedId.empty()) { RsStackMutex stack(mDownloadMutex); /******* LOCK STACK MUTEX *********/ if (mDownloadFeeds.empty()) { @@ -1257,6 +1423,7 @@ bool p3FeedReader::getFeedToDownload(RsFeedReaderFeed &feed) return false; } + /* check state */ if (it->second->workstate != RsFeedReaderFeed::WAITING_TO_DOWNLOAD) { std::cerr << "p3FeedReader::getFeedToDownload - feed in wrong work state for download " << it->second->workstate << std::endl; return false; @@ -1282,6 +1449,8 @@ bool p3FeedReader::getFeedToDownload(RsFeedReaderFeed &feed) void p3FeedReader::onDownloadSuccess(const std::string &feedId, const std::string &content, std::string &icon) { + bool preview; + { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ @@ -1298,11 +1467,14 @@ void p3FeedReader::onDownloadSuccess(const std::string &feedId, const std::strin RsFeedReaderFeed *fi = it->second; fi->workstate = RsFeedReaderFeed::WAITING_TO_PROCESS; fi->content = content; + preview = fi->preview; if (fi->icon != icon) { fi->icon = icon; - IndicateConfigChanged(); + if (!preview) { + IndicateConfigChanged(); + } } #ifdef FEEDREADER_DEBUG @@ -1310,7 +1482,7 @@ void p3FeedReader::onDownloadSuccess(const std::string &feedId, const std::strin #endif } - { + if (!preview) { RsStackMutex stack(mProcessMutex); /******* LOCK STACK MUTEX *********/ if (std::find(mProcessFeeds.begin(), mProcessFeeds.end(), feedId) == mProcessFeeds.end()) { @@ -1324,7 +1496,7 @@ void p3FeedReader::onDownloadSuccess(const std::string &feedId, const std::strin } } -void p3FeedReader::onDownloadError(const std::string &feedId, p3FeedReaderThread::DownloadResult result, const std::string &error) +void p3FeedReader::onDownloadError(const std::string &feedId, p3FeedReaderThread::DownloadResult result, const std::string &errorString) { { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ @@ -1344,7 +1516,6 @@ void p3FeedReader::onDownloadError(const std::string &feedId, p3FeedReaderThread fi->lastUpdate = time(NULL); fi->content.clear(); - long todo; // sort error codes switch (result) { case p3FeedReaderThread::DOWNLOAD_SUCCESS: /* this should not happen */ @@ -1354,9 +1525,6 @@ void p3FeedReader::onDownloadError(const std::string &feedId, p3FeedReaderThread case p3FeedReaderThread::DOWNLOAD_ERROR: fi->errorState = RS_FEED_ERRORSTATE_DOWNLOAD_ERROR; break; - case p3FeedReaderThread::DOWNLOAD_INTERNAL_ERROR: - fi->errorState = RS_FEED_ERRORSTATE_DOWNLOAD_INTERNAL_ERROR; - break; case p3FeedReaderThread::DOWNLOAD_UNKNOWN_CONTENT_TYPE: fi->errorState = RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE; break; @@ -1370,13 +1538,15 @@ void p3FeedReader::onDownloadError(const std::string &feedId, p3FeedReaderThread fi->errorState = RS_FEED_ERRORSTATE_DOWNLOAD_INTERNAL_ERROR; } - fi->errorString = error; + fi->errorString = errorString; #ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReader::onDownloadError - feed " << fi->feedId << " (" << fi->name << ") error download, result = " << result << ", errorState = " << fi->errorState << ", error = " << error << std::endl; + std::cerr << "p3FeedReader::onDownloadError - feed " << fi->feedId << " (" << fi->name << ") error download, result = " << result << ", errorState = " << fi->errorState << ", error = " << errorString << std::endl; #endif - IndicateConfigChanged(); + if (!fi->preview) { + IndicateConfigChanged(); + } } if (mNotify) { @@ -1384,11 +1554,11 @@ void p3FeedReader::onDownloadError(const std::string &feedId, p3FeedReaderThread } } -bool p3FeedReader::getFeedToProcess(RsFeedReaderFeed &feed) +bool p3FeedReader::getFeedToProcess(RsFeedReaderFeed &feed, const std::string &neededFeedId) { - std::string feedId; + std::string feedId = neededFeedId; - { + if (feedId.empty()) { RsStackMutex stack(mProcessMutex); /******* LOCK STACK MUTEX *********/ if (mProcessFeeds.empty()) { @@ -1414,13 +1584,13 @@ bool p3FeedReader::getFeedToProcess(RsFeedReaderFeed &feed) return false; } - if (it->second->workstate != RsFeedReaderFeed::WAITING_TO_PROCESS) { - std::cerr << "p3FeedReader::getFeedToProcess - feed in wrong state for process " << it->second->workstate << std::endl; + RsFeedReaderFeed *fi = it->second; + + if (fi->workstate != RsFeedReaderFeed::WAITING_TO_PROCESS) { + std::cerr << "p3FeedReader::getFeedToProcess - feed in wrong state for process " << fi->workstate << std::endl; return false; } - RsFeedReaderFeed *fi = it->second; - /* set state to processing */ fi->workstate = RsFeedReaderFeed::PROCESSING; fi->errorState = RS_FEED_ERRORSTATE_OK; @@ -1430,7 +1600,7 @@ bool p3FeedReader::getFeedToProcess(RsFeedReaderFeed &feed) feed = *fi; #ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReader::getFeedToProcess - feed " << it->second->feedId << " (" << it->second->name << ") is starting to process" << std::endl; + std::cerr << "p3FeedReader::getFeedToProcess - feed " << fi->feedId << " (" << fi->name << ") is starting to process" << std::endl; #endif } @@ -1447,8 +1617,6 @@ bool p3FeedReader::onProcessSuccess_filterMsg(const std::string &feedId, std::li std::cerr << "p3FeedReader::onProcessSuccess_filterMsg - feed " << feedId << " got " << msgs.size() << " messages" << std::endl; #endif - bool result = true; - { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ @@ -1463,10 +1631,67 @@ bool p3FeedReader::onProcessSuccess_filterMsg(const std::string &feedId, std::li } RsFeedReaderFeed *fi = it->second; - bool forum = (fi->flag & RS_FEED_FLAG_FORUM); - uint32_t errorState = RS_FEED_ERRORSTATE_OK; - if (forum) { + std::list::iterator newMsgIt; + for (newMsgIt = msgs.begin(); newMsgIt != msgs.end(); ) { + RsFeedReaderMsg *miNew = *newMsgIt; + /* search for existing msg */ + std::map::iterator msgIt; + for (msgIt = fi->mMsgs.begin(); msgIt != fi->mMsgs.end(); ++msgIt) { + RsFeedReaderMsg *mi = msgIt->second; + if (mi->title == miNew->title && mi->link == miNew->link && mi->author == miNew->author) { + /* msg exist */ + break; + } + } + if (msgIt != fi->mMsgs.end()) { + /* msg exists */ + delete(miNew); + newMsgIt = msgs.erase(newMsgIt); + } else { + ++newMsgIt; + } + } + + fi->content.clear(); + fi->errorString.clear(); + + if (!fi->preview) { + IndicateConfigChanged(); + } + } + + return true; +} + +void p3FeedReader::onProcessSuccess_addMsgs(const std::string &feedId, bool result, std::list &msgs, bool single) +{ +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << feedId << " got " << msgs.size() << " messages" << std::endl; +#endif + + std::list addedMsgs; + std::string forumId; + std::list forumMsgs; + + { + RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ + + /* find feed */ + std::map::iterator it = mFeeds.find(feedId); + if (it == mFeeds.end()) { + /* feed not found */ +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << feedId << " not found" << std::endl; +#endif + return; + } + + RsFeedReaderFeed *fi = it->second; + bool forum = (fi->flag & RS_FEED_FLAG_FORUM) && !fi->preview; + RsFeedReaderErrorState errorState = RS_FEED_ERRORSTATE_OK; + + if (result && forum && !msgs.empty()) { if (fi->forumId.empty()) { /* create new forum */ std::wstring forumName; @@ -1491,14 +1716,15 @@ bool p3FeedReader::onProcessSuccess_filterMsg(const std::string &feedId, std::li librs::util::ConvertUtf8ToUtf16(fi->description, forumDescription); /* create anonymous public forum */ fi->forumId = rsForums->createForum(forumName, forumDescription, RS_DISTRIB_PUBLIC | RS_DISTRIB_AUTHEN_ANON); + forumId = fi->forumId; if (fi->forumId.empty()) { - errorState = RS_FEED_ERRORSTATE_FORUM_CREATE; + errorState = RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE; #ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReader::onProcessSuccess_filterMsg - can't create forum for feed " << feedId << " (" << it->second->name << ") - ignore all messages" << std::endl; + std::cerr << "p3FeedReader::onProcessSuccess_filterMsg - can't create forum for feed " << feedId << " (" << fi->name << ") - ignore all messages" << std::endl; } else { - std::cerr << "p3FeedReader::onProcessSuccess_filterMsg - forum " << forumId << " (" << fi->name << ") created" << std::endl; + std::cerr << "p3FeedReader::onProcessSuccess_filterMsg - forum " << fi->forumId << " (" << fi->name << ") created" << std::endl; #endif } } else { @@ -1506,129 +1732,66 @@ bool p3FeedReader::onProcessSuccess_filterMsg(const std::string &feedId, std::li ForumInfo forumInfo; if (rsForums->getForumInfo(fi->forumId, forumInfo)) { if ((forumInfo.subscribeFlags & RS_DISTRIB_ADMIN) == 0) { - errorState = RS_FEED_ERRORSTATE_FORUM_NO_ADMIN; + errorState = RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_ADMIN; } else if ((forumInfo.forumFlags & RS_DISTRIB_AUTHEN_REQ) || (forumInfo.forumFlags & RS_DISTRIB_AUTHEN_ANON) == 0) { - errorState = RS_FEED_ERRORSTATE_FORUM_NO_ANONYMOUS_FORUM; + errorState = RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_ANONYMOUS; + } else { + forumId = fi->forumId; } } else { - errorState = RS_FEED_ERRORSTATE_FORUM_NOT_FOUND; + errorState = RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_FOUND; } } } /* process msgs */ if (errorState == RS_FEED_ERRORSTATE_OK) { - std::list::iterator newMsgIt; - for (newMsgIt = msgs.begin(); newMsgIt != msgs.end(); ) { - RsFeedReaderMsg *miNew = *newMsgIt; - /* search for existing msg */ - std::map::iterator msgIt; - for (msgIt = fi->mMsgs.begin(); msgIt != fi->mMsgs.end(); ++msgIt) { - RsFeedReaderMsg *mi = msgIt->second; - if (mi->title == miNew->title && mi->link == mi->link && mi->author == mi->author) { - /* msg exist */ - break; + /* process msgs */ +#ifdef FEEDREADER_DEBUG + uint32_t newMsgs = 0; +#endif + + if (result) { + std::list::iterator newMsgIt; + for (newMsgIt = msgs.begin(); newMsgIt != msgs.end(); ) { + RsFeedReaderMsg *miNew = *newMsgIt; + /* add new msg */ + if (fi->preview) { + rs_sprintf(miNew->msgId, "preview%d", mNextPreviewMsgId--); + } else { + rs_sprintf(miNew->msgId, "%lu", mNextMsgId++); } - } - if (msgIt != fi->mMsgs.end()) { - /* msg exists */ - delete(*newMsgIt); + if (forum) { + miNew->flag = RS_FEEDMSG_FLAG_DELETED; + forumMsgs.push_back(*miNew); +// miNew->description.clear(); + } else { + miNew->flag = RS_FEEDMSG_FLAG_NEW; + addedMsgs.push_back(miNew->msgId); + } + fi->mMsgs[miNew->msgId] = miNew; newMsgIt = msgs.erase(newMsgIt); - } else { - ++newMsgIt; + +#ifdef FEEDREADER_DEBUG + ++newMsgs; +#endif } - } - } else { - result = false; - } - - fi->content.clear(); - fi->errorState = errorState; - fi->errorString.clear(); - - IndicateConfigChanged(); - } - - if (mNotify) { - mNotify->feedChanged(feedId, NOTIFY_TYPE_MOD); - } - - return result; -} - -void p3FeedReader::onProcessSuccess_addMsgs(const std::string &feedId, bool result, std::list &msgs) -{ #ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << feedId << " got " << msgs.size() << " messages" << std::endl; -#endif - - std::list addedMsgs; - std::string forumId; - std::list forumMsgs; - - { - RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ - - /* find feed */ - std::map::iterator it = mFeeds.find(feedId); - if (it == mFeeds.end()) { - /* feed not found */ -#ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << feedId << " not found" << std::endl; -#endif - return; - } - - RsFeedReaderFeed *fi = it->second; - bool forum = (fi->flag & RS_FEED_FLAG_FORUM); - - if (forum) { - forumId = fi->forumId; - if (forumId.empty()) { - /* don't process messages without forum id */ - result = false; -#ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << fi->feedId << " (" << fi->name << ") don't process messages without forum id" << std::endl; + std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << fi->feedId << " (" << fi->name << ") added " << newMsgs << "/" << msgs.size() << " messages" << std::endl; #endif } } - /* process msgs */ -#ifdef FEEDREADER_DEBUG - uint32_t newMsgs = 0; -#endif - - if (result) { - std::list::iterator newMsgIt; - for (newMsgIt = msgs.begin(); newMsgIt != msgs.end(); ) { - RsFeedReaderMsg *miNew = *newMsgIt; - /* add new msg */ - rs_sprintf(miNew->msgId, "%lu", mNextMsgId++); - if (forum) { - miNew->flag = RS_FEEDMSG_FLAG_DELETED; - forumMsgs.push_back(*miNew); -// miNew->description.clear(); - } else { - miNew->flag = RS_FEEDMSG_FLAG_NEW; - addedMsgs.push_back(miNew->msgId); - } - fi->mMsgs[miNew->msgId] = miNew; - newMsgIt = msgs.erase(newMsgIt); - -#ifdef FEEDREADER_DEBUG - ++newMsgs; -#endif - } -#ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << fi->feedId << " (" << fi->name << ") added " << newMsgs << "/" << msgs.size() << " messages" << std::endl; -#endif + if (!single) { + fi->workstate = RsFeedReaderFeed::WAITING; + fi->content.clear(); + fi->errorState = errorState; + fi->lastUpdate = time(NULL); } - fi->workstate = RsFeedReaderFeed::WAITING; - fi->content.clear(); - fi->lastUpdate = time(NULL); - - IndicateConfigChanged(); + if (!fi->preview) { + IndicateConfigChanged(); + } } if (!forumId.empty() && !forumMsgs.empty()) { @@ -1670,7 +1833,7 @@ void p3FeedReader::onProcessSuccess_addMsgs(const std::string &feedId, bool resu } } -void p3FeedReader::onProcessError(const std::string &feedId, p3FeedReaderThread::ProcessResult result) +void p3FeedReader::onProcessError(const std::string &feedId, p3FeedReaderThread::ProcessResult result, const std::string &errorString) { { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ @@ -1700,17 +1863,22 @@ void p3FeedReader::onProcessError(const std::string &feedId, p3FeedReaderThread: case p3FeedReaderThread::PROCESS_ERROR_INIT: fi->errorState = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR; break; + case p3FeedReaderThread::PROCESS_UNKNOWN_FORMAT: + fi->errorState = RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT; + break; default: fi->errorState = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR; } -// fi->errorString = error; + fi->errorString = errorString; #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReader::onProcessError - feed " << fi->feedId << " (" << fi->name << ") error process, result = " << result << ", errorState = " << fi->errorState << std::endl; #endif - IndicateConfigChanged(); + if (!fi->preview) { + IndicateConfigChanged(); + } } if (mNotify) { @@ -1721,6 +1889,7 @@ void p3FeedReader::onProcessError(const std::string &feedId, p3FeedReaderThread: void p3FeedReader::setFeedInfo(const std::string &feedId, const std::string &name, const std::string &description) { bool changed = false; + bool preview; std::string forumId; ForumInfo forumInfoNew; @@ -1738,6 +1907,7 @@ void p3FeedReader::setFeedInfo(const std::string &feedId, const std::string &nam } RsFeedReaderFeed *fi = it->second; + preview = fi->preview; if (fi->name != name) { #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReader::setFeedInfo - feed " << fi->feedId << " changed name from " << fi->name << " to " << name << std::endl; @@ -1753,7 +1923,7 @@ void p3FeedReader::setFeedInfo(const std::string &feedId, const std::string &nam changed = true; } - if ((fi->flag & RS_FEED_FLAG_FORUM) && (fi->flag & RS_FEED_FLAG_UPDATE_FORUM_INFO) && !fi->forumId.empty()) { + if ((fi->flag & RS_FEED_FLAG_FORUM) && (fi->flag & RS_FEED_FLAG_UPDATE_FORUM_INFO) && !fi->forumId.empty() && !preview) { /* change forum too */ forumId = fi->forumId; librs::util::ConvertUtf8ToUtf16(fi->name, forumInfoNew.forumName); @@ -1763,7 +1933,9 @@ void p3FeedReader::setFeedInfo(const std::string &feedId, const std::string &nam } if (changed) { - IndicateConfigChanged(); + if (!preview) { + IndicateConfigChanged(); + } if (mNotify) { mNotify->feedChanged(feedId, NOTIFY_TYPE_MOD); diff --git a/plugins/FeedReader/services/p3FeedReader.h b/plugins/FeedReader/services/p3FeedReader.h index e4525a65b..2bced3c0f 100644 --- a/plugins/FeedReader/services/p3FeedReader.h +++ b/plugins/FeedReader/services/p3FeedReader.h @@ -49,33 +49,35 @@ public: virtual bool getStandardProxy(std::string &proxyAddress, uint16_t &proxyPort); virtual void setStandardProxy(bool useProxy, const std::string &proxyAddress, uint16_t proxyPort); - virtual RsFeedAddResult addFolder(const std::string parentId, const std::string &name, std::string &feedId); - virtual RsFeedAddResult setFolder(const std::string &feedId, const std::string &name); - virtual RsFeedAddResult addFeed(const FeedInfo &feedInfo, std::string &feedId); - virtual RsFeedAddResult setFeed(const std::string &feedId, const FeedInfo &feedInfo); - virtual bool removeFeed(const std::string &feedId); - virtual void getFeedList(const std::string &parentId, std::list &feedInfos); - virtual bool getFeedInfo(const std::string &feedId, FeedInfo &feedInfo); - virtual bool getMsgInfo(const std::string &feedId, const std::string &msgId, FeedMsgInfo &msgInfo); - virtual bool removeMsg(const std::string &feedId, const std::string &msgId); - virtual bool removeMsgs(const std::string &feedId, const std::list &msgIds); - virtual bool getMessageCount(const std::string &feedId, uint32_t *msgCount, uint32_t *newCount, uint32_t *unreadCount); - virtual bool getFeedMsgList(const std::string &feedId, std::list &msgInfos); - virtual bool processFeed(const std::string &feedId); - virtual bool setMessageRead(const std::string &feedId, const std::string &msgId, bool read); + virtual RsFeedAddResult addFolder(const std::string parentId, const std::string &name, std::string &feedId); + virtual RsFeedAddResult setFolder(const std::string &feedId, const std::string &name); + virtual RsFeedAddResult addFeed(const FeedInfo &feedInfo, std::string &feedId); + virtual RsFeedAddResult setFeed(const std::string &feedId, const FeedInfo &feedInfo); + virtual bool removeFeed(const std::string &feedId); + virtual bool addPreviewFeed(const FeedInfo &feedInfo, std::string &feedId); + virtual void getFeedList(const std::string &parentId, std::list &feedInfos); + virtual bool getFeedInfo(const std::string &feedId, FeedInfo &feedInfo); + virtual bool getMsgInfo(const std::string &feedId, const std::string &msgId, FeedMsgInfo &msgInfo); + virtual bool removeMsg(const std::string &feedId, const std::string &msgId); + virtual bool removeMsgs(const std::string &feedId, const std::list &msgIds); + virtual bool getMessageCount(const std::string &feedId, uint32_t *msgCount, uint32_t *newCount, uint32_t *unreadCount); + virtual bool getFeedMsgList(const std::string &feedId, std::list &msgInfos); + virtual bool getFeedMsgIdList(const std::string &feedId, std::list &msgIds); + virtual bool processFeed(const std::string &feedId); + virtual bool setMessageRead(const std::string &feedId, const std::string &msgId, bool read); /****************** p3Service STUFF ******************/ virtual int tick(); /****************** internal STUFF *******************/ - bool getFeedToDownload(RsFeedReaderFeed &feed); + bool getFeedToDownload(RsFeedReaderFeed &feed, const std::string &neededFeedId); void onDownloadSuccess(const std::string &feedId, const std::string &content, std::string &icon); - void onDownloadError(const std::string &feedId, p3FeedReaderThread::DownloadResult result, const std::string &error); + void onDownloadError(const std::string &feedId, p3FeedReaderThread::DownloadResult result, const std::string &errorString); bool onProcessSuccess_filterMsg(const std::string &feedId, std::list &msgs); - void onProcessSuccess_addMsgs(const std::string &feedId, bool result, std::list &msgs); - void onProcessError(const std::string &feedId, p3FeedReaderThread::ProcessResult result); + void onProcessSuccess_addMsgs(const std::string &feedId, bool result, std::list &msgs, bool single); + void onProcessError(const std::string &feedId, p3FeedReaderThread::ProcessResult result, const std::string &errorString); - bool getFeedToProcess(RsFeedReaderFeed &feed); + bool getFeedToProcess(RsFeedReaderFeed &feed, const std::string &neededFeedId); void setFeedInfo(const std::string &feedId, const std::string &name, const std::string &description); @@ -89,14 +91,17 @@ protected: private: void cleanFeeds(); void deleteAllMsgs_locked(RsFeedReaderFeed *fi); + void stopPreviewThreads_locked(); - std::list mThreads; - uint32_t mNextFeedId; - uint32_t mNextMsgId; time_t mLastClean; RsFeedReaderNotify *mNotify; RsMutex mFeedReaderMtx; + std::list mThreads; + uint32_t mNextFeedId; + uint32_t mNextMsgId; + int32_t mNextPreviewFeedId; + int32_t mNextPreviewMsgId; uint32_t mStandardUpdateInterval; uint32_t mStandardStorageTime; bool mStandardUseProxy; @@ -109,6 +114,10 @@ private: RsMutex mProcessMutex; std::list mProcessFeeds; + + RsMutex mPreviewMutex; + p3FeedReaderThread *mPreviewDownloadThread; + p3FeedReaderThread *mPreviewProcessThread; }; #endif diff --git a/plugins/FeedReader/services/p3FeedReaderThread.cc b/plugins/FeedReader/services/p3FeedReaderThread.cc index b95589c01..4ee228951 100644 --- a/plugins/FeedReader/services/p3FeedReaderThread.cc +++ b/plugins/FeedReader/services/p3FeedReaderThread.cc @@ -23,10 +23,9 @@ #include "rsFeedReaderItems.h" #include "util/rsstring.h" #include "util/CURLWrapper.h" +#include "util/XMLWrapper.h" +#include "util/HTMLWrapper.h" -#include -#include -#include #include enum FeedFormat { FORMAT_RSS, FORMAT_RDF }; @@ -36,23 +35,13 @@ enum FeedFormat { FORMAT_RSS, FORMAT_RDF }; *********/ #define FEEDREADER_DEBUG -p3FeedReaderThread::p3FeedReaderThread(p3FeedReader *feedReader, Type type) : RsThread(), mFeedReader(feedReader), mType(type) +p3FeedReaderThread::p3FeedReaderThread(p3FeedReader *feedReader, Type type, const std::string &feedId) : + RsThread(), mFeedReader(feedReader), mType(type), mFeedId(feedId) { - if (type == PROCESS) { - mCharEncodingHandler = xmlFindCharEncodingHandler ("UTF8"); - - if (!mCharEncodingHandler) { - /* no encoding handler found */ - std::cerr << "p3FeedReaderThread::p3FeedReaderThread - no encoding handler found" << std::endl; - } - } else { - mCharEncodingHandler = NULL; - } } p3FeedReaderThread::~p3FeedReaderThread() { - xmlCharEncCloseFunc((xmlCharEncodingHandlerPtr) mCharEncodingHandler); } /***************************************************************************/ @@ -73,16 +62,16 @@ void p3FeedReaderThread::run() case DOWNLOAD: { RsFeedReaderFeed feed; - if (mFeedReader->getFeedToDownload(feed)) { + if (mFeedReader->getFeedToDownload(feed, mFeedId)) { std::string content; std::string icon; - std::string error; + std::string errorString; - DownloadResult result = download(feed, content, icon, error); + DownloadResult result = download(feed, content, icon, errorString); if (result == DOWNLOAD_SUCCESS) { mFeedReader->onDownloadSuccess(feed.feedId, content, icon); } else { - mFeedReader->onDownloadError(feed.feedId, result, error); + mFeedReader->onDownloadError(feed.feedId, result, errorString); } } } @@ -90,31 +79,48 @@ void p3FeedReaderThread::run() case PROCESS: { RsFeedReaderFeed feed; - if (mFeedReader->getFeedToProcess(feed)) { + if (mFeedReader->getFeedToProcess(feed, mFeedId)) { std::list msgs; - std::string error; + std::string errorString; std::list::iterator it; - ProcessResult result = process(feed, msgs, error); + ProcessResult result = process(feed, msgs, errorString); if (result == PROCESS_SUCCESS) { /* first, filter the messages */ bool result = mFeedReader->onProcessSuccess_filterMsg(feed.feedId, msgs); if (isRunning()) { if (result) { - long todo; // process new items /* second, process the descriptions */ - for (it = msgs.begin(); it != msgs.end(); ++it) { + for (it = msgs.begin(); it != msgs.end(); ) { RsFeedReaderMsg *mi = *it; processMsg(feed, mi); + + if (feed.preview) { + /* add every message */ + it = msgs.erase(it); + + std::list msgSingle; + msgSingle.push_back(mi); + mFeedReader->onProcessSuccess_addMsgs(feed.feedId, result, msgSingle, true); + + /* delete not accepted message */ + std::list::iterator it1; + for (it1 = msgSingle.begin(); it1 != msgSingle.end(); ++it1) { + delete (*it1); + } + } else { + ++it; + } } } /* third, add messages */ - mFeedReader->onProcessSuccess_addMsgs(feed.feedId, result, msgs); + mFeedReader->onProcessSuccess_addMsgs(feed.feedId, result, msgs, false); } } else { - mFeedReader->onProcessError(feed.feedId, result); + mFeedReader->onProcessError(feed.feedId, result, errorString); } + /* delete not accepted messages */ for (it = msgs.begin(); it != msgs.end(); ++it) { delete (*it); } @@ -299,70 +305,6 @@ p3FeedReaderThread::DownloadResult p3FeedReaderThread::download(const RsFeedRead /****************************** Process ************************************/ /***************************************************************************/ -static bool convertToString(xmlCharEncodingHandlerPtr charEncodingHandler, const xmlChar *xmlText, std::string &text) -{ - bool result = false; - - xmlBufferPtr in = xmlBufferCreateStatic((void*) xmlText, xmlStrlen(xmlText)); - xmlBufferPtr out = xmlBufferCreate(); - int ret = xmlCharEncOutFunc(charEncodingHandler, out, in); - if (ret >= 0) { - result = true; - text = (char*) xmlBufferContent(out); - } - - xmlBufferFree(in); - xmlBufferFree(out); - - return result; -} - -static bool convertFromString(xmlCharEncodingHandlerPtr charEncodingHandler, const char *text, xmlChar *&xmlText) -{ - bool result = false; - - xmlBufferPtr in = xmlBufferCreateStatic((void*) text, strlen(text)); - xmlBufferPtr out = xmlBufferCreate(); - int ret = xmlCharEncOutFunc(charEncodingHandler, out, in); - if (ret >= 0) { - result = true; - xmlText = xmlBufferDetach(out); - } - - xmlBufferFree(in); - xmlBufferFree(out); - - return result; -} - -static xmlNodePtr findNode(xmlNodePtr node, const char *name, bool children = false) -{ - if (node->name) { - if (xmlStrcasecmp (node->name, (xmlChar*) name) == 0) { - return node; - } - } - - xmlNodePtr nodeFound = NULL; - if (children) { - if (node->children) { - nodeFound = findNode(node->children, name, children); - if (nodeFound) { - return nodeFound; - } - } - } - - if (node->next) { - nodeFound = findNode(node->next, name, children); - if (nodeFound) { - return nodeFound; - } - } - - return NULL; -} - static xmlNodePtr getNextItem(FeedFormat feedFormat, xmlNodePtr channel, xmlNodePtr item) { if (!channel) { @@ -384,7 +326,7 @@ static xmlNodePtr getNextItem(FeedFormat feedFormat, xmlNodePtr channel, xmlNode item = item->next; } for (; item; item = item->next) { - if (item->type == XML_ELEMENT_NODE && xmlStrcasecmp (item->name, (xmlChar*) "item") == 0) { + if (item->type == XML_ELEMENT_NODE && xmlStrcasecmp(item->name, (const xmlChar*) "item") == 0) { break; } } @@ -392,70 +334,6 @@ static xmlNodePtr getNextItem(FeedFormat feedFormat, xmlNodePtr channel, xmlNode return item; } -static bool getChildText(/*xmlCharEncodingHandlerPtr*/ void *charEncodingHandler, xmlNodePtr node, const char *childName, std::string &text) -{ - if (node == NULL || node->children == NULL) { - return false; - } - - xmlNodePtr child = findNode(node->children, childName, true); - if (!child) { - return false; - } - - if (child->type != XML_ELEMENT_NODE) { - return false; - } - - if (!child->children) { - return false; - } - - if (child->children->type != XML_TEXT_NODE) { - return false; - } - - if (child->children->content) { - return convertToString((xmlCharEncodingHandlerPtr) charEncodingHandler, child->children->content, text); - } - - return true; -} - -static std::string xmlGetAttr(/*xmlCharEncodingHandlerPtr*/ void *charEncodingHandler, xmlNodePtr node, const char *name) -{ - if (!node || !name) { - return ""; - } - - std::string value; - - xmlChar *xmlValue = xmlGetProp(node, (const xmlChar*) name); - if (xmlValue) { - convertToString((xmlCharEncodingHandlerPtr) charEncodingHandler, xmlValue, value); - xmlFree(xmlValue); - } - - return value; -} - -static bool xmlSetAttr(/*xmlCharEncodingHandlerPtr*/ void *charEncodingHandler, xmlNodePtr node, const char *name, const char *value) -{ - if (!node || !name) { - return false; - } - - xmlChar *xmlValue = NULL; - if (!convertFromString((xmlCharEncodingHandlerPtr) charEncodingHandler, value, xmlValue)) { - return false; - } - - xmlAttrPtr xmlAttr = xmlSetProp (node, (const xmlChar*) name, xmlValue); - xmlFree(xmlValue); - - return xmlAttr != NULL; -} - static void splitString(std::string s, std::vector &v, const char d) { v.clear(); @@ -919,14 +797,14 @@ p3FeedReaderThread::ProcessResult p3FeedReaderThread::process(const RsFeedReader ProcessResult result = PROCESS_SUCCESS; - xmlDocPtr document = xmlReadDoc((const xmlChar*) feed.content.c_str(), "", NULL, XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_COMPACT | XML_PARSE_NOCDATA); - if (document) { - xmlNodePtr root = xmlDocGetRootElement(document); + XMLWrapper xml; + if (xml.readXML(feed.content.c_str())) { + xmlNodePtr root = xml.getRootElement(); if (root) { FeedFormat feedFormat; - if (xmlStrcmp (root->name, (xmlChar*) "rss") == 0) { + if (xmlStrcasecmp(root->name, (const xmlChar*) "rss") == 0) { feedFormat = FORMAT_RSS; - } else if (xmlStrcmp (root->name, (xmlChar*) "rdf") != 0) { + } else if (xmlStrcasecmp (root->name, (const xmlChar*) "rdf") != 0) { feedFormat = FORMAT_RDF; } else { result = PROCESS_UNKNOWN_FORMAT; @@ -934,18 +812,18 @@ p3FeedReaderThread::ProcessResult p3FeedReaderThread::process(const RsFeedReader } if (result == PROCESS_SUCCESS) { - xmlNodePtr channel = findNode(root->children, "channel"); + xmlNodePtr channel = xml.findNode(root->children, "channel"); if (channel) { /* import header info */ if (feed.flag & RS_FEED_FLAG_INFO_FROM_FEED) { std::string title; - if (getChildText(mCharEncodingHandler, channel, "title", title) && !title.empty()) { + if (xml.getChildText(channel, "title", title) && !title.empty()) { std::string::size_type p; while ((p = title.find_first_of("\r\n")) != std::string::npos) { title.erase(p, 1); } std::string description; - getChildText(mCharEncodingHandler, channel, "description", description); + xml.getChildText(channel, "description", description); mFeedReader->setFeedInfo(feed.feedId, title, description); } } @@ -958,7 +836,7 @@ p3FeedReaderThread::ProcessResult p3FeedReaderThread::process(const RsFeedReader } std::string title; - if (!getChildText(mCharEncodingHandler, node, "title", title) || title.empty()) { + if (!xml.getChildText(node, "title", title) || title.empty()) { continue; } @@ -974,8 +852,8 @@ p3FeedReaderThread::ProcessResult p3FeedReaderThread::process(const RsFeedReader item->title = title; /* try feedburner:origLink */ - if (!getChildText(mCharEncodingHandler, node, "origLink", item->link) || item->link.empty()) { - getChildText(mCharEncodingHandler, node, "link", item->link); + if (!xml.getChildText(node, "origLink", item->link) || item->link.empty()) { + xml.getChildText(node, "link", item->link); } long todo; // remove sid @@ -1002,15 +880,15 @@ p3FeedReaderThread::ProcessResult p3FeedReaderThread::process(const RsFeedReader // sLink.Delete (nSIDStart, nSIDEnd - nSIDStart); // } - getChildText(mCharEncodingHandler, node, "author", item->author); + xml.getChildText(node, "author", item->author); - getChildText(mCharEncodingHandler, node, "description", item->description); + xml.getChildText(node, "description", item->description); std::string pubDate; - if (getChildText(mCharEncodingHandler, node, "pubdate", pubDate)) { + if (xml.getChildText(node, "pubdate", pubDate)) { item->pubDate = parseRFC822Date(pubDate); } - if (getChildText(mCharEncodingHandler, node, "date", pubDate)) { + if (xml.getChildText(node, "date", pubDate)) { item->pubDate = parseISO8601Date (pubDate); } @@ -1030,8 +908,6 @@ p3FeedReaderThread::ProcessResult p3FeedReaderThread::process(const RsFeedReader result = PROCESS_UNKNOWN_FORMAT; error = "Can't read document"; } - - xmlFreeDoc(document); } else { result = PROCESS_ERROR_INIT; } @@ -1101,9 +977,9 @@ bool p3FeedReaderThread::processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMs /* process description */ long todo; // encoding - htmlDocPtr document = htmlReadMemory(msg->description.c_str(), msg->description.length(), url.c_str(), "", HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | HTML_PARSE_COMPACT); - if (document) { - xmlNodePtr root = xmlDocGetRootElement(document); + HTMLWrapper html; + if (html.readHTML(msg->description.c_str(), url.c_str())) { + xmlNodePtr root = html.getRootElement(); if (root) { /* process all children */ std::list parents; @@ -1118,12 +994,12 @@ bool p3FeedReaderThread::processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMs if (node->type == XML_ELEMENT_NODE) { /* check for image */ - if (strcasecmp((char*) node->name, "img") == 0) { + if (xmlStrcasecmp(node->name, (const xmlChar*) "img") == 0) { bool removeImage = true; if (feed.flag & RS_FEED_FLAG_EMBED_IMAGES) { /* embed image */ - std::string src = xmlGetAttr(mCharEncodingHandler, node, "src"); + std::string src = html.getAttr(node, "src"); if (!src.empty()) { /* download image */ #ifdef FEEDREADER_DEBUG @@ -1139,7 +1015,7 @@ bool p3FeedReaderThread::processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMs if (toBase64(data, base64)) { std::string imageBase64; rs_sprintf(imageBase64, "data:%s;base64,%s", contentType.c_str(), base64.c_str()); - if (xmlSetAttr(mCharEncodingHandler, node, "src", imageBase64.c_str())) { + if (html.setAttr(node, "src", imageBase64.c_str())) { removeImage = false; } } @@ -1163,26 +1039,20 @@ bool p3FeedReaderThread::processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMs } } - xmlChar *html = NULL; - int htmlSize = 0; - htmlDocDumpMemoryFormat(document, &html, &htmlSize, 0); - if (html) { - convertToString((xmlCharEncodingHandlerPtr) mCharEncodingHandler, html, msg->description); - xmlFree(html); + if (isRunning()) { + if (!html.saveHTML(msg->description)) { +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl; +#endif + result = false; + } } else { #ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl; + std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") no root element" << std::endl; #endif result = false; } - } else { -#ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") no root element" << std::endl; -#endif - result = false; } - - xmlFreeDoc(document); } return result; diff --git a/plugins/FeedReader/services/p3FeedReaderThread.h b/plugins/FeedReader/services/p3FeedReaderThread.h index b405b8822..5da053fd0 100644 --- a/plugins/FeedReader/services/p3FeedReaderThread.h +++ b/plugins/FeedReader/services/p3FeedReaderThread.h @@ -43,8 +43,7 @@ public: DOWNLOAD_ERROR, DOWNLOAD_UNKNOWN_CONTENT_TYPE, DOWNLOAD_NOT_FOUND, - DOWNLOAD_UNKOWN_RESPONSE_CODE, - DOWNLOAD_INTERNAL_ERROR + DOWNLOAD_UNKOWN_RESPONSE_CODE }; enum ProcessResult { @@ -54,9 +53,11 @@ public: }; public: - p3FeedReaderThread(p3FeedReader *feedReader, Type type); + p3FeedReaderThread(p3FeedReader *feedReader, Type type, const std::string &feedId); virtual ~p3FeedReaderThread(); + std::string getFeedId() { return mFeedId; } + private: virtual void run(); @@ -68,7 +69,7 @@ private: p3FeedReader *mFeedReader; Type mType; - /*xmlCharEncodingHandlerPtr*/ void *mCharEncodingHandler; + std::string mFeedId; }; #endif diff --git a/plugins/FeedReader/services/rsFeedReaderItems.cc b/plugins/FeedReader/services/rsFeedReaderItems.cc index 8b6222994..2183ffcdd 100644 --- a/plugins/FeedReader/services/rsFeedReaderItems.cc +++ b/plugins/FeedReader/services/rsFeedReaderItems.cc @@ -50,6 +50,7 @@ void RsFeedReaderFeed::clear() errorState = RS_FEED_ERRORSTATE_OK; errorString.clear(); + preview = false; workstate = WAITING; content.clear(); } @@ -176,7 +177,9 @@ RsFeedReaderFeed *RsFeedReaderSerialiser::deserialiseFeed(void *data, uint32_t * ok &= getRawUInt32(data, rssize, &offset, &(item->storageTime)); ok &= getRawUInt32(data, rssize, &offset, &(item->flag)); ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->forumId); - ok &= getRawUInt32(data, rssize, &offset, &(item->errorState)); + uint32_t errorState = 0; + ok &= getRawUInt32(data, rssize, &offset, &errorState); + item->errorState = (RsFeedReaderErrorState) errorState; ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->errorString); if (offset != rssize) diff --git a/plugins/FeedReader/services/rsFeedReaderItems.h b/plugins/FeedReader/services/rsFeedReaderItems.h index 7f6703290..3a2e720c3 100644 --- a/plugins/FeedReader/services/rsFeedReaderItems.h +++ b/plugins/FeedReader/services/rsFeedReaderItems.h @@ -32,20 +32,6 @@ const uint8_t RS_PKT_SUBTYPE_FEEDREADER_MSG = 0x03; /**************************************************************************/ -#define RS_FEED_ERRORSTATE_OK 0 -#define RS_FEED_ERRORSTATE_DOWNLOAD_INTERNAL_ERROR 1 -#define RS_FEED_ERRORSTATE_DOWNLOAD_ERROR 2 -#define RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE 3 -#define RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND 4 -#define RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE 5 - -#define RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR 50 - -#define RS_FEED_ERRORSTATE_FORUM_CREATE 100 -#define RS_FEED_ERRORSTATE_FORUM_NOT_FOUND 101 -#define RS_FEED_ERRORSTATE_FORUM_NO_ADMIN 102 -#define RS_FEED_ERRORSTATE_FORUM_NO_ANONYMOUS_FORUM 103 - #define RS_FEED_FLAG_FOLDER 0x001 #define RS_FEED_FLAG_INFO_FROM_FEED 0x002 #define RS_FEED_FLAG_STANDARD_STORAGE_TIME 0x004 @@ -76,25 +62,26 @@ public: virtual void clear(); virtual std::ostream& print(std::ostream &out, uint16_t indent = 0); - std::string feedId; - std::string parentId; - std::string name; - std::string url; - std::string user; - std::string password; - std::string proxyAddress; - uint16_t proxyPort; - uint32_t updateInterval; - time_t lastUpdate; - uint32_t flag; // RS_FEED_FLAG_... - std::string forumId; - uint32_t storageTime; - std::string description; - std::string icon; - uint32_t errorState; - std::string errorString; + std::string feedId; + std::string parentId; + std::string name; + std::string url; + std::string user; + std::string password; + std::string proxyAddress; + uint16_t proxyPort; + uint32_t updateInterval; + time_t lastUpdate; + uint32_t flag; // RS_FEED_FLAG_... + std::string forumId; + uint32_t storageTime; + std::string description; + std::string icon; + RsFeedReaderErrorState errorState; + std::string errorString; /* Not Serialised */ + bool preview; WorkState workstate; std::string content; diff --git a/plugins/FeedReader/services/util/CURLWrapper.cc b/plugins/FeedReader/util/CURLWrapper.cpp similarity index 100% rename from plugins/FeedReader/services/util/CURLWrapper.cc rename to plugins/FeedReader/util/CURLWrapper.cpp diff --git a/plugins/FeedReader/services/util/CURLWrapper.h b/plugins/FeedReader/util/CURLWrapper.h similarity index 100% rename from plugins/FeedReader/services/util/CURLWrapper.h rename to plugins/FeedReader/util/CURLWrapper.h diff --git a/plugins/FeedReader/util/HTMLWrapper.cpp b/plugins/FeedReader/util/HTMLWrapper.cpp new file mode 100644 index 000000000..2722af72e --- /dev/null +++ b/plugins/FeedReader/util/HTMLWrapper.cpp @@ -0,0 +1,60 @@ +/**************************************************************** + * RetroShare GUI is distributed under the following license: + * + * Copyright (C) 2012 by Thunder + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#include + +#include "HTMLWrapper.h" +#include + +HTMLWrapper::HTMLWrapper() : XMLWrapper() +{ +} + +bool HTMLWrapper::readHTML(const char *html, const char *url) +{ + cleanup(); + + mDocument = htmlReadMemory(html, strlen(html), url, "", HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING/* | HTML_PARSE_COMPACT*/); + if (mDocument) { + return true; + } + + return false; +} + +bool HTMLWrapper::saveHTML(std::string &html) +{ + if (!mDocument) { + return false; + } + + xmlChar *newHtml = NULL; + int newHtmlSize = 0; + htmlDocDumpMemoryFormat(mDocument, &newHtml, &newHtmlSize, 0); + if (newHtml) { + convertToString(newHtml, html); + xmlFree(newHtml); + + return true; + } + + return false; +} diff --git a/plugins/FeedReader/util/HTMLWrapper.h b/plugins/FeedReader/util/HTMLWrapper.h new file mode 100644 index 000000000..e93552da8 --- /dev/null +++ b/plugins/FeedReader/util/HTMLWrapper.h @@ -0,0 +1,36 @@ +/**************************************************************** + * RetroShare GUI is distributed under the following license: + * + * Copyright (C) 2012 by Thunder + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#ifndef HTMLWRAPPER +#define HTMLWRAPPER + +#include "XMLWrapper.h" + +class HTMLWrapper : public XMLWrapper +{ +public: + HTMLWrapper(); + + bool readHTML(const char *html, const char *url); + bool saveHTML(std::string &html); +}; + +#endif diff --git a/plugins/FeedReader/util/XMLWrapper.cpp b/plugins/FeedReader/util/XMLWrapper.cpp new file mode 100644 index 000000000..ddb5a02fc --- /dev/null +++ b/plugins/FeedReader/util/XMLWrapper.cpp @@ -0,0 +1,245 @@ +/**************************************************************** + * RetroShare GUI is distributed under the following license: + * + * Copyright (C) 2012 by Thunder + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#include +#include + +#include "XMLWrapper.h" + +XMLWrapper::XMLWrapper() +{ + mDocument = NULL; + mCharEncodingHandler = xmlFindCharEncodingHandler ("UTF8"); + + if (!mCharEncodingHandler) { + /* no encoding handler found */ + std::cerr << "XMLWrapper::XMLWrapper - no encoding handler found" << std::endl; + } +} + +XMLWrapper::~XMLWrapper() +{ + cleanup(); + xmlCharEncCloseFunc(mCharEncodingHandler); +} + +void XMLWrapper::cleanup() +{ + if (mDocument) { + xmlFreeDoc(mDocument); + mDocument = NULL; + } +} + +bool XMLWrapper::convertToString(const xmlChar *xmlText, std::string &text) +{ + bool result = false; + + xmlBufferPtr in = xmlBufferCreateStatic((void*) xmlText, xmlStrlen(xmlText)); + xmlBufferPtr out = xmlBufferCreate(); + int ret = xmlCharEncOutFunc(mCharEncodingHandler, out, in); + if (ret >= 0) { + result = true; + text = (char*) xmlBufferContent(out); + } + + xmlBufferFree(in); + xmlBufferFree(out); + + return result; +} + +bool XMLWrapper::convertFromString(const char *text, xmlChar *&xmlText) +{ + bool result = false; + + xmlBufferPtr in = xmlBufferCreateStatic((void*) text, strlen(text)); + xmlBufferPtr out = xmlBufferCreate(); + int ret = xmlCharEncOutFunc(mCharEncodingHandler, out, in); + if (ret >= 0) { + result = true; + xmlText = xmlBufferDetach(out); + } + + xmlBufferFree(in); + xmlBufferFree(out); + + return result; +} + +xmlNodePtr XMLWrapper::getRootElement() +{ + if (mDocument) { + return xmlDocGetRootElement(mDocument); + } + + return NULL; +} + +bool XMLWrapper::readXML(const char *xml) +{ + cleanup(); + + mDocument = xmlReadDoc((const xmlChar*) xml, "", NULL, XML_PARSE_NOERROR | XML_PARSE_NOWARNING/* | XML_PARSE_COMPACT | XML_PARSE_NOCDATA*/); + if (mDocument) { + return true; + } + + return false; +} + +bool XMLWrapper::getContent(xmlNodePtr node, std::string &content) +{ + content.clear(); + + if (!node) { + return false; + } + + xmlChar *xmlContent = xmlNodeListGetString(mDocument, node, 1); + if (!xmlContent) { + return true; + } + + bool result = convertToString(xmlContent, content); + xmlFree(xmlContent); + + return result; +} + +std::string XMLWrapper::nodeName(xmlNodePtr node) +{ + std::string name; + + if (node) { + convertToString(node->name, name); + } + + return name; +} + +std::string XMLWrapper::attrName(xmlAttrPtr attr) +{ + std::string name; + + if (attr) { + convertToString(attr->name, name); + } + + return name; +} + +xmlNodePtr XMLWrapper::findNode(xmlNodePtr node, const char *name, bool children) +{ + if (node->name) { + if (xmlStrcasecmp(node->name, (xmlChar*) name) == 0) { + return node; + } + } + + xmlNodePtr nodeFound = NULL; + if (children) { + if (node->children) { + nodeFound = findNode(node->children, name, children); + if (nodeFound) { + return nodeFound; + } + } + } + + if (node->next) { + nodeFound = findNode(node->next, name, children); + if (nodeFound) { + return nodeFound; + } + } + + return NULL; +} + +bool XMLWrapper::getChildText(xmlNodePtr node, const char *childName, std::string &text) +{ + if (node == NULL || node->children == NULL) { + return false; + } + + xmlNodePtr child = findNode(node->children, childName, true); + if (!child) { + return false; + } + + if (child->type != XML_ELEMENT_NODE) { + return false; + } + + if (!child->children) { + return false; + } + + if (child->children->type != XML_TEXT_NODE) { + return false; + } + + if (child->children->content) { + return convertToString(child->children->content, text); + } + + return true; +} + +std::string XMLWrapper::getAttr(xmlNodePtr node, xmlAttrPtr attr) +{ + return getAttr(node, (const char*) attr->name); +} + +std::string XMLWrapper::getAttr(xmlNodePtr node, const char *name) +{ + if (!node || !name) { + return ""; + } + + std::string value; + + xmlChar *xmlValue = xmlGetProp(node, (const xmlChar*) name); + if (xmlValue) { + convertToString(xmlValue, value); + xmlFree(xmlValue); + } + + return value; +} + +bool XMLWrapper::setAttr(xmlNodePtr node, const char *name, const char *value) +{ + if (!node || !name) { + return false; + } + + xmlChar *xmlValue = NULL; + if (!convertFromString(value, xmlValue)) { + return false; + } + + xmlAttrPtr xmlAttr = xmlSetProp (node, (const xmlChar*) name, xmlValue); + xmlFree(xmlValue); + + return xmlAttr != NULL; +} diff --git a/plugins/FeedReader/util/XMLWrapper.h b/plugins/FeedReader/util/XMLWrapper.h new file mode 100644 index 000000000..8a896d44c --- /dev/null +++ b/plugins/FeedReader/util/XMLWrapper.h @@ -0,0 +1,60 @@ +/**************************************************************** + * RetroShare GUI is distributed under the following license: + * + * Copyright (C) 2012 by Thunder + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#ifndef XMLWRAPPER +#define XMLWRAPPER + +#include +#include + +class XMLWrapper +{ +public: + XMLWrapper(); + ~XMLWrapper(); + + void cleanup(); + + bool readXML(const char *xml); + + std::string nodeName(xmlNodePtr node); + std::string attrName(xmlAttrPtr attr); + + xmlNodePtr findNode(xmlNodePtr node, const char *name, bool children = false); + bool getChildText(xmlNodePtr node, const char *childName, std::string &text); + + bool getContent(xmlNodePtr node, std::string &content); + + std::string getAttr(xmlNodePtr node, xmlAttrPtr attr); + std::string getAttr(xmlNodePtr node, const char *name); + bool setAttr(xmlNodePtr node, const char *name, const char *value); + + xmlNodePtr getRootElement(); + +protected: + xmlDocPtr mDocument; + xmlCharEncodingHandlerPtr mCharEncodingHandler; + + bool convertToString(const xmlChar *xmlText, std::string &text); + bool convertFromString(const char *text, xmlChar *&xmlText); +}; + +#endif