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