FeedReader:

- Added error handling to xml functions
- Added xslt transformation
- Added retransform of existing messages
- Redesigned preview dialog
- Enabled embed images for forum feeds
- Changed config format, switching back to an older version results in a loss of all data of the FeedReader

Added new base class RSPlainTextEdit with placeholder text.
New library libxslt needed

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@6081 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
thunder2 2013-01-22 00:11:43 +00:00
parent 919fb3f62d
commit ef49000b9a
28 changed files with 1549 additions and 849 deletions

View File

@ -229,14 +229,11 @@ static int vasprintf(char **sptr, const char *fmt, va_list argv)
//}
#endif
int rs_sprintf(std::string &str, const char *fmt, ...)
int rs_sprintf_args(std::string &str, const char *fmt, va_list ap)
{
char *buffer = NULL;
va_list ap;
va_start(ap, fmt);
int retval = vasprintf(&buffer, fmt, ap);
va_end(ap);
int retval = vasprintf(&buffer, fmt, (va_list) ap);
if (retval >= 0) {
if (buffer) {
@ -252,15 +249,23 @@ int rs_sprintf(std::string &str, const char *fmt, ...)
return retval;
}
int rs_sprintf_append(std::string &str, const char *fmt, ...)
int rs_sprintf(std::string &str, const char *fmt, ...)
{
va_list ap;
char *buffer = NULL;
va_start(ap, fmt);
int retval = vasprintf(&buffer, fmt, ap);
int retval = rs_sprintf_args(str, fmt, ap);
va_end(ap);
return retval;
}
int rs_sprintf_append_args(std::string &str, const char *fmt, va_list ap)
{
char *buffer = NULL;
int retval = vasprintf(&buffer, fmt, (va_list) ap);
if (retval >= 0) {
if (buffer) {
str.append(buffer);
@ -271,6 +276,17 @@ int rs_sprintf_append(std::string &str, const char *fmt, ...)
return retval;
}
int rs_sprintf_append(std::string &str, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int retval = rs_sprintf_append_args(str, fmt, ap);
va_end(ap);
return retval;
}
void stringToUpperCase(const std::string& s, std::string &upper)
{
upper = s ;

View File

@ -15,7 +15,7 @@
*
* 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,
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
****************************************************************/
@ -23,6 +23,7 @@
#define RSSTRING_H_
#include <string>
#include <stdarg.h>
namespace librs { namespace util {
@ -39,7 +40,9 @@ bool ConvertUtf16ToUtf8(const std::wstring& source, std::string& dest);
#define UINT64FMT "%llu"
#endif
int rs_sprintf_args(std::string &str, const char *fmt, va_list ap);
int rs_sprintf(std::string &str, const char *fmt, ...);
int rs_sprintf_append_args(std::string &str, const char *fmt, va_list ap);
int rs_sprintf_append(std::string &str, const char *fmt, ...);
void stringToUpperCase(const std::string& s, std::string &upper);

View File

@ -51,8 +51,8 @@ FORMS = gui/FeedReaderDialog.ui \
TARGET = FeedReader
RESOURCES = gui/FeedReader_images.qrc \
lang/FeedReader_lang.qrc
lang/FeedReader_lang.qrc
TRANSLATIONS += \
lang/FeedReader_cs.ts \
lang/FeedReader_da.ts \
@ -76,17 +76,17 @@ linux-* {
INCLUDEPATH += $${LIBXML2_DIR}
LIBS += -lcurl -lxml2
LIBS += -lcurl -lxml2 -lxslt
}
win32 {
DEFINES += CURL_STATICLIB LIBXML_STATIC
DEFINES += CURL_STATICLIB LIBXML_STATIC LIBXSLT_STATIC LIBEXSLT_STATIC
CURL_DIR = ../../../curl-7.26.0
LIBXML2_DIR = ../../../libxml2-2.8.0
LIBICONV_DIR = ../../../libiconv-1.14
LIBXSLT_DIR = ../../../libxslt-1.1.28
INCLUDEPATH += $${CURL_DIR}/include $${LIBXML2_DIR}/include $${LIBICONV_DIR}/include
INCLUDEPATH += $${CURL_DIR}/include $${LIBXML2_DIR}/include $${LIBXSLT_DIR} $${LIBICONV_DIR}/include
LIBS += -lcurl -lxml2 -lws2_32 -lwldap32
LIBS += -lcurl -lxml2 -lxslt -lws2_32 -lwldap32
}

View File

@ -53,7 +53,6 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
/* currently only for loacl feeds */
connect(ui->saveCompletePageCheckBox, SIGNAL(toggled(bool)), this, SLOT(denyForumToggled()));
connect(ui->embedImagesCheckBox, SIGNAL(toggled(bool)), this, SLOT(denyForumToggled()));
connect(ui->urlLineEdit, SIGNAL(textChanged(QString)), this, SLOT(validate()));
connect(ui->nameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(validate()));
@ -78,6 +77,9 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
/* not yet supported */
ui->authenticationGroupBox->setEnabled(false);
mTransformationType = RS_FEED_TRANSFORMATION_TYPE_NONE;
ui->transformationTypeLabel->setText(FeedReaderStringDefs::transforationTypeString(mTransformationType));
/* fill own forums */
std::list<ForumInfo> forumList;
if (rsForums->getForumList(forumList)) {
@ -163,7 +165,7 @@ void AddFeedDialog::typeForumToggled()
void AddFeedDialog::denyForumToggled()
{
if (ui->saveCompletePageCheckBox->isChecked() || ui->embedImagesCheckBox->isChecked()) {
if (ui->saveCompletePageCheckBox->isChecked()) {
ui->typeForumRadio->setEnabled(false);
ui->typeLocalRadio->setChecked(true);
} else {
@ -230,7 +232,6 @@ bool AddFeedDialog::fillFeed(const std::string &feedId)
if (feedInfo.flag.forum) {
ui->typeForumRadio->setChecked(true);
ui->saveCompletePageCheckBox->setEnabled(false);
ui->embedImagesCheckBox->setEnabled(false);
if (feedInfo.forumId.empty()) {
ui->forumNameLabel->setText(tr("Not yet created"));
@ -263,8 +264,12 @@ bool AddFeedDialog::fillFeed(const std::string &feedId)
ui->useStandardStorageTimeCheckBox->setChecked(feedInfo.flag.standardStorageTime);
ui->storageTimeSpinBox->setValue(feedInfo.storageTime / (60 * 60 *24));
mTransformationType = feedInfo.transformationType;
mXPathsToUse = feedInfo.xpathsToUse;
mXPathsToRemove = feedInfo.xpathsToRemove;
mXslt = feedInfo.xslt;
ui->transformationTypeLabel->setText(FeedReaderStringDefs::transforationTypeString(mTransformationType));
}
return true;
@ -306,8 +311,10 @@ void AddFeedDialog::getFeedInfo(FeedInfo &feedInfo)
feedInfo.flag.standardStorageTime = ui->useStandardStorageTimeCheckBox->isChecked();
feedInfo.storageTime = ui->storageTimeSpinBox->value() * 60 *60 * 24;
feedInfo.transformationType = mTransformationType;
feedInfo.xpathsToUse = mXPathsToUse;
feedInfo.xpathsToRemove = mXPathsToRemove;
feedInfo.xslt = mXslt;
}
void AddFeedDialog::createFeed()
@ -345,6 +352,7 @@ void AddFeedDialog::preview()
PreviewFeedDialog dialog(mFeedReader, mNotify, feedInfo, this);
if (dialog.exec() == QDialog::Accepted) {
dialog.getXPaths(mXPathsToUse, mXPathsToRemove);
mTransformationType = dialog.getData(mXPathsToUse, mXPathsToRemove, mXslt);
ui->transformationTypeLabel->setText(FeedReaderStringDefs::transforationTypeString(mTransformationType));
}
}

View File

@ -63,8 +63,10 @@ private:
std::string mFeedId;
std::string mParentId;
RsFeedTransformationType mTransformationType;
std::list<std::string> mXPathsToUse;
std::list<std::string> mXPathsToRemove;
std::string mXslt;
Ui::AddFeedDialog *ui;
};

View File

@ -21,7 +21,16 @@
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -36,6 +45,61 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="3" column="0">
<widget class="QGroupBox" name="typeGroupBox">
<property name="title">
<string>Type</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="typeForumRadio">
<property name="text">
<string>Forum</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="forumComboBox"/>
</item>
<item>
<widget class="QLabel" name="forumNameLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">Forum name</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QRadioButton" name="typeLocalRadio">
<property name="text">
<string>Local Feed</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="7" column="0">
<widget class="QGroupBox" name="authenticationGroupBox">
<property name="title">
@ -76,63 +140,6 @@
</layout>
</widget>
</item>
<item row="8" column="0">
<widget class="QGroupBox" name="updateInteralGroupBox">
<property name="title">
<string>Update interval</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="useStandardUpdateInterval">
<property name="text">
<string>Use standard update interval</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="updateIntervalLabel">
<property name="text">
<string>Interval in minutes (0 = manual)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="updateIntervalSpinBox">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="lastUpdateLayout">
<item>
<widget class="QLabel" name="lastUpdateLabel">
<property name="text">
<string>Last update</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lastUpdate">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Never</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="7" column="1">
<widget class="QGroupBox" name="storageTimeGroupBox">
<property name="title">
@ -209,47 +216,47 @@
</layout>
</widget>
</item>
<item row="11" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="previewButton">
<property name="text">
<string>Preview</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="typeGroupBox">
<item row="8" column="0">
<widget class="QGroupBox" name="updateInteralGroupBox">
<property name="title">
<string>Type</string>
<string>Update interval</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="margin">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="useStandardUpdateInterval">
<property name="text">
<string>Use standard update interval</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="updateIntervalLabel">
<property name="text">
<string>Interval in minutes (0 = manual)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="updateIntervalSpinBox">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="lastUpdateLayout">
<item>
<widget class="QRadioButton" name="typeForumRadio">
<widget class="QLabel" name="lastUpdateLabel">
<property name="text">
<string>Forum</string>
<string>Last update</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="forumComboBox"/>
</item>
<item>
<widget class="QLabel" name="forumNameLabel">
<widget class="QLabel" name="lastUpdate">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -257,78 +264,25 @@
</sizepolicy>
</property>
<property name="text">
<string notr="true">Forum name</string>
<string>Never</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QRadioButton" name="typeLocalRadio">
<property name="text">
<string>Local Feed</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="3" column="1">
<widget class="QGroupBox" name="flagGroupBox">
<property name="title">
<string>Misc</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="activatedCheckBox">
<property name="text">
<string>Activated</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useInfoFromFeedCheckBox">
<property name="text">
<string>Use name and description from feed</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="updateForumInfoCheckBox">
<property name="text">
<string>Update forum information</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="embedImagesCheckBox">
<property name="text">
<string>Embed images (experimental for local feeds)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="saveCompletePageCheckBox">
<property name="text">
<string>Save complete web page (experimental for local feeds)</string>
</property>
</widget>
</item>
</layout>
</widget>
<item row="11" column="0" colspan="2">
<layout class="QHBoxLayout" name="buttonLayout">
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<layout class="QVBoxLayout" name="descriptionLayout">
@ -368,6 +322,83 @@
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Transformation</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="transformationTypeLabel">
<property name="text">
<string>Transformation type</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="previewButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Preview &amp;&amp; Transformation</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="3" column="1" rowspan="2">
<widget class="QGroupBox" name="flagGroupBox">
<property name="title">
<string>Misc</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="activatedCheckBox">
<property name="text">
<string>Activated</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useInfoFromFeedCheckBox">
<property name="text">
<string>Use name and description from feed</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="updateForumInfoCheckBox">
<property name="text">
<string>Update forum information</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="embedImagesCheckBox">
<property name="text">
<string>Embed images</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="saveCompletePageCheckBox">
<property name="text">
<string>Save complete web page (experimental for local feeds)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -67,7 +67,7 @@ FeedReaderFeedItem::FeedReaderFeedItem(RsFeedReader *feedReader, FeedReaderNotif
ui->titleLabel->setText(QString::fromUtf8(feedInfo.name.c_str()));
ui->msgTitleLabel->setText(QString::fromUtf8(msgInfo.title.c_str()));
ui->descriptionLabel->setText(QString::fromUtf8(msgInfo.description.c_str()));
ui->descriptionLabel->setText(QString::fromUtf8((msgInfo.descriptionTransformed.empty() ? msgInfo.description : msgInfo.descriptionTransformed).c_str()));
ui->dateTimeLabel->setText(DateTime::formatLongDateTime(msgInfo.pubDate));

View File

@ -219,7 +219,16 @@ void FeedReaderMessageWidget::setFeedId(const std::string &feedId)
mFeedInfo = FeedInfo();
}
ui->msgReadAllButton->setEnabled(!mFeedId.empty());
if (mFeedId.empty()) {
ui->msgReadAllButton->setEnabled(false);
ui->msgTreeWidget->setPlaceholderText("");
} else {
if (mFeedInfo.flag.forum) {
ui->msgTreeWidget->setPlaceholderText(tr("The messages will be added to the forum"));
} else {
ui->msgTreeWidget->setPlaceholderText("");
}
}
updateMsgs();
updateCurrentMessage();
@ -307,6 +316,11 @@ void FeedReaderMessageWidget::msgTreeCustomPopupMenu(QPoint /*point*/)
action = contextMnu.addAction(QIcon(""), tr("Remove"), this, SLOT(removeMsg()));
action->setEnabled(!selectedItems.empty());
contextMnu.addSeparator();
action = contextMnu.addAction(QIcon(""), tr("Retransform"), this, SLOT(retransformMsg()));
action->setEnabled((mFeedInfo.transformationType != RS_FEED_TRANSFORMATION_TYPE_NONE) && !selectedItems.empty());
contextMnu.exec(QCursor::pos());
}
@ -495,6 +509,12 @@ void FeedReaderMessageWidget::msgChanged(const QString &feedId, const QString &m
}
}
if (type == NOTIFY_TYPE_MOD) {
if (msgId.toStdString() == currentMsgId()) {
updateCurrentMessage();
}
}
if (type == NOTIFY_TYPE_ADD) {
QTreeWidgetItem *item = new RSTreeWidgetItem(mMsgCompareRole);
updateMsgItem(item, msgInfo);
@ -591,7 +611,7 @@ void FeedReaderMessageWidget::updateCurrentMessage()
setMsgAsReadUnread(row, setToReadOnActive);
}
QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8(msgInfo.description.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS);
QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8((msgInfo.descriptionTransformed.empty() ? msgInfo.description : msgInfo.descriptionTransformed).c_str()), RSHTML_FORMATTEXT_EMBED_LINKS);
ui->msgText->setHtml(msgTxt);
ui->msgTitle->setText(QString::fromUtf8(msgInfo.title.c_str()));
@ -744,6 +764,20 @@ void FeedReaderMessageWidget::removeMsg()
mFeedReader->removeMsgs(mFeedId, msgIds);
}
void FeedReaderMessageWidget::retransformMsg()
{
if (mFeedId.empty()) {
return;
}
QList<QTreeWidgetItem*> selectedItems = ui->msgTreeWidget->selectedItems();
QList<QTreeWidgetItem*>::iterator it;
for (it = selectedItems.begin(); it != selectedItems.end(); ++it) {
mFeedReader->retransformMsg(mFeedId, (*it)->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString());
}
}
void FeedReaderMessageWidget::processFeed()
{
if (mFeedId.empty()) {

View File

@ -52,6 +52,7 @@ private slots:
void processFeed();
void openLinkMsg();
void copyLinkMsg();
void retransformMsg();
/* FeedReaderNotify */
void feedChanged(const QString &feedId, int type);

View File

@ -17,7 +17,16 @@
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -36,7 +45,16 @@
<enum>QFrame::Sunken</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="margin">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
@ -138,7 +156,7 @@
</widget>
</item>
<item row="3" column="0">
<widget class="QTreeWidget" name="msgTreeWidget">
<widget class="RSTreeWidget" name="msgTreeWidget">
<property name="font">
<font>
<pointsize>9</pointsize>
@ -278,6 +296,11 @@
<extends>QLineEdit</extends>
<header location="global">gui/common/LineEditClear.h</header>
</customwidget>
<customwidget>
<class>RSTreeWidget</class>
<extends>QTreeWidget</extends>
<header>gui/common/RSTreeWidget.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="FeedReader_images.qrc"/>

View File

@ -71,7 +71,7 @@ QString FeedReaderStringDefs::workState(FeedInfo::WorkState state)
return QApplication::translate("FeedReaderStringDefs", "Processing");
}
return "";
return QApplication::translate("FeedReaderStringDefs", "Unknown");
}
QString FeedReaderStringDefs::errorString(const FeedInfo &feedInfo)
@ -135,6 +135,15 @@ QString FeedReaderStringDefs::errorString(RsFeedReaderErrorState errorState, con
case RS_FEED_ERRORSTATE_PROCESS_XPATH_NO_RESULT:
errorText = QApplication::translate("FeedReaderStringDefs", "Empty XPath result");
break;
case RS_FEED_ERRORSTATE_PROCESS_XSLT_FORMAT_ERROR:
errorText = QApplication::translate("FeedReaderStringDefs", "XSLT format error");
break;
case RS_FEED_ERRORSTATE_PROCESS_XSLT_TRANSFORM_ERROR:
errorText = QApplication::translate("FeedReaderStringDefs", "XSLT transformation error");
break;
case RS_FEED_ERRORSTATE_PROCESS_XSLT_NO_RESULT:
errorText = QApplication::translate("FeedReaderStringDefs", "Empty XSLT result");
break;
default:
errorText = QApplication::translate("FeedReaderStringDefs", "Unknown error");
@ -146,3 +155,17 @@ QString FeedReaderStringDefs::errorString(RsFeedReaderErrorState errorState, con
return errorText;
}
QString FeedReaderStringDefs::transforationTypeString(RsFeedTransformationType transformationType)
{
switch (transformationType) {
case RS_FEED_TRANSFORMATION_TYPE_NONE:
return QApplication::translate("FeedReaderStringDefs", "No transformation");
case RS_FEED_TRANSFORMATION_TYPE_XPATH:
return QApplication::translate("FeedReaderStringDefs", "XPath");
case RS_FEED_TRANSFORMATION_TYPE_XSLT:
return QApplication::translate("FeedReaderStringDefs", "XSLT");
}
return QApplication::translate("FeedReaderStringDefs", "Unknown");
}

View File

@ -35,6 +35,7 @@ public:
static QString workState(FeedInfo::WorkState state);
static QString errorString(const FeedInfo &feedInfo);
static QString errorString(RsFeedReaderErrorState errorState, const std::string &errorString);
static QString transforationTypeString(RsFeedTransformationType transformationType);
};
#endif

View File

@ -30,7 +30,6 @@
#include "util/HandleRichText.h"
#include "gui/settings/rsharesettings.h"
#include "interface/rsFeedReader.h"
#include "retroshare/rsiface.h"
#include "util/HTMLWrapper.h"
@ -146,35 +145,44 @@ PreviewFeedDialog::PreviewFeedDialog(RsFeedReader *feedReader, FeedReaderNotify
ui->setupUi(this);
ui->feedNameLabel->clear();
ui->useXPathCheckBox->setChecked(true);
/* connect signals */
connect(ui->previousPushButton, SIGNAL(clicked()), this, SLOT(previousMsg()));
connect(ui->nextPushButton, SIGNAL(clicked()), this, SLOT(nextMsg()));
connect(ui->closeStructureButton, SIGNAL(clicked()), this, SLOT(showStructureFrame()));
connect(ui->structureButton, SIGNAL(toggled(bool)), this, SLOT(showStructureFrame(bool)));
connect(ui->xpathPushButton, SIGNAL(toggled(bool)), this, SLOT(showXPathFrame(bool)));
connect(ui->useXPathCheckBox, SIGNAL(toggled(bool)), this, SLOT(fillStructureTree()));
connect(ui->structureButton, SIGNAL(toggled(bool)), this, SLOT(showStructureFrame()));
connect(ui->xpathUseListWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(xpathListCustomPopupMenu(QPoint)));
connect(ui->xpathRemoveListWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(xpathListCustomPopupMenu(QPoint)));
connect(ui->xpathUseListWidget->itemDelegate(), SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), this, SLOT(xpathCloseEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
connect(ui->xpathRemoveListWidget->itemDelegate(), SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), this, SLOT(xpathCloseEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
connect(ui->transformationTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(transformationTypeChanged()));
connect(mNotify, SIGNAL(feedChanged(QString,int)), this, SLOT(feedChanged(QString,int)));
connect(mNotify, SIGNAL(msgChanged(QString,QString,int)), this, SLOT(msgChanged(QString,QString,int)));
ui->transformationTypeComboBox->addItem(FeedReaderStringDefs::transforationTypeString(RS_FEED_TRANSFORMATION_TYPE_NONE), RS_FEED_TRANSFORMATION_TYPE_NONE);
ui->transformationTypeComboBox->addItem(FeedReaderStringDefs::transforationTypeString(RS_FEED_TRANSFORMATION_TYPE_XPATH), RS_FEED_TRANSFORMATION_TYPE_XPATH);
ui->transformationTypeComboBox->addItem(FeedReaderStringDefs::transforationTypeString(RS_FEED_TRANSFORMATION_TYPE_XSLT), RS_FEED_TRANSFORMATION_TYPE_XSLT);
ui->xsltTextEdit->setPlaceholderText(tr("XSLT is used on focus lost or when Ctrl+Enter is pressed"));
// ui->documentTreeWidget->setItemDelegate(new PreviewItemDelegate(ui->documentTreeWidget));
ui->structureFrame->hide();
showStructureFrame();
/* Set initial size the splitter */
// QList<int> sizes;
// sizes << 300 << 300 << 150; // Qt calculates the right sizes
// ui->splitter->setSizes(sizes);
if (mFeedReader->addPreviewFeed(feedInfo, mFeedId)) {
setFeedInfo("");
} else {
setFeedInfo(tr("Cannot create preview"));
}
setXPathInfo("");
showXPathFrame(true);
setTransformationInfo("");
/* fill xpath/xslt expressions */
ui->transformationTypeComboBox->setCurrentIndex(ui->transformationTypeComboBox->findData(feedInfo.transformationType));
/* fill xpath expressions */
QListWidgetItem *item;
std::string xpath;
foreach(xpath, feedInfo.xpathsToUse){
@ -188,12 +196,15 @@ PreviewFeedDialog::PreviewFeedDialog(RsFeedReader *feedReader, FeedReaderNotify
ui->xpathRemoveListWidget->addItem(item);
}
ui->xsltTextEdit->setPlainText(QString::fromUtf8(feedInfo.xslt.c_str()));
updateMsgCount();
ui->xpathUseListWidget->installEventFilter(this);
ui->xpathUseListWidget->viewport()->installEventFilter(this);
ui->xpathRemoveListWidget->installEventFilter(this);
ui->xpathRemoveListWidget->viewport()->installEventFilter(this);
ui->xsltTextEdit->installEventFilter(this);
/* load settings */
processSettings(true);
@ -234,8 +245,6 @@ void PreviewFeedDialog::processSettings(bool load)
bool PreviewFeedDialog::eventFilter(QObject *obj, QEvent *event)
{
long todo_here;
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent) {
@ -247,16 +256,28 @@ bool PreviewFeedDialog::eventFilter(QObject *obj, QEvent *event)
QListWidgetItem *item = listWidget->currentItem();
if (item) {
delete(item);
processXPath();
processTransformation();
}
return true; // eat event
}
}
}
if ((keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) && (keyEvent->modifiers() & Qt::ControlModifier)) {
/* Ctrl+Enter pressed */
if (obj == ui->xsltTextEdit) {
processTransformation();
return true; // eat event
}
}
}
}
if (event->type() == QEvent::Drop) {
processXPath();
processTransformation();
}
if (event->type() == QEvent::FocusOut) {
if (obj == ui->xsltTextEdit) {
processTransformation();
}
}
/* pass the event on to the parent class */
return QDialog::eventFilter(obj, event);
@ -338,27 +359,51 @@ void PreviewFeedDialog::msgChanged(const QString &feedId, const QString &msgId,
updateMsgCount();
}
void PreviewFeedDialog::showStructureFrame(bool show)
void PreviewFeedDialog::showStructureFrame()
{
ui->structureButton->setChecked(show);
ui->structureFrame->setVisible(show);
bool show = ui->structureButton->isChecked();
RsFeedTransformationType transformationType = (RsFeedTransformationType) ui->transformationTypeComboBox->itemData(ui->transformationTypeComboBox->currentIndex()).toInt();
if (show) {
fillStructureTree();
ui->structureTreeFrame->setVisible(show);
switch (transformationType) {
case RS_FEED_TRANSFORMATION_TYPE_NONE:
ui->msgTextOrg->hide();
ui->structureTreeWidgetOrg->hide();
ui->transformationFrame->hide();
ui->xpathFrame->hide();
ui->xsltFrame->hide();
break;
case RS_FEED_TRANSFORMATION_TYPE_XPATH:
ui->msgTextOrg->setVisible(show);
ui->structureTreeWidgetOrg->show();
ui->transformationFrame->setVisible(show);
ui->xpathFrame->show();
ui->xsltFrame->hide();
break;
case RS_FEED_TRANSFORMATION_TYPE_XSLT:
ui->msgTextOrg->setVisible(show);
ui->structureTreeWidgetOrg->show();
ui->transformationFrame->setVisible(show);
ui->xpathFrame->hide();
ui->xsltFrame->show();
break;
}
if (ui->msgTextOrg->isVisible()) {
QString msgTxt = RsHtml().formatText(ui->msgTextOrg->document(), QString::fromUtf8(mDescription.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS);
ui->msgTextOrg->setHtml(msgTxt);
} else {
ui->msgTextOrg->clear();
}
fillStructureTree(false);
fillStructureTree(true);
}
void PreviewFeedDialog::showXPathFrame(bool show)
void PreviewFeedDialog::transformationTypeChanged()
{
ui->xpathFrame->setVisible(show);
if (show) {
ui->xpathPushButton->setToolTip(tr("Hide XPath expressions"));
ui->xpathPushButton->setIcon(QIcon(":images/show_toolbox_frame.png"));
} else {
ui->xpathPushButton->setToolTip(tr("Show XPath expressions"));
ui->xpathPushButton->setIcon(QIcon(":images/hide_toolbox_frame.png"));
}
showStructureFrame();
processTransformation();
}
void PreviewFeedDialog::xpathListCustomPopupMenu(QPoint /*point*/)
@ -396,7 +441,7 @@ void PreviewFeedDialog::xpathListCustomPopupMenu(QPoint /*point*/)
void PreviewFeedDialog::xpathCloseEditor(QWidget */*editor*/, QAbstractItemDelegate::EndEditHint /*hint*/)
{
processXPath();
processTransformation();
}
void PreviewFeedDialog::addXPath()
@ -468,7 +513,7 @@ void PreviewFeedDialog::removeXPath()
delete(item);
}
processXPath();
processTransformation();
}
int PreviewFeedDialog::getMsgPos()
@ -491,10 +536,10 @@ void PreviewFeedDialog::setFeedInfo(const QString &info)
ui->feedInfoLabel->setVisible(!info.isEmpty());
}
void PreviewFeedDialog::setXPathInfo(const QString &info)
void PreviewFeedDialog::setTransformationInfo(const QString &info)
{
ui->xpathInfoLabel->setText(info);
ui->xpathInfoLabel->setVisible(!info.isEmpty());
ui->transformationInfoLabel->setText(info);
ui->transformationInfoLabel->setVisible(!info.isEmpty());
}
void PreviewFeedDialog::fillFeedInfo(const FeedInfo &feedInfo)
@ -551,8 +596,9 @@ void PreviewFeedDialog::updateMsg()
if (mMsgId.empty() || !mFeedReader->getMsgInfo(mFeedId, mMsgId, msgInfo)) {
ui->msgTitle->clear();
ui->msgText->clear();
ui->msgTextOrg->clear();
mDescription.clear();
mDescriptionXPath.clear();
mDescriptionTransformed.clear();
return;
}
@ -561,8 +607,15 @@ void PreviewFeedDialog::updateMsg()
/* store description */
mDescription = msgInfo.description;
if (ui->msgTextOrg->isVisible()) {
QString msgTxt = RsHtml().formatText(ui->msgTextOrg->document(), QString::fromUtf8(mDescription.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS);
ui->msgTextOrg->setHtml(msgTxt);
}
showStructureFrame();
/* process xpath */
processXPath();
processTransformation();
}
static void buildNodeText(HTMLWrapper &html, xmlNodePtr node, QString &text)
@ -699,44 +752,52 @@ static void examineChildElements(QTreeWidget *treeWidget, HTMLWrapper &html, QLi
// treeWidget->setItemWidget(item, 0, label);
}
void PreviewFeedDialog::fillStructureTree()
void PreviewFeedDialog::fillStructureTree(bool transform)
{
if (!ui->structureTreeWidget->isVisible()) {
return;
if (transform && ui->structureTreeWidget->isVisible()) {
if (mDescriptionTransformed.empty()) {
ui->structureTreeWidget->clear();
} else {
HTMLWrapper html;
if (html.readHTML(mDescriptionTransformed.c_str(), "")) {
xmlNodePtr root = html.getRootElement();
if (root) {
QList<xmlNodePtr> nodes;
nodes.push_back(root);
examineChildElements(ui->structureTreeWidget, html, nodes, ui->structureTreeWidget->invisibleRootItem());
ui->structureTreeWidget->resizeColumnToContents(0);
}
} else {
QTreeWidgetItem *item = new QTreeWidgetItem;
item->setText(0, tr("Error parsing document") + ": " + QString::fromUtf8(html.lastError().c_str()));
ui->structureTreeWidget->addTopLevelItem(item);
}
}
}
// if (ui->structureTreeWidget->topLevelItemCount() > 0) {
// return;
// }
if (mDescriptionXPath.empty()) {
ui->structureTreeWidget->clear();
return;
if (!transform && ui->structureTreeWidgetOrg->isVisible()) {
if (mDescription.empty()) {
ui->structureTreeWidgetOrg->clear();
} else {
HTMLWrapper html;
if (html.readHTML(mDescription.c_str(), "")) {
xmlNodePtr root = html.getRootElement();
if (root) {
QList<xmlNodePtr> nodes;
nodes.push_back(root);
examineChildElements(ui->structureTreeWidgetOrg, html, nodes, ui->structureTreeWidgetOrg->invisibleRootItem());
ui->structureTreeWidgetOrg->resizeColumnToContents(0);
}
} else {
QTreeWidgetItem *item = new QTreeWidgetItem;
item->setText(0, tr("Error parsing document") + ": " + QString::fromUtf8(html.lastError().c_str()));
ui->structureTreeWidgetOrg->addTopLevelItem(item);
}
}
}
bool useXPath = ui->useXPathCheckBox->isChecked();
HTMLWrapper html;
if (!html.readHTML(useXPath ? mDescriptionXPath.c_str() : mDescription.c_str(), "")) {
QTreeWidgetItem *item = new QTreeWidgetItem;
item->setText(0, tr("Error parsing document"));
ui->structureTreeWidget->addTopLevelItem(item);
return;
}
xmlNodePtr root = html.getRootElement();
if (!root) {
return;
}
QList<xmlNodePtr> nodes;
nodes.push_back(root);
examineChildElements(ui->structureTreeWidget, html, nodes, ui->structureTreeWidget->invisibleRootItem());
ui->structureTreeWidget->resizeColumnToContents(0);
}
void PreviewFeedDialog::getXPaths(std::list<std::string> &xpathsToUse, std::list<std::string> &xpathsToRemove)
RsFeedTransformationType PreviewFeedDialog::getData(std::list<std::string> &xpathsToUse, std::list<std::string> &xpathsToRemove, std::string &xslt)
{
xpathsToUse.clear();
xpathsToRemove.clear();
@ -751,24 +812,40 @@ void PreviewFeedDialog::getXPaths(std::list<std::string> &xpathsToUse, std::list
for (row = 0; row < rowCount; ++row) {
xpathsToRemove.push_back(ui->xpathRemoveListWidget->item(row)->text().toUtf8().constData());
}
xslt = ui->xsltTextEdit->toPlainText().toUtf8().constData();
return (RsFeedTransformationType) ui->transformationTypeComboBox->itemData(ui->transformationTypeComboBox->currentIndex()).toInt();
}
void PreviewFeedDialog::processXPath()
void PreviewFeedDialog::processTransformation()
{
std::list<std::string> xpathsToUse;
std::list<std::string> xpathsToRemove;
std::string xslt;
getXPaths(xpathsToUse, xpathsToRemove);
RsFeedTransformationType transformationType = getData(xpathsToUse, xpathsToRemove, xslt);
mDescriptionXPath = mDescription;
mDescriptionTransformed = mDescription;
std::string errorString;
RsFeedReaderErrorState result = mFeedReader->processXPath(xpathsToUse, xpathsToRemove, mDescriptionXPath, errorString);
setXPathInfo(FeedReaderStringDefs::errorString(result, errorString));
RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK;
switch (transformationType) {
case RS_FEED_TRANSFORMATION_TYPE_NONE:
break;
case RS_FEED_TRANSFORMATION_TYPE_XPATH:
result = mFeedReader->processXPath(xpathsToUse, xpathsToRemove, mDescriptionTransformed, errorString);
break;
case RS_FEED_TRANSFORMATION_TYPE_XSLT:
result = mFeedReader->processXslt(xslt, mDescriptionTransformed, errorString);
break;
}
setTransformationInfo(FeedReaderStringDefs::errorString(result, errorString));
/* fill message */
QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8(mDescriptionXPath.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS);
QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8(mDescriptionTransformed.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS);
ui->msgText->setHtml(msgTxt);
/* fill structure */
fillStructureTree();
fillStructureTree(true);
}

View File

@ -25,6 +25,8 @@
#include <QDialog>
#include <QItemDelegate>
#include "interface/rsFeedReader.h"
namespace Ui {
class PreviewFeedDialog;
}
@ -54,12 +56,12 @@ class FeedInfo;
class PreviewFeedDialog : public QDialog
{
Q_OBJECT
public:
PreviewFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, const FeedInfo &feedInfo, QWidget *parent = 0);
~PreviewFeedDialog();
void getXPaths(std::list<std::string> &xpathsToUse, std::list<std::string> &xpathsToRemove);
RsFeedTransformationType getData(std::list<std::string> &xpathsToUse, std::list<std::string> &xpathsToRemove, std::string &xslt);
protected:
bool eventFilter(QObject *obj, QEvent *ev);
@ -67,14 +69,13 @@ protected:
private slots:
void previousMsg();
void nextMsg();
void showStructureFrame(bool show = false);
void showXPathFrame(bool show);
void showStructureFrame();
void xpathListCustomPopupMenu(QPoint point);
void xpathCloseEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint);
void addXPath();
void editXPath();
void removeXPath();
void fillStructureTree();
void transformationTypeChanged();
/* FeedReaderNotify */
void feedChanged(const QString &feedId, int type);
@ -84,11 +85,12 @@ private:
void processSettings(bool load);
int getMsgPos();
void setFeedInfo(const QString &info);
void setXPathInfo(const QString &info);
void setTransformationInfo(const QString &info);
void fillFeedInfo(const FeedInfo &feedInfo);
void updateMsgCount();
void updateMsg();
void processXPath();
void fillStructureTree(bool transform);
void processTransformation();
RsFeedReader *mFeedReader;
FeedReaderNotify *mNotify;
@ -96,7 +98,7 @@ private:
std::string mMsgId;
std::list<std::string> mMsgIds;
std::string mDescription;
std::string mDescriptionXPath;
std::string mDescriptionTransformed;
Ui::PreviewFeedDialog *ui;
};

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,9 @@
#include <string>
#include <list>
class RsFeedReader;
extern RsFeedReader *rsFeedReader;
enum RsFeedReaderErrorState {
RS_FEED_ERRORSTATE_OK = 0,
@ -47,13 +50,12 @@ enum RsFeedReaderErrorState {
RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR = 150,
RS_FEED_ERRORSTATE_PROCESS_XPATH_INTERNAL_ERROR = 151,
RS_FEED_ERRORSTATE_PROCESS_XPATH_WRONG_EXPRESSION = 152,
RS_FEED_ERRORSTATE_PROCESS_XPATH_NO_RESULT = 153
RS_FEED_ERRORSTATE_PROCESS_XPATH_NO_RESULT = 153,
RS_FEED_ERRORSTATE_PROCESS_XSLT_FORMAT_ERROR = 154,
RS_FEED_ERRORSTATE_PROCESS_XSLT_TRANSFORM_ERROR = 155,
RS_FEED_ERRORSTATE_PROCESS_XSLT_NO_RESULT = 156
};
class RsFeedReader;
extern RsFeedReader *rsFeedReader;
enum RsFeedAddResult
{
RS_FEED_ADD_RESULT_SUCCESS,
@ -64,6 +66,13 @@ enum RsFeedAddResult
RS_FEED_ADD_RESULT_FEED_IS_NO_FOLDER
};
enum RsFeedTransformationType
{
RS_FEED_TRANSFORMATION_TYPE_NONE = 0,
RS_FEED_TRANSFORMATION_TYPE_XPATH = 1,
RS_FEED_TRANSFORMATION_TYPE_XSLT = 2
};
class FeedInfo
{
public:
@ -96,28 +105,31 @@ public:
flag.embedImages = false;
flag.saveCompletePage = false;
flag.preview = false;
transformationType = RS_FEED_TRANSFORMATION_TYPE_NONE;
}
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;
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;
std::list<std::string> xpathsToUse;
std::list<std::string> xpathsToRemove;
RsFeedTransformationType transformationType;
std::list<std::string> xpathsToUse;
std::list<std::string> xpathsToRemove;
std::string xslt;
struct {
bool folder : 1;
@ -152,6 +164,7 @@ public:
std::string link;
std::string author;
std::string description;
std::string descriptionTransformed;
time_t pubDate;
struct {
@ -202,8 +215,10 @@ public:
virtual bool getFeedMsgIdList(const std::string &feedId, std::list<std::string> &msgIds) = 0;
virtual bool processFeed(const std::string &feedId) = 0;
virtual bool setMessageRead(const std::string &feedId, const std::string &msgId, bool read) = 0;
virtual bool retransformMsg(const std::string &feedId, const std::string &msgId) = 0;
virtual RsFeedReaderErrorState processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, std::string &description, std::string &errorString) = 0;
virtual RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString) = 0;
};
#endif

View File

@ -88,8 +88,10 @@ static void feedToInfo(const RsFeedReaderFeed *feed, FeedInfo &info)
info.errorState = feed->errorState;
info.errorString = feed->errorString;
info.transformationType = feed->transformationType;
info.xpathsToUse = feed->xpathsToUse.ids;
info.xpathsToRemove = feed->xpathsToRemove.ids;
info.xslt = feed->xslt;
info.flag.folder = (feed->flag & RS_FEED_FLAG_FOLDER);
info.flag.infoFromFeed = (feed->flag & RS_FEED_FLAG_INFO_FROM_FEED);
@ -144,8 +146,10 @@ static void infoToFeed(const FeedInfo &info, RsFeedReaderFeed *feed, bool add)
feed->forumId = info.forumId;
}
feed->transformationType = info.transformationType;
feed->xpathsToUse.ids = info.xpathsToUse;
feed->xpathsToRemove.ids = info.xpathsToRemove;
feed->xslt = info.xslt;
// feed->preview = info.flag.preview;
@ -200,6 +204,7 @@ static void feedMsgToInfo(const RsFeedReaderMsg *msg, FeedMsgInfo &info)
info.link = msg->link;
info.author = msg->author;
info.description = msg->description;
info.descriptionTransformed = msg->descriptionTransformed;
info.pubDate = msg->pubDate;
info.flag.isnew = (msg->flag & RS_FEEDMSG_FLAG_NEW);
@ -491,6 +496,7 @@ RsFeedAddResult p3FeedReader::setFeed(const std::string &feedId, const FeedInfo
forumId = fi->forumId;
librs::util::ConvertUtf8ToUtf16(fi->name, forumInfo.forumName);
librs::util::ConvertUtf8ToUtf16(fi->description, forumInfo.forumDesc);
forumInfo.forumName.insert(0, FEEDREADER_FORUM_PREFIX);
}
}
@ -759,8 +765,11 @@ bool p3FeedReader::removeMsg(const std::string &feedId, const std::string &msgId
return false;
}
msgIt->second->flag |= RS_FEEDMSG_FLAG_DELETED | RS_FEEDMSG_FLAG_READ;
msgIt->second->flag &= ~RS_FEEDMSG_FLAG_NEW;
RsFeedReaderMsg *mi = msgIt->second;
mi->flag |= RS_FEEDMSG_FLAG_DELETED | RS_FEEDMSG_FLAG_READ;
mi->flag &= ~RS_FEEDMSG_FLAG_NEW;
mi->description.clear();
mi->descriptionTransformed.clear();
}
if (changed) {
@ -805,8 +814,11 @@ bool p3FeedReader::removeMsgs(const std::string &feedId, const std::list<std::st
continue;
}
msgIt->second->flag |= RS_FEEDMSG_FLAG_DELETED | RS_FEEDMSG_FLAG_READ;
msgIt->second->flag &= ~RS_FEEDMSG_FLAG_NEW;
RsFeedReaderMsg *mi = msgIt->second;
mi->flag |= RS_FEEDMSG_FLAG_DELETED | RS_FEEDMSG_FLAG_READ;
mi->flag &= ~RS_FEEDMSG_FLAG_NEW;
mi->description.clear();
mi->descriptionTransformed.clear();
removedMsgs.push_back(*idIt);
}
@ -1121,11 +1133,74 @@ bool p3FeedReader::setMessageRead(const std::string &feedId, const std::string &
return true;
}
bool p3FeedReader::retransformMsg(const std::string &feedId, const std::string &msgId)
{
bool msgChanged = false;
bool feedChanged = false;
{
RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/
std::map<std::string, RsFeedReaderFeed*>::iterator feedIt = mFeeds.find(feedId);
if (feedIt == mFeeds.end()) {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::setMessageRead - feed " << feedId << " not found" << std::endl;
#endif
return false;
}
RsFeedReaderFeed *fi = feedIt->second;
std::map<std::string, RsFeedReaderMsg*>::iterator msgIt;
msgIt = fi->msgs.find(msgId);
if (msgIt == fi->msgs.end()) {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::setMessageRead - msg " << msgId << " not found" << std::endl;
#endif
return false;
}
RsFeedReaderMsg *mi = msgIt->second;
std::string errorString;
std::string descriptionTransformed = mi->descriptionTransformed;
if (p3FeedReaderThread::processTransformation(*fi, mi, errorString) == RS_FEED_ERRORSTATE_OK) {
if (mi->descriptionTransformed != descriptionTransformed) {
msgChanged = true;
}
} else {
if (!errorString.empty()) {
fi->errorString = errorString;
feedChanged = true;
}
}
}
if (feedChanged || msgChanged) {
IndicateConfigChanged();
if (mNotify) {
if (feedChanged) {
mNotify->notifyFeedChanged(feedId, NOTIFY_TYPE_MOD);
}
if (msgChanged) {
mNotify->notifyMsgChanged(feedId, msgId, NOTIFY_TYPE_MOD);
}
}
}
return true;
}
RsFeedReaderErrorState p3FeedReader::processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, std::string &description, std::string &errorString)
{
return p3FeedReaderThread::processXPath(xpathsToUse, xpathsToRemove, description, errorString);
}
RsFeedReaderErrorState p3FeedReader::processXslt(const std::string &xslt, std::string &description, std::string &errorString)
{
return p3FeedReaderThread::processXslt(xslt, description, errorString);
}
/***************************************************************************/
/****************************** p3Service **********************************/
/***************************************************************************/
@ -1776,7 +1851,8 @@ void p3FeedReader::onProcessSuccess_addMsgs(const std::string &feedId, std::list
if (forum) {
miNew->flag = RS_FEEDMSG_FLAG_DELETED;
forumMsgs.push_back(*miNew);
// miNew->description.clear();
miNew->description.clear();
miNew->descriptionTransformed.clear();
} else {
miNew->flag = RS_FEEDMSG_FLAG_NEW;
addedMsgs.push_back(miNew->msgId);
@ -1816,7 +1892,7 @@ void p3FeedReader::onProcessSuccess_addMsgs(const std::string &feedId, std::list
forumMsgInfo.forumId = forumId;
librs::util::ConvertUtf8ToUtf16(mi.title, forumMsgInfo.title);
std::string description = mi.description;
std::string description = mi.descriptionTransformed.empty() ? mi.description : mi.descriptionTransformed;
/* add link */
if (!mi.link.empty()) {
description += "<br><a href=\"" + mi.link + "\">" + mi.link + "</a>";

View File

@ -62,8 +62,10 @@ public:
virtual bool getFeedMsgIdList(const std::string &feedId, std::list<std::string> &msgIds);
virtual bool processFeed(const std::string &feedId);
virtual bool setMessageRead(const std::string &feedId, const std::string &msgId, bool read);
virtual bool retransformMsg(const std::string &feedId, const std::string &msgId);
virtual RsFeedReaderErrorState processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, std::string &description, std::string &errorString);
virtual RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString);
/****************** p3Service STUFF ******************/
virtual int tick();

View File

@ -117,6 +117,10 @@ void p3FeedReaderThread::run()
delete (*it1);
}
} else {
result = processTransformation(feed, mi, errorString);
if (result != RS_FEED_ERRORSTATE_OK) {
break;
}
++it;
}
}
@ -258,14 +262,14 @@ static bool getFavicon(CURLWrapper &CURL, const std::string &url, std::string &i
return result;
}
RsFeedReaderErrorState p3FeedReaderThread::download(const RsFeedReaderFeed &feed, std::string &content, std::string &icon, std::string &error)
RsFeedReaderErrorState p3FeedReaderThread::download(const RsFeedReaderFeed &feed, std::string &content, std::string &icon, std::string &errorString)
{
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::download - feed " << feed.feedId << " (" << feed.name << ")" << std::endl;
#endif
content.clear();
error.clear();
errorString.clear();
RsFeedReaderErrorState result;
@ -291,7 +295,7 @@ RsFeedReaderErrorState p3FeedReaderThread::download(const RsFeedReaderFeed &feed
result = RS_FEED_ERRORSTATE_OK;
} else {
result = RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE;
error = contentType;
errorString = contentType;
}
}
break;
@ -300,17 +304,17 @@ RsFeedReaderErrorState p3FeedReaderThread::download(const RsFeedReaderFeed &feed
break;
default:
result = RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE;
rs_sprintf(error, "%ld", responseCode);
rs_sprintf(errorString, "%ld", responseCode);
}
getFavicon(CURL, feed.url, icon);
} else {
result = RS_FEED_ERRORSTATE_DOWNLOAD_ERROR;
error = curl_easy_strerror(code);
errorString = curl_easy_strerror(code);
}
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::download - feed " << feed.feedId << " (" << feed.name << "), result " << result << ", error = " << error << std::endl;
std::cerr << "p3FeedReaderThread::download - feed " << feed.feedId << " (" << feed.name << "), result " << result << ", error = " << errorString << std::endl;
#endif
return result;
@ -805,7 +809,7 @@ static time_t parseISO8601Date(const std::string &pubDate)
return result;
}
RsFeedReaderErrorState p3FeedReaderThread::process(const RsFeedReaderFeed &feed, std::list<RsFeedReaderMsg*> &entries, std::string &error)
RsFeedReaderErrorState p3FeedReaderThread::process(const RsFeedReaderFeed &feed, std::list<RsFeedReaderMsg*> &entries, std::string &errorString)
{
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::process - feed " << feed.feedId << " (" << feed.name << ")" << std::endl;
@ -826,7 +830,7 @@ RsFeedReaderErrorState p3FeedReaderThread::process(const RsFeedReaderFeed &feed,
feedFormat = FORMAT_ATOM;
} else {
result = RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT;
error = "Only RSS, RDF or ATOM supported";
errorString = "Only RSS, RDF or ATOM supported";
}
if (result == RS_FEED_ERRORSTATE_OK) {
@ -962,19 +966,23 @@ RsFeedReaderErrorState p3FeedReaderThread::process(const RsFeedReaderFeed &feed,
}
} else {
result = RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT;
error = "Channel not found";
errorString = "Channel not found";
}
}
} else {
result = RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT;
error = "Can't read document";
errorString = "Can't read document";
}
} else {
result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR;
errorString = xml.lastError();
}
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::process - feed " << feed.feedId << " (" << feed.name << "), result " << result << ", error = " << error << std::endl;
std::cerr << "p3FeedReaderThread::process - feed " << feed.feedId << " (" << feed.name << "), result " << result << ", error = " << errorString << std::endl;
if (result == RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR) {
std::cerr << " Error: " << errorString << std::endl;
}
#endif
return result;
@ -1059,7 +1067,7 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
}
/* check if string contains xml chars (very simple test) */
if (msg->description.find('<') == std::string::npos) {
if (msg->description.find('<') == std::string::npos && feed.transformationType == RS_FEED_TRANSFORMATION_TYPE_NONE) {
return result;
}
@ -1164,10 +1172,6 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
}
nodesToDelete.clear();
if (isRunning() && !feed.preview) {
result = processXPath(feed.xpathsToUse.ids, feed.xpathsToRemove.ids, html, errorString);
}
if (isRunning() && result == RS_FEED_ERRORSTATE_OK) {
unsigned int xpathCount;
unsigned int xpathIndex;
@ -1241,8 +1245,10 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (result == RS_FEED_ERRORSTATE_OK) {
if (isRunning()) {
if (!html.saveHTML(msg->description)) {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR;
}
@ -1255,8 +1261,10 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
}
} else {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot read html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
}
@ -1265,6 +1273,30 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
return result;
}
RsFeedReaderErrorState p3FeedReaderThread::processTransformation(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg, std::string &errorString)
{
RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK;
switch (feed.transformationType) {
case RS_FEED_TRANSFORMATION_TYPE_NONE:
break;
case RS_FEED_TRANSFORMATION_TYPE_XPATH:
msg->descriptionTransformed = msg->description;
result = processXPath(feed.xpathsToUse.ids, feed.xpathsToRemove.ids, msg->descriptionTransformed, errorString);
break;
case RS_FEED_TRANSFORMATION_TYPE_XSLT:
msg->descriptionTransformed = msg->description;
result = processXslt(feed.xslt, msg->descriptionTransformed, errorString);
break;
}
if (msg->descriptionTransformed == msg->description) {
msg->descriptionTransformed.clear();
}
return result;
}
RsFeedReaderErrorState p3FeedReaderThread::processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, HTMLWrapper &html, std::string &errorString)
{
long todo_fill_errorString;
@ -1376,8 +1408,6 @@ RsFeedReaderErrorState p3FeedReaderThread::processXPath(const std::list<std::str
RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK;
long todo_fill_errorString;
/* process description */
long todo; // encoding
HTMLWrapper html;
@ -1388,8 +1418,10 @@ RsFeedReaderErrorState p3FeedReaderThread::processXPath(const std::list<std::str
if (result == RS_FEED_ERRORSTATE_OK) {
if (!html.saveHTML(description)) {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processXPath - cannot dump html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR;
}
@ -1398,11 +1430,131 @@ RsFeedReaderErrorState p3FeedReaderThread::processXPath(const std::list<std::str
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processXPath - no root element" << std::endl;
#endif
errorString = "No root element found";
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
}
} else {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processXPath - cannot read html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
}
return result;
}
RsFeedReaderErrorState p3FeedReaderThread::processXslt(const std::string &xslt, HTMLWrapper &html, std::string &errorString)
{
XMLWrapper style;
if (!style.readXML(xslt.c_str())) {
errorString = style.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processXslt - error loading style" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
return RS_FEED_ERRORSTATE_PROCESS_XSLT_FORMAT_ERROR;
}
XMLWrapper xmlResult;
if (!html.transform(style, xmlResult)) {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processXslt - error transform" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
return RS_FEED_ERRORSTATE_PROCESS_XSLT_TRANSFORM_ERROR;
}
RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK;
xmlNodePtr root = xmlResult.getRootElement();
if (root) {
if (xmlResult.nodeName(root) == "html") {
if (root->children && xmlResult.nodeName(root->children) == "body") {
root = root->children->children;
}
}
HTMLWrapper htmlNew;
if (htmlNew.createHTML()) {
xmlNodePtr body = htmlNew.getBody();
if (body) {
/* copy result nodes */
xmlNodePtr node;
for (node = root; node; node = node->next) {
xmlNodePtr newNode = xmlCopyNode(node, 1);
if (newNode) {
if (!xmlAddChild(body, newNode)) {
xmlFreeNode(newNode);
break;
}
} else {
result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR;
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processXslt - node copy error" << std::endl;
#endif
break;
}
}
} else {
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
}
} else {
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
}
if (result == RS_FEED_ERRORSTATE_OK) {
html = htmlNew;
}
} else {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processXslt - no result" << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_XSLT_NO_RESULT;
}
return result;
}
RsFeedReaderErrorState p3FeedReaderThread::processXslt(const std::string &xslt, std::string &description, std::string &errorString)
{
if (xslt.empty()) {
return RS_FEED_ERRORSTATE_OK;
}
RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK;
/* process description */
long todo; // encoding
HTMLWrapper html;
if (html.readHTML(description.c_str(), "")) {
xmlNodePtr root = html.getRootElement();
if (root) {
result = processXslt(xslt, html, errorString);
if (result == RS_FEED_ERRORSTATE_OK) {
if (!html.saveHTML(description)) {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processXslt - cannot dump html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR;
}
}
} else {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processXslt - no root element" << std::endl;
#endif
errorString = "No root element found";
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
}
} else {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processXslt - cannot read html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
}

View File

@ -51,11 +51,15 @@ public:
static RsFeedReaderErrorState processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, std::string &description, std::string &errorString);
static RsFeedReaderErrorState processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, HTMLWrapper &html, std::string &errorString);
static RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString);
static RsFeedReaderErrorState processXslt(const std::string &xslt, HTMLWrapper &html, std::string &errorString);
static RsFeedReaderErrorState processTransformation(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg, std::string &errorString);
private:
virtual void run();
RsFeedReaderErrorState download(const RsFeedReaderFeed &feed, std::string &content, std::string &icon, std::string &error);
RsFeedReaderErrorState process(const RsFeedReaderFeed &feed, std::list<RsFeedReaderMsg*> &entries, std::string &error);
RsFeedReaderErrorState download(const RsFeedReaderFeed &feed, std::string &content, std::string &icon, std::string &errorString);
RsFeedReaderErrorState process(const RsFeedReaderFeed &feed, std::list<RsFeedReaderMsg*> &entries, std::string &errorString);
std::string getProxyForFeed(const RsFeedReaderFeed &feed);
RsFeedReaderErrorState processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg, std::string &errorString);

