Syndication

rss2/item.cpp
1/*
2 This file is part of the syndication library
3 SPDX-FileCopyrightText: 2005 Frank Osterfeld <osterfeld@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include <constants.h>
9#include <rss2/category.h>
10#include <rss2/enclosure.h>
11#include <rss2/item.h>
12#include <rss2/source.h>
13#include <rss2/tools_p.h>
14#include <specificitem.h>
15#include <specificitemvisitor.h>
16#include <tools.h>
17
18#include <QDomElement>
19#include <QList>
20#include <QString>
21
22#include <vector>
23
24namespace Syndication
25{
26namespace RSS2
27{
28class SYNDICATION_NO_EXPORT Item::ItemPrivate
29{
30public:
32};
33
34Item::Item(QSharedPointer<Document> doc)
35 : ElementWrapper()
36 , d(new ItemPrivate)
37{
38 d->doc = doc;
39}
40
41Item::Item(const QDomElement &element, QSharedPointer<Document> doc)
42 : ElementWrapper(element)
43 , d(new ItemPrivate)
44{
45 d->doc = doc;
46}
47
48Item::~Item()
49{
50}
51
52Item::Item(const Item &other)
53 : ElementWrapper(other)
54 , SpecificItem(other)
55{
56 d = other.d;
57}
58
59Item &Item::operator=(const Item &other)
60{
61 ElementWrapper::operator=(other);
62 SpecificItem::operator=(other);
63 d = other.d;
64 return *this;
65}
66
67QString Item::title() const
68{
69 if (!d->doc) {
70 return originalTitle();
71 }
72
73 bool isCDATA = false;
74 bool containsMarkup = false;
75 d->doc->getItemTitleFormatInfo(&isCDATA, &containsMarkup);
76
77 return normalize(originalTitle(), isCDATA, containsMarkup);
78}
79
80QString Item::originalDescription() const
81{
82 return extractElementTextNS(QString(), QStringLiteral("description"));
83}
84
85QString Item::originalTitle() const
86{
87 return extractElementTextNS(QString(), QStringLiteral("title"));
88}
89
90QString Item::link() const
91{
92 QString url = extractElementTextNS(QString(), QStringLiteral("link"));
93 if (url.startsWith(QLatin1String("http://")) || url.startsWith(QLatin1String("https://"))) {
94 return url;
95 }
96 if (url.isEmpty()) {
97 return QString();
98 }
99 if (d->doc->link().isEmpty()) {
100 return url;
101 }
102 // link does not look like a complete url, assume the feed author expects
103 // the doc link to provide the base of the url.
104 QString baseUrl = d->doc->link();
105 if (url.startsWith(QLatin1Char('/')) || baseUrl.endsWith(QLatin1Char('/'))) {
106 return baseUrl + url;
107 } else {
108 return baseUrl + QLatin1Char('/') + url;
109 }
110}
111
112QString Item::description() const
113{
114 if (!d->doc) {
115 return originalDescription();
116 }
117
118 bool isCDATA = false;
119 bool containsMarkup = false;
120 d->doc->getItemDescriptionFormatInfo(&isCDATA, &containsMarkup);
121
122 return normalize(originalDescription(), isCDATA, containsMarkup);
123}
124
125QString Item::content() const
126{
127 // parse encoded stuff from content:encoded, xhtml:body and friends into content
128 return extractContent(*this);
129}
130
131QList<Category> Item::categories() const
132{
133 const QList<QDomElement> cats = elementsByTagNameNS(QString(), QStringLiteral("category"));
134
135 QList<Category> categories;
136 categories.reserve(cats.count());
137
138 std::transform(cats.cbegin(), cats.cend(), std::back_inserter(categories), [](const QDomElement &element) {
139 return Category(element);
140 });
141
142 return categories;
143}
144
145QString Item::comments() const
146{
147 return extractElementTextNS(QString(), QStringLiteral("comments"));
148}
149
150QString Item::author() const
151{
152 QString a = extractElementTextNS(QString(), QStringLiteral("author"));
153 if (!a.isNull()) {
154 return a;
155 } else {
156 // if author is not available, fall back to dc:creator
157 return extractElementTextNS(dublinCoreNamespace(), QStringLiteral("creator"));
158 }
159}
160
161QList<Enclosure> Item::enclosures() const
162{
163 const QList<QDomElement> encs = elementsByTagNameNS(QString(), QStringLiteral("enclosure"));
164
165 QList<Enclosure> enclosures;
166 enclosures.reserve(encs.count());
167
168 std::transform(encs.cbegin(), encs.cend(), std::back_inserter(enclosures), [](const QDomElement &element) {
169 return Enclosure(element);
170 });
171
172 return enclosures;
173}
174
175QString Item::guid() const
176{
177 return extractElementTextNS(QString(), QStringLiteral("guid"));
178}
179
180bool Item::guidIsPermaLink() const
181{
182 bool guidIsPermaLink = true; // true is default
183
184 QDomElement guidNode = firstElementByTagNameNS(QString(), QStringLiteral("guid"));
185 if (!guidNode.isNull()) {
186 if (guidNode.attribute(QStringLiteral("isPermaLink")) == QLatin1String("false")) {
187 guidIsPermaLink = false;
188 }
189 }
190
191 return guidIsPermaLink;
192}
193
194time_t Item::pubDate() const
195{
196 QString str = extractElementTextNS(QString(), QStringLiteral("pubDate"));
197
198 if (!str.isNull()) {
199 return parseDate(str, RFCDate);
200 }
201
202 // if there is no pubDate, check for dc:date
203 str = extractElementTextNS(dublinCoreNamespace(), QStringLiteral("date"));
204 return parseDate(str, ISODate);
205}
206
207time_t Item::expirationDate() const
208{
209 QString str = extractElementTextNS(QString(), QStringLiteral("expirationDate"));
210 return parseDate(str, RFCDate);
211}
212
213Source Item::source() const
214{
215 return Source(firstElementByTagNameNS(QString(), QStringLiteral("source")));
216}
217
218QString Item::rating() const
219{
220 return extractElementTextNS(QString(), QStringLiteral("rating"));
221}
222
223QString Item::debugInfo() const
224{
225 QString info = QLatin1String("### Item: ###################\n");
226 if (!title().isNull()) {
227 info += QLatin1String("title: #") + title() + QLatin1String("#\n");
228 }
229 if (!link().isNull()) {
230 info += QLatin1String("link: #") + link() + QLatin1String("#\n");
231 }
232 if (!description().isNull()) {
233 info += QLatin1String("description: #") + description() + QLatin1String("#\n");
234 }
235 if (!content().isNull()) {
236 info += QLatin1String("content: #") + content() + QLatin1String("#\n");
237 }
238 if (!author().isNull()) {
239 info += QLatin1String("author: #") + author() + QLatin1String("#\n");
240 }
241 if (!comments().isNull()) {
242 info += QLatin1String("comments: #") + comments() + QLatin1String("#\n");
243 }
244 QString dpubdate = dateTimeToString(pubDate());
245 if (!dpubdate.isNull()) {
246 info += QLatin1String("pubDate: #") + dpubdate + QLatin1String("#\n");
247 }
248 if (!guid().isNull()) {
249 info += QLatin1String("guid: #") + guid() + QLatin1String("#\n");
250 }
251 if (guidIsPermaLink()) {
252 info += QLatin1String("guid is PL: #true#\n");
253 }
254 if (!source().isNull()) {
255 info += source().debugInfo();
256 }
257
258 const QList<Category> cats = categories();
259 for (const auto &c : cats) {
260 info += c.debugInfo();
261 }
262
263 const QList<Enclosure> encs = enclosures();
264 for (const auto &e : encs) {
265 info += e.debugInfo();
266 }
267
268 info += QLatin1String("### Item end ################\n");
269 return info;
270}
271
272QList<QDomElement> Item::unhandledElements() const
273{
274 // TODO: do not hardcode this list here
275 static std::vector<ElementType> handled; // QVector would require a default ctor, and ElementType is too big for QList
276 if (handled.empty()) {
277 handled.reserve(11);
278 handled.push_back(ElementType(QStringLiteral("title")));
279 handled.push_back(ElementType(QStringLiteral("link")));
280 handled.push_back(ElementType(QStringLiteral("description")));
281 handled.push_back(ElementType(QStringLiteral("pubDate")));
282 handled.push_back(ElementType(QStringLiteral("expirationDate")));
283 handled.push_back(ElementType(QStringLiteral("rating")));
284 handled.push_back(ElementType(QStringLiteral("source")));
285 handled.push_back(ElementType(QStringLiteral("guid")));
286 handled.push_back(ElementType(QStringLiteral("comments")));
287 handled.push_back(ElementType(QStringLiteral("author")));
288 handled.push_back(ElementType(QStringLiteral("date"), dublinCoreNamespace()));
289 }
290
291 QList<QDomElement> notHandled;
292
293 QDomNodeList children = element().childNodes();
294 const int numChildren = children.size();
295 for (int i = 0; i < numChildren; ++i) {
296 QDomElement el = children.at(i).toElement();
297 if (!el.isNull() //
298 && std::find(handled.cbegin(), handled.cend(), ElementType(el.localName(), el.namespaceURI())) == handled.cend()) {
299 notHandled.append(el);
300 }
301 }
302
303 return notHandled;
304}
305
306bool Item::accept(SpecificItemVisitor *visitor)
307{
308 return visitor->visitRSS2Item(this);
309}
310
311} // namespace RSS2
312} // namespace Syndication
KCALUTILS_EXPORT QString dateTimeToString(const QDateTime &date, bool dateOnly=false, bool shortfmt=true)
KIOCORE_EXPORT CopyJob * link(const QList< QUrl > &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
QString normalize(QStringView str)
KEDUVOCDOCUMENT_EXPORT QStringList comments(const QString &language=QString())
QString attribute(const QString &name, const QString &defValue) const const
bool isNull() const const
QString localName() const const
QString namespaceURI() const const
QDomElement toElement() const const
int size() const const
void append(QList< T > &&value)
const_iterator cbegin() const const
const_iterator cend() const const
qsizetype count() const const
void reserve(qsizetype size)
const QObjectList & children() const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isNull() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:15 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.