View File

@ -51,8 +51,10 @@ void RsFeedReaderFeed::clear()
icon.clear();
errorState = RS_FEED_ERRORSTATE_OK;
errorString.clear();
transformationType = RS_FEED_TRANSFORMATION_TYPE_NONE;
xpathsToUse.ids.clear();
xpathsToRemove.ids.clear();
xslt.clear();
preview = false;
workstate = WAITING;
@ -85,8 +87,10 @@ uint32_t RsFeedReaderSerialiser::sizeFeed(RsFeedReaderFeed *item)
s += GetTlvStringSize(item->forumId);
s += sizeof(uint32_t); /* errorstate */
s += GetTlvStringSize(item->errorString);
s += sizeof(uint32_t); /* transformationType */
s += item->xpathsToUse.TlvSize();
s += item->xpathsToRemove.TlvSize();
s += GetTlvStringSize(item->xslt);
return s;
}
@ -110,7 +114,7 @@ bool RsFeedReaderSerialiser::serialiseFeed(RsFeedReaderFeed *item, void *data, u
offset += 8;
/* add values */
ok &= setRawUInt16(data, tlvsize, &offset, 0); /* version */
ok &= setRawUInt16(data, tlvsize, &offset, 1); /* version */
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_GENID, item->feedId);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->parentId);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_LINK, item->url);
@ -128,8 +132,10 @@ bool RsFeedReaderSerialiser::serialiseFeed(RsFeedReaderFeed *item, void *data, u
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->forumId);
ok &= setRawUInt32(data, tlvsize, &offset, item->errorState);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->errorString);
ok &= setRawUInt32(data, tlvsize, &offset, item->transformationType);
ok &= item->xpathsToUse.SetTlv(data, tlvsize, &offset);
ok &= item->xpathsToRemove.SetTlv(data, tlvsize, &offset);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->xslt);
if (offset != tlvsize)
{
@ -192,9 +198,28 @@ RsFeedReaderFeed *RsFeedReaderSerialiser::deserialiseFeed(void *data, uint32_t *
ok &= getRawUInt32(data, rssize, &offset, &errorState);
item->errorState = (RsFeedReaderErrorState) errorState;
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->errorString);
if (version >= 1) {
uint32_t value = RS_FEED_TRANSFORMATION_TYPE_NONE;
ok &= getRawUInt32(data, rssize, &offset, &value);
if (ok)
{
item->transformationType = (RsFeedTransformationType) value;
}
}
ok &= item->xpathsToUse.GetTlv(data, rssize, &offset);
ok &= item->xpathsToRemove.GetTlv(data, rssize, &offset);
if (version >= 1) {
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->xslt);
}
if (version == 0)
{
if (!item->xpathsToUse.ids.empty() || !item->xpathsToRemove.ids.empty())
{
/* set transformation type */
item->transformationType = RS_FEED_TRANSFORMATION_TYPE_XPATH;
}
}
if (offset != rssize)
{
/* error */
@ -226,6 +251,7 @@ void RsFeedReaderMsg::clear()
link.clear();
author.clear();
description.clear();
descriptionTransformed.clear();
pubDate = 0;
flag = 0;
}
@ -245,6 +271,7 @@ uint32_t RsFeedReaderSerialiser::sizeMsg(RsFeedReaderMsg *item)
s += GetTlvStringSize(item->link);
s += GetTlvStringSize(item->author);
s += GetTlvStringSize(item->description);
s += GetTlvStringSize(item->descriptionTransformed);
s += sizeof(time_t); /* pubDate */
s += sizeof(uint32_t); /* flag */
@ -270,13 +297,14 @@ bool RsFeedReaderSerialiser::serialiseMsg(RsFeedReaderMsg *item, void *data, uin
offset += 8;
/* add values */
ok &= setRawUInt16(data, tlvsize, &offset, 0); /* version */
ok &= setRawUInt16(data, tlvsize, &offset, 1); /* version */
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_GENID, item->msgId);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->feedId);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_NAME, item->title);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_LINK, item->link);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->author);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_COMMENT, item->description);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_COMMENT, item->descriptionTransformed);
ok &= setRawUInt32(data, tlvsize, &offset, item->pubDate);
ok &= setRawUInt32(data, tlvsize, &offset, item->flag);
@ -328,6 +356,9 @@ RsFeedReaderMsg *RsFeedReaderSerialiser::deserialiseMsg(void *data, uint32_t *pk
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_LINK, item->link);
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->author);
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_COMMENT, item->description);
if (version >= 1) {
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_COMMENT, item->descriptionTransformed);
}
ok &= getRawUInt32(data, rssize, &offset, (uint32_t*) &(item->pubDate));
ok &= getRawUInt32(data, rssize, &offset, &(item->flag));

View File

@ -65,26 +65,28 @@ 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;
RsFeedReaderErrorState 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;
RsTlvStringSet xpathsToUse;
RsTlvStringSet xpathsToRemove;
RsFeedTransformationType transformationType;
RsTlvStringSet xpathsToUse;
RsTlvStringSet xpathsToRemove;
std::string xslt;
/* Not Serialised */
bool preview;
@ -113,6 +115,7 @@ public:
std::string link;
std::string author;
std::string description;
std::string descriptionTransformed;
time_t pubDate;
uint32_t flag; // RS_FEEDMSG_FLAG_...
};

View File

@ -32,7 +32,10 @@ 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 | HTML_PARSE_NONET | HTML_PARSE_NOBLANKS);
handleError(true, mLastErrorString);
mDocument = htmlReadMemory(html, strlen(html), url, "", /*HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | */HTML_PARSE_COMPACT | HTML_PARSE_NONET | HTML_PARSE_NOBLANKS);
handleError(false, mLastErrorString);
if (mDocument) {
return true;
}
@ -48,7 +51,10 @@ bool HTMLWrapper::saveHTML(std::string &html)
xmlChar *newHtml = NULL;
int newHtmlSize = 0;
handleError(true, mLastErrorString);
htmlDocDumpMemoryFormat(mDocument, &newHtml, &newHtmlSize, 0);
handleError(false, mLastErrorString);
if (newHtml) {
convertToString(newHtml, html);
xmlFree(newHtml);

View File

@ -25,6 +25,14 @@
#include "XMLWrapper.h"
#include "XPathWrapper.h"
#include <util/rsstring.h>
#include <util/rsthreads.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
static RsMutex xmlMtx("XMLWrapper");
static std::string xmlErrorString;
XMLWrapper::XMLWrapper()
{
mDocument = NULL;
@ -42,6 +50,35 @@ XMLWrapper::~XMLWrapper()
xmlCharEncCloseFunc(mCharEncodingHandler);
}
static void xmlErrorHandler(void */*context*/, const char *msg, ...)
{
va_list vl;
va_start(vl, msg);
rs_sprintf_append_args(xmlErrorString, msg, vl);
va_end(vl);
}
void XMLWrapper::handleError(bool init, std::string &errorString)
{
if (init) {
xmlMtx.lock();
xmlErrorString.clear();
errorString.clear();
xsltSetGenericErrorFunc(this, xmlErrorHandler);
xmlSetGenericErrorFunc(this, xmlErrorHandler);
} else {
xsltSetGenericErrorFunc(NULL, NULL);
xmlSetGenericErrorFunc(NULL, NULL);
errorString = xmlErrorString;
xmlErrorString.clear();
xmlMtx.unlock();
}
}
void XMLWrapper::trimString(std::string &string)
{
/* trim left */
@ -59,7 +96,6 @@ void XMLWrapper::trimString(std::string &string)
}
}
XMLWrapper &XMLWrapper::operator=(const XMLWrapper &xml)
{
cleanup();
@ -80,6 +116,13 @@ void XMLWrapper::cleanup()
}
}
void XMLWrapper::attach(xmlDocPtr document)
{
cleanup();
mDocument = document;
}
bool XMLWrapper::convertToString(const xmlChar *xmlText, std::string &text)
{
bool result = false;
@ -138,7 +181,10 @@ bool XMLWrapper::readXML(const char *xml)
{
cleanup();
mDocument = xmlReadDoc(BAD_CAST xml, "", NULL, XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_COMPACT | XML_PARSE_NOENT | XML_PARSE_NOCDATA);
handleError(true, mLastErrorString);
mDocument = xmlReadDoc(BAD_CAST xml, "", NULL, /*XML_PARSE_NOERROR | XML_PARSE_NOWARNING | */XML_PARSE_COMPACT | XML_PARSE_NOENT | XML_PARSE_NOCDATA);
handleError(false, mLastErrorString);
if (mDocument) {
return true;
}
@ -358,3 +404,23 @@ XPathWrapper *XMLWrapper::createXPath()
return NULL;
}
bool XMLWrapper::transform(const XMLWrapper &style, XMLWrapper &result)
{
handleError(true, mLastErrorString);
xmlDocPtr resultDoc = NULL;
xsltStylesheetPtr stylesheet = xsltParseStylesheetDoc(style.getDocument());
if (stylesheet) {
resultDoc = xsltApplyStylesheet(stylesheet, getDocument(), NULL);
stylesheet->doc = NULL; // xsltFreeStylesheet is freeing doc
xsltFreeStylesheet(stylesheet);
}
result.attach(resultDoc);
handleError(false, mLastErrorString);
return resultDoc ? true : false;
}

View File

@ -39,12 +39,16 @@ public:
XMLWrapper &operator=(const XMLWrapper &xml);
void cleanup();
std::string lastError() { return mLastErrorString; }
bool readXML(const char *xml);
xmlDocPtr getDocument() const;
xmlNodePtr getRootElement() const;
bool convertToString(const xmlChar *xmlText, std::string &text);
bool convertFromString(const char *text, xmlChar *&xmlText);
std::string nodeName(xmlNodePtr node);
std::string attrName(xmlAttrPtr attr);
@ -62,12 +66,16 @@ public:
XPathWrapper *createXPath();
bool convertToString(const xmlChar *xmlText, std::string &text);
bool convertFromString(const char *text, xmlChar *&xmlText);
bool transform(const XMLWrapper &style, XMLWrapper &result);
protected:
void attach(xmlDocPtr document);
void handleError(bool init, std::string &errorString);
protected:
xmlDocPtr mDocument;
xmlCharEncodingHandlerPtr mCharEncodingHandler;
std::string mLastErrorString;
};
#endif
#endif

View File

@ -0,0 +1,54 @@
/****************************************************************
*
* RetroShare is distributed under the following license:
*
* Copyright (C) 2013, RetroShare Team
*
* 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 <QPainter>
#include "RSPlainTextEdit.h"
RSPlainTextEdit::RSPlainTextEdit(QWidget *parent)
: QPlainTextEdit(parent)
{
}
void RSPlainTextEdit::setPlaceholderText(const QString &text)
{
mPlaceholderText = text;
viewport()->repaint();
}
void RSPlainTextEdit::paintEvent(QPaintEvent *event)
{
QPlainTextEdit::paintEvent(event);
if (mPlaceholderText.isEmpty() == false && toPlainText().isEmpty()) {
QWidget *vieportWidget = viewport();
QPainter painter(vieportWidget);
QPen pen = painter.pen();
QColor color = pen.color();
color.setAlpha(128);
pen.setColor(color);
painter.setPen(pen);
painter.drawText(QRect(QPoint(), vieportWidget->size()), Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextWordWrap, mPlaceholderText);
}
}

View File

@ -0,0 +1,44 @@
/****************************************************************
*
* RetroShare is distributed under the following license:
*
* Copyright (C) 2013, RetroShare Team
*
* 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 RSPLAINTEXTEDIT_H
#define RSPLAINTEXTEDIT_H
#include <QPlainTextEdit>
class RSPlainTextEdit : public QPlainTextEdit
{
Q_OBJECT
public:
RSPlainTextEdit(QWidget *parent = 0);
void setPlaceholderText(const QString &text);
protected:
void paintEvent(QPaintEvent *event);
private:
QString mPlaceholderText;
};
#endif // RSPLAINTEXTEDIT_H

View File

@ -397,6 +397,7 @@ HEADERS += rshare.h \
gui/common/GroupDefs.h \
gui/common/Emoticons.h \
gui/common/RSListWidgetItem.h \
gui/common/RSPlainTextEdit.h \
gui/common/RSTreeWidget.h \
gui/common/RSTreeWidgetItem.h \
gui/common/RSTabWidget.h \
@ -665,6 +666,7 @@ SOURCES += main.cpp \
gui/common/GroupDefs.cpp \
gui/common/Emoticons.cpp \
gui/common/RSListWidgetItem.cpp \
gui/common/RSPlainTextEdit.cpp \
gui/common/RSTreeWidget.cpp \
gui/common/RSTreeWidgetItem.cpp \
gui/common/RSTabWidget.cpp \