Refactor into a 'Source'-based structure
This commit is contained in:
parent
b2d74a8760
commit
d65c834a6f
1
Makefile
1
Makefile
|
@ -35,7 +35,6 @@ SRCS = \
|
||||||
src/Feed.cpp \
|
src/Feed.cpp \
|
||||||
src/FeedEditWindow.cpp \
|
src/FeedEditWindow.cpp \
|
||||||
src/FeedListItem.cpp \
|
src/FeedListItem.cpp \
|
||||||
src/FeedSource.cpp \
|
|
||||||
src/FeedsView.cpp \
|
src/FeedsView.cpp \
|
||||||
src/LocalSource.cpp \
|
src/LocalSource.cpp \
|
||||||
src/MainWindow.cpp \
|
src/MainWindow.cpp \
|
||||||
|
|
|
@ -29,17 +29,14 @@ APP_MIME_SIG = application/x-vnd.PoggerDaemon
|
||||||
# same name (source.c or source.cpp) are included from different directories.
|
# same name (source.c or source.cpp) are included from different directories.
|
||||||
# Also note that spaces in folder names do not work well with this Makefile.
|
# Also note that spaces in folder names do not work well with this Makefile.
|
||||||
SRCS = \
|
SRCS = \
|
||||||
src/AtomFeed.cpp \
|
|
||||||
src/Daemon.cpp \
|
src/Daemon.cpp \
|
||||||
src/DeskbarView.cpp \
|
src/DeskbarView.cpp \
|
||||||
src/Entry.cpp \
|
src/Entry.cpp \
|
||||||
src/Feed.cpp \
|
src/Feed.cpp \
|
||||||
src/FeedController.cpp \
|
src/FeedController.cpp \
|
||||||
src/FeedSource.cpp \
|
|
||||||
src/LocalSource.cpp \
|
src/LocalSource.cpp \
|
||||||
src/Notifier.cpp \
|
src/Notifier.cpp \
|
||||||
src/Preferences.cpp \
|
src/Preferences.cpp \
|
||||||
src/RssFeed.cpp \
|
|
||||||
src/Util.cpp
|
src/Util.cpp
|
||||||
|
|
||||||
# Specify the resource definition files to use. Full or relative paths can be
|
# Specify the resource definition files to use. Full or relative paths can be
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#include <StorageKit.h>
|
#include <StorageKit.h>
|
||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
#include "AtomFeed.h"
|
|
||||||
#include "Entry.h"
|
#include "Entry.h"
|
||||||
#include "Feed.h"
|
#include "Feed.h"
|
||||||
#include "FeedController.h"
|
#include "FeedController.h"
|
||||||
|
@ -20,7 +19,6 @@
|
||||||
#include "Mimetypes.h"
|
#include "Mimetypes.h"
|
||||||
#include "Notifier.h"
|
#include "Notifier.h"
|
||||||
#include "Preferences.h"
|
#include "Preferences.h"
|
||||||
#include "RssFeed.h"
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -158,7 +156,9 @@ App::_OpenSourceFile(BMessage* refMessage)
|
||||||
{
|
{
|
||||||
entry_ref entryRef;
|
entry_ref entryRef;
|
||||||
refMessage->FindRef("refs", &entryRef);
|
refMessage->FindRef("refs", &entryRef);
|
||||||
FeedEditWindow* window = new FeedEditWindow(BEntry(&entryRef));
|
BPath path(&entryRef);
|
||||||
|
|
||||||
|
FeedEditWindow* window = new FeedEditWindow(BString(path.Path()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
140
src/AtomFeed.cpp
140
src/AtomFeed.cpp
|
@ -1,140 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020, Jaidyn Levesque <jadedctrl@teknik.io>
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "AtomFeed.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <Catalog.h>
|
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
|
|
||||||
#include "App.h"
|
|
||||||
#include "Entry.h"
|
|
||||||
#include "Util.h"
|
|
||||||
|
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
|
||||||
#define B_TRANSLATION_CONTEXT "AtomFeed"
|
|
||||||
|
|
||||||
|
|
||||||
AtomFeed::AtomFeed()
|
|
||||||
{
|
|
||||||
fTitle = BString("");
|
|
||||||
fCachePath = BString("");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
AtomFeed::AtomFeed(Feed* feed)
|
|
||||||
: AtomFeed::AtomFeed()
|
|
||||||
{
|
|
||||||
SetTitle(feed->Title());
|
|
||||||
SetXmlUrl(feed->XmlUrl());
|
|
||||||
SetCachePath(feed->CachePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
AtomFeed::Parse()
|
|
||||||
{
|
|
||||||
fEntries = BObjectList<Entry>(5, true);
|
|
||||||
tinyxml2::XMLDocument xml;
|
|
||||||
xml.LoadFile(CachePath().String());
|
|
||||||
|
|
||||||
Feed::Parse();
|
|
||||||
|
|
||||||
tinyxml2::XMLElement* xfeed = xml.FirstChildElement("feed");
|
|
||||||
|
|
||||||
RootParse(xfeed);
|
|
||||||
ParseEntries(xfeed);
|
|
||||||
|
|
||||||
Filetize();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
AtomFeed::RootParse(tinyxml2::XMLElement* xfeed)
|
|
||||||
{
|
|
||||||
tinyxml2::XMLElement* xentry = xfeed->FirstChildElement("entry");
|
|
||||||
|
|
||||||
bool set = false;
|
|
||||||
|
|
||||||
_SetTitle(xfeed->FirstChildElement("title"));
|
|
||||||
|
|
||||||
set = _SetDate(xfeed->FirstChildElement("updated"));
|
|
||||||
if (!set)
|
|
||||||
set = _SetDate(xfeed->FirstChildElement("published"));
|
|
||||||
if (!set && xentry)
|
|
||||||
set = _SetDate(xentry->FirstChildElement("updated"));
|
|
||||||
if (!set && xentry)
|
|
||||||
set = _SetDate(xentry->FirstChildElement("published"));
|
|
||||||
|
|
||||||
BString logString(B_TRANSLATE("Channel '%source%' at %url%:\n"));
|
|
||||||
logString.ReplaceAll("%source%", fTitle.String());
|
|
||||||
logString.ReplaceAll("%url%", fXmlUrl.UrlString());
|
|
||||||
|
|
||||||
std::cout << logString.String();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
AtomFeed::EntryParse(tinyxml2::XMLElement* xentry)
|
|
||||||
{
|
|
||||||
Entry* newEntry = new Entry();
|
|
||||||
|
|
||||||
tinyxml2::XMLElement* xcontent = xentry->FirstChildElement("content");
|
|
||||||
tinyxml2::XMLElement* xmedia = xentry->FirstChildElement("media:group");
|
|
||||||
tinyxml2::XMLPrinter xprinter;
|
|
||||||
|
|
||||||
newEntry->SetTitle(xentry->FirstChildElement("title"));
|
|
||||||
newEntry->SetPostUrl(xentry->FirstChildElement("link")->Attribute("href"));
|
|
||||||
newEntry->SetFeedTitle(fTitle);
|
|
||||||
|
|
||||||
bool set = false;
|
|
||||||
set = newEntry->SetDescription(xentry->FirstChildElement("summary"));
|
|
||||||
if (!set)
|
|
||||||
set = newEntry->SetDescription(xentry->FirstChildElement("description"));
|
|
||||||
if (!set && xmedia)
|
|
||||||
set = newEntry->SetDescription(
|
|
||||||
xmedia->FirstChildElement("media:description"));
|
|
||||||
|
|
||||||
set = newEntry->SetDate(xentry->FirstChildElement("updated"));
|
|
||||||
if (!set)
|
|
||||||
set = newEntry->SetDate(xentry->FirstChildElement("published"));
|
|
||||||
|
|
||||||
if (fDate == NULL || fDate < newEntry->Date())
|
|
||||||
_SetDate(newEntry->Date());
|
|
||||||
|
|
||||||
if (xcontent) {
|
|
||||||
xcontent->Accept(&xprinter);
|
|
||||||
newEntry->SetContent(xprinter.CStr());
|
|
||||||
}
|
|
||||||
|
|
||||||
_AddEntry(newEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
AtomFeed::ParseEntries(tinyxml2::XMLElement* xfeed)
|
|
||||||
{
|
|
||||||
tinyxml2::XMLElement* xentry;
|
|
||||||
|
|
||||||
xentry = xfeed->FirstChildElement("entry");
|
|
||||||
|
|
||||||
int entryCount = _XmlCountSiblings(xentry, "entry");
|
|
||||||
fEntries = BObjectList<Entry>(entryCount, true);
|
|
||||||
|
|
||||||
BString logString(B_TRANSLATE("\t-%count% entries-\n"));
|
|
||||||
BString entryStr;
|
|
||||||
entryStr << entryCount;
|
|
||||||
logString.ReplaceAll("%count%", entryStr);
|
|
||||||
|
|
||||||
std::cout << logString.String();
|
|
||||||
|
|
||||||
while (xentry) {
|
|
||||||
EntryParse(xentry);
|
|
||||||
xentry = xentry->NextSiblingElement("entry");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020, Jaidyn Levesque <jadedctrl@teknik.io>
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
#ifndef ATOM_FEED_H
|
|
||||||
#define ATOM_FEED_H
|
|
||||||
|
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
|
|
||||||
#include "Feed.h"
|
|
||||||
|
|
||||||
|
|
||||||
class AtomFeed: public Feed {
|
|
||||||
public:
|
|
||||||
AtomFeed();
|
|
||||||
AtomFeed(Feed*);
|
|
||||||
|
|
||||||
void Parse();
|
|
||||||
void RootParse(tinyxml2::XMLElement*);
|
|
||||||
void EntryParse(tinyxml2::XMLElement*);
|
|
||||||
void ParseEntries(tinyxml2::XMLElement*);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -192,24 +192,10 @@ Entry::Date()
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Entry::SetDate(const char* dateStr)
|
Entry::SetDate(BDateTime date)
|
||||||
{
|
{
|
||||||
if (dateStr == NULL)
|
fDate = date;
|
||||||
return false;
|
|
||||||
BDateTime newDate = feedDateToBDate(dateStr);
|
|
||||||
if (newDate == NULL)
|
|
||||||
return false;
|
|
||||||
fDate = newDate;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Entry::SetDate(tinyxml2::XMLElement* elem)
|
|
||||||
{
|
|
||||||
if (elem != NULL)
|
|
||||||
return SetDate(elem->GetText());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -42,9 +42,7 @@ public:
|
||||||
bool SetPostUrl(tinyxml2::XMLElement*);
|
bool SetPostUrl(tinyxml2::XMLElement*);
|
||||||
|
|
||||||
BDateTime Date();
|
BDateTime Date();
|
||||||
bool SetDate(const char*);
|
bool SetDate(BDateTime date);
|
||||||
bool SetDate(tinyxml2::XMLElement*);
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BString fTitle;
|
BString fTitle;
|
||||||
|
|
328
src/Feed.cpp
328
src/Feed.cpp
|
@ -14,69 +14,18 @@
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
|
|
||||||
Feed::Feed(BUrl xml, BString path)
|
Feed::Feed()
|
||||||
: Feed()
|
: fTitle(BString(""))
|
||||||
{
|
{
|
||||||
SetXmlUrl(xml);
|
fLastDate = BDateTime::CurrentDateTime(B_LOCAL_TIME);
|
||||||
SetCachePath(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Feed::Feed(BUrl xml)
|
Feed::Feed(const char* identifier, const char* title, const char* url)
|
||||||
: Feed()
|
: fTitle(BString(title)),
|
||||||
|
fIdentifier(BString(identifier)),
|
||||||
|
fUrl(BUrl(url))
|
||||||
{
|
{
|
||||||
SetXmlUrl(xml);
|
|
||||||
|
|
||||||
BPath cache;
|
|
||||||
find_directory(B_USER_CACHE_DIRECTORY, &cache);
|
|
||||||
cache.Append("Pogger");
|
|
||||||
|
|
||||||
cache.Append(urlToFilename(fXmlUrl));
|
|
||||||
SetCachePath(cache.Path());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// For pre-existing feed
|
|
||||||
Feed::Feed(BEntry entry)
|
|
||||||
: Feed()
|
|
||||||
{
|
|
||||||
BFile file(&entry, B_READ_ONLY);
|
|
||||||
BPath path;
|
|
||||||
entry.GetPath(&path);
|
|
||||||
SetCachePath(BString(path.Path()));
|
|
||||||
|
|
||||||
BString name, url;
|
|
||||||
file.ReadAttrString("META:url", &url);
|
|
||||||
file.ReadAttrString("Feed:name", &name);
|
|
||||||
file.ReadAttrString("Feed:hash", &fLastHash);
|
|
||||||
|
|
||||||
if (!url.IsEmpty())
|
|
||||||
SetXmlUrl(BUrl(url));
|
|
||||||
if (!name.IsEmpty())
|
|
||||||
SetTitle(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Feed::Feed(const char* pathStr)
|
|
||||||
: Feed(BEntry(pathStr))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// For new feed
|
|
||||||
Feed::Feed(BUrl xml, BEntry entry)
|
|
||||||
: Feed()
|
|
||||||
{
|
|
||||||
BPath path;
|
|
||||||
BString pathString;
|
|
||||||
entry.GetPath(&path);
|
|
||||||
pathString = path.Path();
|
|
||||||
|
|
||||||
if (entry.IsDirectory())
|
|
||||||
pathString += BString(urlToFilename(xml));
|
|
||||||
|
|
||||||
SetCachePath(pathString);
|
|
||||||
SetXmlUrl(xml);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,128 +33,10 @@ Feed::Feed(Feed* feed)
|
||||||
: Feed()
|
: Feed()
|
||||||
{
|
{
|
||||||
SetTitle(feed->Title());
|
SetTitle(feed->Title());
|
||||||
SetXmlUrl(feed->XmlUrl());
|
SetUrl(feed->Url());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Feed::Feed()
|
|
||||||
:
|
|
||||||
fTitle(BString(""))
|
|
||||||
{
|
|
||||||
fLastDate = BDateTime::CurrentDateTime(B_LOCAL_TIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Feed::~Feed()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
Feed::Parse()
|
|
||||||
{
|
|
||||||
BFile feedFile = BFile(fCachePath, B_READ_ONLY);
|
|
||||||
time_t tt_lastDate = 0;
|
|
||||||
|
|
||||||
feedFile.ReadAttr("Feed:when", B_TIME_TYPE, 0, &tt_lastDate, sizeof(time_t));
|
|
||||||
|
|
||||||
if (tt_lastDate > 0)
|
|
||||||
fLastDate.SetTime_t(tt_lastDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Download a remote feed's XML to the cache path.
|
|
||||||
bool
|
|
||||||
Feed::Fetch()
|
|
||||||
{
|
|
||||||
BFile cacheFile = BFile(fCachePath, B_READ_WRITE | B_CREATE_FILE);
|
|
||||||
|
|
||||||
int32 result = fetch(fXmlUrl, &cacheFile, &fHash, 30);
|
|
||||||
cacheFile.WriteAttrString("Feed:hash", &fHash);
|
|
||||||
|
|
||||||
if (result == 0)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
Feed::Filetize()
|
|
||||||
{
|
|
||||||
BFile feedFile(fCachePath, B_WRITE_ONLY | B_CREATE_FILE);
|
|
||||||
time_t tt_date = fDate.Time_t();
|
|
||||||
BString url = fXmlUrl.UrlString();
|
|
||||||
BString name = Title();
|
|
||||||
BString type("application/x-feed-source");
|
|
||||||
|
|
||||||
feedFile.WriteAttrString("Feed:name", &name);
|
|
||||||
feedFile.WriteAttrString("META:url", &url);
|
|
||||||
feedFile.WriteAttrString("BEOS:TYPE", &type);
|
|
||||||
feedFile.WriteAttr("Feed:when", B_TIME_TYPE, 0, &tt_date, sizeof(time_t));
|
|
||||||
feedFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, type.String(),
|
|
||||||
type.CountChars() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
Feed::Unfiletize()
|
|
||||||
{
|
|
||||||
BEntry entry(CachePath().String());
|
|
||||||
entry.Remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Feed::IsRss()
|
|
||||||
{
|
|
||||||
tinyxml2::XMLDocument xml;
|
|
||||||
xml.LoadFile(fCachePath.String());
|
|
||||||
|
|
||||||
if (xml.FirstChildElement("rss"))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Feed::IsAtom()
|
|
||||||
{
|
|
||||||
tinyxml2::XMLDocument xml;
|
|
||||||
xml.LoadFile(fCachePath.String());
|
|
||||||
|
|
||||||
if (xml.FirstChildElement("feed"))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Feed::IsUpdated()
|
|
||||||
{
|
|
||||||
return fLastHash != fHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Count the amount of siblings to an element of given type name
|
|
||||||
int
|
|
||||||
Feed::_XmlCountSiblings (tinyxml2::XMLElement* xsibling, const char* sibling_name)
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
while (xsibling) {
|
|
||||||
count++;
|
|
||||||
xsibling = xsibling->NextSiblingElement(sibling_name);
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Feed::_AddEntry (Entry* newEntry)
|
|
||||||
{
|
|
||||||
fEntries.AddItem(newEntry);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BObjectList<Entry>
|
BObjectList<Entry>
|
||||||
Feed::Entries()
|
Feed::Entries()
|
||||||
|
@ -228,22 +59,10 @@ Feed::NewEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
void
|
||||||
Feed::SetTitle(const char* titleStr)
|
Feed::SetEntries(BObjectList<Entry> entries)
|
||||||
{
|
{
|
||||||
if (titleStr != NULL)
|
fEntries = entries;
|
||||||
fTitle = BString(titleStr);
|
|
||||||
else return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Feed::_SetTitle(tinyxml2::XMLElement* elem)
|
|
||||||
{
|
|
||||||
if (elem != NULL && fTitle.IsEmpty() == true)
|
|
||||||
return SetTitle(elem->GetText());
|
|
||||||
else return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -255,65 +74,30 @@ Feed::Title()
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Feed::SetXmlUrl(BUrl newUrl)
|
Feed::SetTitle(const char* titleStr)
|
||||||
{
|
{
|
||||||
fXmlUrl = newUrl;
|
if (titleStr != NULL)
|
||||||
|
fTitle = BString(titleStr);
|
||||||
|
else return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BUrl
|
BUrl
|
||||||
Feed::XmlUrl()
|
Feed::Url()
|
||||||
{
|
{
|
||||||
return fXmlUrl;
|
return fUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Feed::SetCachePath(BString path)
|
Feed::SetUrl(BUrl newUrl)
|
||||||
{
|
{
|
||||||
fCachePath = path;
|
fUrl = newUrl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BString
|
|
||||||
Feed::CachePath()
|
|
||||||
{
|
|
||||||
return fCachePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Set the latest date given by feed (from entry or from root)
|
|
||||||
bool
|
|
||||||
Feed::_SetDate(BDateTime newDate)
|
|
||||||
{
|
|
||||||
if (newDate == NULL)
|
|
||||||
return false;
|
|
||||||
fDate = newDate;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Feed::_SetDate(const char* dateCStr)
|
|
||||||
{
|
|
||||||
if (dateCStr == NULL)
|
|
||||||
return false;
|
|
||||||
_SetDate(feedDateToBDate(dateCStr));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Feed::_SetDate(tinyxml2::XMLElement* elem)
|
|
||||||
{
|
|
||||||
if (elem != NULL)
|
|
||||||
return _SetDate(elem->GetText());
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BDateTime
|
BDateTime
|
||||||
Feed::Date()
|
Feed::Date()
|
||||||
{
|
{
|
||||||
|
@ -321,3 +105,77 @@ Feed::Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Set the latest date given by feed (from entry or from metadata)
|
||||||
|
bool
|
||||||
|
Feed::SetDate(BDateTime newDate)
|
||||||
|
{
|
||||||
|
if (newDate == NULL)
|
||||||
|
return false;
|
||||||
|
fDate = newDate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BDateTime
|
||||||
|
Feed::LastDate()
|
||||||
|
{
|
||||||
|
return fLastDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Set the latest time the feed was fetched and parsed
|
||||||
|
bool
|
||||||
|
Feed::SetLastDate(BDateTime newDate)
|
||||||
|
{
|
||||||
|
if (newDate == NULL)
|
||||||
|
return false;
|
||||||
|
fLastDate = newDate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
Feed::Hash()
|
||||||
|
{
|
||||||
|
return fHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Feed::SetHash(BString hash)
|
||||||
|
{
|
||||||
|
fHash = hash;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
Feed::LastHash()
|
||||||
|
{
|
||||||
|
return fLastHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Feed::SetLastHash(BString hash)
|
||||||
|
{
|
||||||
|
fLastHash = hash;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
Feed::Identifier()
|
||||||
|
{
|
||||||
|
return fIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Feed::SetIdentifier(BString id)
|
||||||
|
{
|
||||||
|
fIdentifier = id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
59
src/Feed.h
59
src/Feed.h
|
@ -21,57 +21,42 @@ class BUrl;
|
||||||
|
|
||||||
class Feed {
|
class Feed {
|
||||||
public:
|
public:
|
||||||
Feed(BUrl);
|
|
||||||
Feed(BUrl, BString);
|
|
||||||
Feed(BEntry);
|
|
||||||
Feed(const char* pathStr);
|
|
||||||
Feed(BUrl xml, BEntry entry);
|
|
||||||
Feed(Feed*);
|
|
||||||
Feed();
|
Feed();
|
||||||
|
Feed(const char* identifier, const char* title, const char* url);
|
||||||
~Feed();
|
Feed(Feed*);
|
||||||
|
|
||||||
virtual void Parse();
|
|
||||||
|
|
||||||
BObjectList<Entry> Entries();
|
BObjectList<Entry> Entries();
|
||||||
BObjectList<Entry> NewEntries();
|
BObjectList<Entry> NewEntries();
|
||||||
|
|
||||||
bool Fetch();
|
void SetEntries(BObjectList<Entry> entries);
|
||||||
|
|
||||||
void Filetize();
|
|
||||||
void Unfiletize();
|
|
||||||
|
|
||||||
bool IsRss();
|
|
||||||
bool IsAtom();
|
|
||||||
bool IsUpdated();
|
|
||||||
|
|
||||||
BString Title();
|
BString Title();
|
||||||
|
bool SetTitle(const char*);
|
||||||
|
|
||||||
|
BUrl Url();
|
||||||
|
bool SetUrl(BUrl newUrl);
|
||||||
|
|
||||||
|
BDateTime Date();
|
||||||
bool SetDate(BDateTime);
|
bool SetDate(BDateTime);
|
||||||
|
|
||||||
BUrl XmlUrl();
|
BDateTime LastDate();
|
||||||
BDateTime Date();
|
bool SetLastDate(BDateTime);
|
||||||
|
|
||||||
bool SetTitle(const char*);
|
BString Hash();
|
||||||
bool SetXmlUrl(BUrl newUrl);
|
bool SetHash(BString hash);
|
||||||
bool SetCachePath(BString path);
|
|
||||||
|
|
||||||
BString CachePath();
|
BString LastHash();
|
||||||
|
bool SetLastHash(BString hash);
|
||||||
|
|
||||||
|
BString Identifier();
|
||||||
|
bool SetIdentifier(BString id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool _SetTitle(tinyxml2::XMLElement*);
|
|
||||||
bool _SetDate(const char*);
|
|
||||||
bool _SetDate(tinyxml2::XMLElement*);
|
|
||||||
bool _SetDate(BDateTime newDate);
|
|
||||||
|
|
||||||
bool _AddEntry(Entry*);
|
|
||||||
|
|
||||||
int _XmlCountSiblings(tinyxml2::XMLElement*, const char*);
|
|
||||||
|
|
||||||
BString fTitle;
|
BString fTitle;
|
||||||
BDateTime fDate;
|
BDateTime fDate; // Lastest date (of post/update) given by feed
|
||||||
BDateTime fLastDate;
|
BDateTime fLastDate; // Last time feed was parsed
|
||||||
BUrl fXmlUrl;
|
BUrl fUrl;
|
||||||
BString fCachePath;
|
BString fIdentifier;
|
||||||
BString fHash;
|
BString fHash;
|
||||||
BString fLastHash;
|
BString fLastHash;
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,8 @@
|
||||||
#include <StringList.h>
|
#include <StringList.h>
|
||||||
|
|
||||||
#include "Daemon.h"
|
#include "Daemon.h"
|
||||||
#include "AtomFeed.h"
|
|
||||||
#include "Entry.h"
|
#include "Entry.h"
|
||||||
#include "LocalSource.h"
|
#include "LocalSource.h"
|
||||||
#include "RssFeed.h"
|
|
||||||
|
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
@ -32,7 +30,7 @@ FeedController::FeedController()
|
||||||
fMainThread(find_thread(NULL)),
|
fMainThread(find_thread(NULL)),
|
||||||
fDownloadThread(0),
|
fDownloadThread(0),
|
||||||
fParseThread(0),
|
fParseThread(0),
|
||||||
fDownloadQueue(new BStringList()),
|
fDownloadQueue(new BObjectList<Feed>()),
|
||||||
fMessageRunner(new BMessageRunner(be_app, BMessage(kControllerCheck), 50000, -1))
|
fMessageRunner(new BMessageRunner(be_app, BMessage(kControllerCheck), 50000, -1))
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -61,21 +59,20 @@ FeedController::MessageReceived(BMessage* msg)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
const void* data;
|
const void* data;
|
||||||
ssize_t size = sizeof(BString);
|
ssize_t size = sizeof(Feed);
|
||||||
|
|
||||||
BStringList paths;
|
|
||||||
msg->FindStrings("feedPaths", &paths);
|
|
||||||
fDownloadQueue->Add(paths);
|
|
||||||
|
|
||||||
|
while (msg->HasData("feeds", B_RAW_TYPE, i)) {
|
||||||
|
msg->FindData("feeds", B_RAW_TYPE, i, &data, &size);
|
||||||
|
fDownloadQueue->AddItem((Feed*)data);
|
||||||
|
_SendProgress();
|
||||||
|
}
|
||||||
fMessageRunner->SetCount(-1);
|
fMessageRunner->SetCount(-1);
|
||||||
_SendProgress();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kUpdateSubscribed:
|
case kUpdateSubscribed:
|
||||||
{
|
{
|
||||||
BStringList subFeeds = LocalSource::Feeds();
|
BObjectList<Feed> list = LocalSource::Feeds();
|
||||||
fDownloadQueue->Add(subFeeds);
|
fDownloadQueue->AddList(&list);
|
||||||
|
|
||||||
_SendProgress();
|
_SendProgress();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -97,7 +94,7 @@ FeedController::MessageReceived(BMessage* msg)
|
||||||
void
|
void
|
||||||
FeedController::_SendProgress()
|
FeedController::_SendProgress()
|
||||||
{
|
{
|
||||||
int32 dqCount = fDownloadQueue->CountStrings();
|
int32 dqCount = fDownloadQueue->CountItems();
|
||||||
|
|
||||||
if (fEnqueuedTotal < dqCount)
|
if (fEnqueuedTotal < dqCount)
|
||||||
fEnqueuedTotal = dqCount;
|
fEnqueuedTotal = dqCount;
|
||||||
|
@ -116,19 +113,13 @@ void
|
||||||
FeedController::_ProcessQueueItem()
|
FeedController::_ProcessQueueItem()
|
||||||
{
|
{
|
||||||
if (has_data(fDownloadThread) && !fDownloadQueue->IsEmpty()) {
|
if (has_data(fDownloadThread) && !fDownloadQueue->IsEmpty()) {
|
||||||
BString feed = fDownloadQueue->Remove(0);
|
Feed* feed = fDownloadQueue->ItemAt(0);
|
||||||
Feed* buffer;
|
fDownloadQueue->RemoveItemAt(0);
|
||||||
|
send_data(fDownloadThread, 0, (void*)feed, sizeof(Feed));
|
||||||
if (BUrl(feed.String()).IsValid() == true)
|
|
||||||
buffer = new Feed(BUrl(feed.String()));
|
|
||||||
else
|
|
||||||
buffer = new Feed(feed.String());
|
|
||||||
|
|
||||||
send_data(fDownloadThread, 0, (void*)buffer, sizeof(Feed));
|
|
||||||
|
|
||||||
BMessage downloadInit = BMessage(kDownloadStart);
|
BMessage downloadInit = BMessage(kDownloadStart);
|
||||||
downloadInit.AddString("feed_name", buffer->Title());
|
downloadInit.AddString("feed_name", feed->Title());
|
||||||
downloadInit.AddString("feed_url", buffer->XmlUrl().UrlString());
|
downloadInit.AddString("feed_url", feed->Url().UrlString());
|
||||||
((App*)be_app)->MessageReceived(&downloadInit);
|
((App*)be_app)->MessageReceived(&downloadInit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,8 +140,7 @@ FeedController::_ReceiveStatus()
|
||||||
{
|
{
|
||||||
BMessage complete = BMessage(kDownloadComplete);
|
BMessage complete = BMessage(kDownloadComplete);
|
||||||
complete.AddString("feed_name", feedBuffer->Title());
|
complete.AddString("feed_name", feedBuffer->Title());
|
||||||
complete.AddString("feed_url",
|
complete.AddString("feed_url", feedBuffer->Url().UrlString());
|
||||||
feedBuffer->XmlUrl().UrlString());
|
|
||||||
((App*)be_app)->MessageReceived(&complete);
|
((App*)be_app)->MessageReceived(&complete);
|
||||||
|
|
||||||
send_data(fParseThread, 0, (void*)feedBuffer, sizeof(Feed));
|
send_data(fParseThread, 0, (void*)feedBuffer, sizeof(Feed));
|
||||||
|
@ -161,7 +151,7 @@ FeedController::_ReceiveStatus()
|
||||||
BMessage failure = BMessage(kDownloadFail);
|
BMessage failure = BMessage(kDownloadFail);
|
||||||
failure.AddString("feed_name", feedBuffer->Title());
|
failure.AddString("feed_name", feedBuffer->Title());
|
||||||
failure.AddString("feed_url",
|
failure.AddString("feed_url",
|
||||||
feedBuffer->XmlUrl().UrlString());
|
feedBuffer->Url().UrlString());
|
||||||
((App*)be_app)->MessageReceived(&failure);
|
((App*)be_app)->MessageReceived(&failure);
|
||||||
_SendProgress();
|
_SendProgress();
|
||||||
break;
|
break;
|
||||||
|
@ -170,7 +160,7 @@ FeedController::_ReceiveStatus()
|
||||||
{
|
{
|
||||||
BMessage failure = BMessage(kParseFail);
|
BMessage failure = BMessage(kParseFail);
|
||||||
failure.AddString("feed_name", feedBuffer->Title());
|
failure.AddString("feed_name", feedBuffer->Title());
|
||||||
failure.AddString("feed_url", feedBuffer->XmlUrl().UrlString());
|
failure.AddString("feed_url", feedBuffer->Url().UrlString());
|
||||||
((App*)be_app)->MessageReceived(&failure);
|
((App*)be_app)->MessageReceived(&failure);
|
||||||
_SendProgress();
|
_SendProgress();
|
||||||
break;
|
break;
|
||||||
|
@ -179,7 +169,7 @@ FeedController::_ReceiveStatus()
|
||||||
default:
|
default:
|
||||||
BMessage complete = BMessage(kParseComplete);
|
BMessage complete = BMessage(kParseComplete);
|
||||||
complete.AddString("feed_name", feedBuffer->Title());
|
complete.AddString("feed_name", feedBuffer->Title());
|
||||||
complete.AddString("feed_url", feedBuffer->XmlUrl().UrlString());
|
complete.AddString("feed_url", feedBuffer->Url().UrlString());
|
||||||
complete.AddInt32("entry_count", code);
|
complete.AddInt32("entry_count", code);
|
||||||
((App*)be_app)->MessageReceived(&complete);
|
((App*)be_app)->MessageReceived(&complete);
|
||||||
_SendProgress();
|
_SendProgress();
|
||||||
|
@ -201,9 +191,9 @@ FeedController::_DownloadLoop(void* data)
|
||||||
receive_data(&sender, (void*)feedBuffer, sizeof(Feed));
|
receive_data(&sender, (void*)feedBuffer, sizeof(Feed));
|
||||||
|
|
||||||
std::cout << B_TRANSLATE("Downloading feed from ")
|
std::cout << B_TRANSLATE("Downloading feed from ")
|
||||||
<< feedBuffer->XmlUrl().UrlString() << "…\n";
|
<< feedBuffer->Url().UrlString() << "…\n";
|
||||||
|
|
||||||
if (feedBuffer->Fetch()) {
|
if (LocalSource::Fetch(feedBuffer)) {
|
||||||
send_data(main, kDownloadComplete, (void*)feedBuffer, sizeof(Feed));
|
send_data(main, kDownloadComplete, (void*)feedBuffer, sizeof(Feed));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -228,39 +218,19 @@ FeedController::_ParseLoop(void* data)
|
||||||
BObjectList<Entry> entries;
|
BObjectList<Entry> entries;
|
||||||
int32 entriesCount = 0;
|
int32 entriesCount = 0;
|
||||||
BString feedTitle;
|
BString feedTitle;
|
||||||
BUrl feedUrl = feedBuffer->XmlUrl();
|
BUrl feedUrl = feedBuffer->Url();
|
||||||
|
|
||||||
if (feedBuffer->IsAtom() && feedBuffer->IsUpdated()) {
|
LocalSource::Parse(feedBuffer);
|
||||||
AtomFeed feed(feedBuffer);
|
entries = feedBuffer->NewEntries();
|
||||||
feed.Parse();
|
entriesCount = entries.CountItems();
|
||||||
entries = feed.NewEntries();
|
feedTitle = feedBuffer->Title();
|
||||||
entriesCount = entries.CountItems();
|
|
||||||
feedTitle = feed.Title();
|
|
||||||
|
|
||||||
for (int i = 0; i < entriesCount; i++)
|
for (int i = 0; i < entriesCount; i++)
|
||||||
entries.ItemAt(i)->Filetize();
|
entries.ItemAt(i)->Filetize();
|
||||||
entries.MakeEmpty();
|
entries.MakeEmpty();
|
||||||
}
|
|
||||||
else if (feedBuffer->IsRss() && feedBuffer->IsUpdated()) {
|
|
||||||
RssFeed feed(feedBuffer);
|
|
||||||
feed.Parse();
|
|
||||||
entries = feed.NewEntries();
|
|
||||||
entriesCount = entries.CountItems();
|
|
||||||
feedTitle = feed.Title();
|
|
||||||
|
|
||||||
for (int i = 0; i < entriesCount; i++)
|
|
||||||
entries.ItemAt(i)->Filetize();
|
|
||||||
entries.MakeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (feedBuffer->IsAtom() || feedBuffer->IsRss()) {
|
|
||||||
send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
LocalSource::EditFeed(feedBuffer);
|
||||||
|
send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed));
|
||||||
free(feedBuffer);
|
free(feedBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,6 @@ public:
|
||||||
|
|
||||||
void MessageReceived(BMessage* msg);
|
void MessageReceived(BMessage* msg);
|
||||||
|
|
||||||
static BStringList SubscribedFeeds();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _SendProgress();
|
void _SendProgress();
|
||||||
|
|
||||||
|
@ -54,7 +52,7 @@ private:
|
||||||
thread_id fDownloadThread;
|
thread_id fDownloadThread;
|
||||||
thread_id fParseThread;
|
thread_id fParseThread;
|
||||||
|
|
||||||
BStringList* fDownloadQueue;
|
BObjectList<Feed>* fDownloadQueue;
|
||||||
BMessageRunner* fMessageRunner;
|
BMessageRunner* fMessageRunner;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "FeedController.h"
|
#include "FeedController.h"
|
||||||
#include "FeedListItem.h"
|
#include "FeedListItem.h"
|
||||||
#include "FeedsView.h"
|
#include "FeedsView.h"
|
||||||
|
#include "LocalSource.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,26 +34,18 @@ FeedEditWindow::FeedEditWindow()
|
||||||
{
|
{
|
||||||
_InitInterface();
|
_InitInterface();
|
||||||
MoveOnScreen();
|
MoveOnScreen();
|
||||||
fFeed = Feed();
|
fFeed = new Feed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FeedEditWindow::FeedEditWindow(BEntry feedEntry)
|
FeedEditWindow::FeedEditWindow(BString identifier)
|
||||||
:
|
: FeedEditWindow()
|
||||||
FeedEditWindow()
|
|
||||||
{
|
{
|
||||||
SetTitle(B_TRANSLATE("Edit feed"));
|
SetTitle(B_TRANSLATE("Edit feed"));
|
||||||
fFeed = Feed(feedEntry);
|
fFeed = LocalSource::GetFeed(identifier);
|
||||||
|
|
||||||
fFeedNameText->SetText(fFeed.Title().String());
|
fFeedNameText->SetText(fFeed->Title().String());
|
||||||
fFeedUrlText->SetText(fFeed.XmlUrl().UrlString().String());
|
fFeedUrlText->SetText(fFeed->Url().UrlString().String());
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FeedEditWindow::FeedEditWindow(FeedListItem* feedItem)
|
|
||||||
:
|
|
||||||
FeedEditWindow(BEntry(feedItem->FeedPath()))
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,22 +121,6 @@ FeedEditWindow::_InitInterface()
|
||||||
void
|
void
|
||||||
FeedEditWindow::_SaveFeed()
|
FeedEditWindow::_SaveFeed()
|
||||||
{
|
{
|
||||||
BPath subPath;
|
|
||||||
find_directory(B_USER_SETTINGS_DIRECTORY, &subPath);
|
|
||||||
subPath.Append("Pogger");
|
|
||||||
subPath.Append("Subscriptions");
|
|
||||||
BDirectory subDir(subPath.Path());
|
|
||||||
if (subDir.InitCheck() == B_ENTRY_NOT_FOUND) {
|
|
||||||
subDir.CreateDirectory(subPath.Path(), &subDir);
|
|
||||||
|
|
||||||
BPath defaultSubPath(subPath);
|
|
||||||
defaultSubPath.Append("Haiku Project");
|
|
||||||
Feed defaultSub(BUrl("https://www.haiku-os.org/blog/index.xml"),
|
|
||||||
BEntry(defaultSubPath.Path()));
|
|
||||||
defaultSub.SetTitle("Haiku Project");
|
|
||||||
defaultSub.Filetize();
|
|
||||||
}
|
|
||||||
|
|
||||||
BString title(fFeedNameText->Text());
|
BString title(fFeedNameText->Text());
|
||||||
const char* urlString = fFeedUrlText->Text();
|
const char* urlString = fFeedUrlText->Text();
|
||||||
BUrl url = BUrl(urlString);
|
BUrl url = BUrl(urlString);
|
||||||
|
@ -165,26 +142,20 @@ FeedEditWindow::_SaveFeed()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BString filename;
|
|
||||||
if (title.IsEmpty())
|
|
||||||
filename = BString(urlToFilename(BUrl(urlString)));
|
|
||||||
else
|
|
||||||
filename = BString(title);
|
|
||||||
subPath.Append(filename);
|
|
||||||
|
|
||||||
if (fFeed.CachePath().IsEmpty())
|
|
||||||
fFeed.SetCachePath(BString(subPath.Path()));
|
|
||||||
|
|
||||||
if (!title.IsEmpty())
|
if (!title.IsEmpty())
|
||||||
fFeed.SetTitle(title.String());
|
fFeed->SetTitle(title.String());
|
||||||
fFeed.SetXmlUrl(BUrl(urlString));
|
fFeed->SetUrl(BUrl(urlString));
|
||||||
fFeed.Filetize();
|
|
||||||
|
if (fFeed->Identifier().IsEmpty() == true)
|
||||||
|
LocalSource::AddFeed(fFeed);
|
||||||
|
else
|
||||||
|
LocalSource::EditFeed(fFeed);
|
||||||
|
|
||||||
BMessage edited(kFeedsEdited);
|
BMessage edited(kFeedsEdited);
|
||||||
BMessage enqueueUpdated(kEnqueueFeed);
|
// BMessage enqueueUpdated(kEnqueueFeed);
|
||||||
enqueueUpdated.AddString("feedPaths", fFeed.CachePath());
|
// enqueueUpdated.AddData("feeds", B_RAW_TYPE, &fFeed, sizeof(Feed));
|
||||||
|
//
|
||||||
((App*)be_app)->MessageReceived(&enqueueUpdated);
|
// ((App*)be_app)->MessageReceived(&enqueueUpdated);
|
||||||
((App*)be_app)->PostMessage(&edited);
|
((App*)be_app)->PostMessage(&edited);
|
||||||
Quit();
|
Quit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,7 @@ enum
|
||||||
class FeedEditWindow : public BWindow {
|
class FeedEditWindow : public BWindow {
|
||||||
public:
|
public:
|
||||||
FeedEditWindow();
|
FeedEditWindow();
|
||||||
FeedEditWindow(BEntry feedEntry);
|
FeedEditWindow(BString feedIdentifier);
|
||||||
FeedEditWindow(FeedListItem* feedItem);
|
|
||||||
~FeedEditWindow();
|
~FeedEditWindow();
|
||||||
|
|
||||||
void MessageReceived(BMessage* msg);
|
void MessageReceived(BMessage* msg);
|
||||||
|
@ -38,7 +37,7 @@ private:
|
||||||
void _SaveFeed();
|
void _SaveFeed();
|
||||||
|
|
||||||
BString fFeedPath;
|
BString fFeedPath;
|
||||||
Feed fFeed;
|
Feed* fFeed;
|
||||||
|
|
||||||
BStringView* fFeedNameLabel;
|
BStringView* fFeedNameLabel;
|
||||||
BTextControl* fFeedNameText;
|
BTextControl* fFeedNameText;
|
||||||
|
|
|
@ -20,8 +20,8 @@ FeedListItem::FeedListItem(Feed* feed)
|
||||||
:
|
:
|
||||||
BStringItem(feed->Title().String(), 0, false),
|
BStringItem(feed->Title().String(), 0, false),
|
||||||
fStatus(kClearStatus),
|
fStatus(kClearStatus),
|
||||||
fFeedUrl(feed->XmlUrl()),
|
fFeedUrl(feed->Url()),
|
||||||
fFeedPath(feed->CachePath())
|
fFeedIdentifier(feed->Identifier())
|
||||||
{
|
{
|
||||||
if (feed->Title().IsEmpty() == true)
|
if (feed->Title().IsEmpty() == true)
|
||||||
SetText(B_TRANSLATE("Untitled Feed"));
|
SetText(B_TRANSLATE("Untitled Feed"));
|
||||||
|
@ -59,9 +59,9 @@ FeedListItem::DrawItem(BView* owner, BRect frame, bool complete)
|
||||||
|
|
||||||
|
|
||||||
BString
|
BString
|
||||||
FeedListItem::FeedPath()
|
FeedListItem::FeedIdentifier()
|
||||||
{
|
{
|
||||||
return fFeedPath;
|
return fFeedIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,14 +29,14 @@ public:
|
||||||
void DrawItem(BView* owner, BRect frame, bool complete);
|
void DrawItem(BView* owner, BRect frame, bool complete);
|
||||||
|
|
||||||
BUrl FeedUrl();
|
BUrl FeedUrl();
|
||||||
BString FeedPath();
|
BString FeedIdentifier();
|
||||||
|
|
||||||
void SetStatus(int8 status);
|
void SetStatus(int8 status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int8 fStatus;
|
int8 fStatus;
|
||||||
BUrl fFeedUrl;
|
BUrl fFeedUrl;
|
||||||
BString fFeedPath;
|
BString fFeedIdentifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "FeedSource.h"
|
|
||||||
|
|
|
@ -7,14 +7,17 @@
|
||||||
|
|
||||||
#include <StringList.h>
|
#include <StringList.h>
|
||||||
|
|
||||||
|
#include "Feed.h"
|
||||||
|
|
||||||
|
|
||||||
// Hierarchy: FeedSource → Feed → Entry
|
// Hierarchy: FeedSource → Feed → Entry
|
||||||
// TODO: RemoveFeed(), AddFeed(), etc.
|
// TODO: RemoveFeed(), AddFeed(), etc.
|
||||||
class FeedSource {
|
class FeedSource {
|
||||||
public:
|
public:
|
||||||
BStringList Feeds();
|
static BStringList FeedIdentifiers();
|
||||||
|
|
||||||
const char* name = "";
|
Feed RemoveFeed(Feed feed);
|
||||||
|
Feed RemoveFeed(const char* identifier);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -189,7 +189,7 @@ FeedsView::_EditSelectedFeed()
|
||||||
{
|
{
|
||||||
int32 selIndex = fFeedsListView->CurrentSelection();
|
int32 selIndex = fFeedsListView->CurrentSelection();
|
||||||
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
|
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
|
||||||
FeedEditWindow* edit = new FeedEditWindow(selected);
|
FeedEditWindow* edit = new FeedEditWindow(selected->FeedIdentifier());
|
||||||
|
|
||||||
edit->Show();
|
edit->Show();
|
||||||
edit->Activate();
|
edit->Activate();
|
||||||
|
@ -211,24 +211,23 @@ FeedsView::_RemoveSelectedFeed()
|
||||||
|
|
||||||
int32 selIndex = fFeedsListView->CurrentSelection();
|
int32 selIndex = fFeedsListView->CurrentSelection();
|
||||||
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
|
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
|
||||||
Feed delFeed = Feed(BEntry(selected->FeedPath()));
|
|
||||||
|
|
||||||
delFeed.Unfiletize();
|
LocalSource::RemoveFeed(LocalSource::GetFeed(selected->FeedIdentifier()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
FeedsView::_PopulateFeedList()
|
FeedsView::_PopulateFeedList()
|
||||||
{
|
{
|
||||||
BStringList feeds = LocalSource::Feeds();
|
BObjectList<Feed> feeds = LocalSource::Feeds();
|
||||||
int32 selected = fFeedsListView->CurrentSelection();
|
int32 selected = fFeedsListView->CurrentSelection();
|
||||||
|
|
||||||
for (int i = fFeedsListView->CountItems(); i >= 0; i--)
|
for (int i = fFeedsListView->CountItems(); i >= 0; i--)
|
||||||
delete ((FeedListItem*)fFeedsListView->RemoveItem(i));
|
delete ((FeedListItem*)fFeedsListView->RemoveItem(i));
|
||||||
|
|
||||||
for (int i = 0; i < feeds.CountStrings(); i++) {
|
for (int i = 0; i < feeds.CountItems(); i++) {
|
||||||
Feed feed = Feed(feeds.StringAt(i).String());
|
Feed* feed = feeds.ItemAt(i);
|
||||||
FeedListItem* item = new FeedListItem(&feed);
|
FeedListItem* item = new FeedListItem(feed);
|
||||||
fFeedsListView->AddItem(item);
|
fFeedsListView->AddItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,40 +5,432 @@
|
||||||
|
|
||||||
#include "LocalSource.h"
|
#include "LocalSource.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <Catalog.h>
|
||||||
#include <Directory.h>
|
#include <Directory.h>
|
||||||
#include <Entry.h>
|
#include <Entry.h>
|
||||||
|
#include <File.h>
|
||||||
#include <FindDirectory.h>
|
#include <FindDirectory.h>
|
||||||
#include <Path.h>
|
#include <Path.h>
|
||||||
|
|
||||||
#include "Feed.h"
|
#include "Feed.h"
|
||||||
|
#include "Util.h"
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "LocalSource"
|
||||||
|
|
||||||
|
|
||||||
BStringList
|
LocalSource::LocalSource()
|
||||||
|
{
|
||||||
|
BDirectory subDir = BDirectory(_SubscriptionPath().Path());
|
||||||
|
if (subDir.InitCheck() == B_ENTRY_NOT_FOUND) {
|
||||||
|
// subDir.CreateDirectory(subPath.Path(), &subDir);
|
||||||
|
|
||||||
|
// subPath.Append("Haiku Project");
|
||||||
|
// Feed defaultSub(BUrl("https://www.haiku-os.org/blog/index.xml"),
|
||||||
|
// BEntry(subPath.Path()));
|
||||||
|
// defaultSub.SetTitle("Haiku Project");
|
||||||
|
// defaultSub.Filetize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BObjectList<Feed>
|
||||||
LocalSource::Feeds()
|
LocalSource::Feeds()
|
||||||
|
{
|
||||||
|
BDirectory subDir = BDirectory(_SubscriptionPath().Path());
|
||||||
|
BEntry feedEntry;
|
||||||
|
BPath feedPath;
|
||||||
|
|
||||||
|
BObjectList<Feed> feeds;
|
||||||
|
|
||||||
|
while (subDir.GetNextEntry(&feedEntry) == B_OK
|
||||||
|
&& feedEntry.GetPath(&feedPath) == B_OK)
|
||||||
|
{
|
||||||
|
Feed* feed = GetFeed(feedPath.Path());
|
||||||
|
feeds.AddItem(feed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return feeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Feed*
|
||||||
|
LocalSource::GetFeed(const char* identifier)
|
||||||
|
{
|
||||||
|
BFile file(identifier, B_READ_ONLY);
|
||||||
|
time_t tt_lastDate = 0;
|
||||||
|
BDateTime lastDate;
|
||||||
|
BString title;
|
||||||
|
BString url;
|
||||||
|
BString hash;
|
||||||
|
|
||||||
|
file.ReadAttrString("Feed:name", &title);
|
||||||
|
file.ReadAttrString("META:url", &url);
|
||||||
|
file.ReadAttrString("Feed:hash", &hash);
|
||||||
|
file.ReadAttr("Feed:when", B_TIME_TYPE, 0, &tt_lastDate, sizeof(time_t));
|
||||||
|
lastDate.SetTime_t(tt_lastDate);
|
||||||
|
|
||||||
|
Feed* feed = new Feed(identifier, title.String(), url.String());
|
||||||
|
feed->SetHash(hash);
|
||||||
|
feed->SetLastDate(lastDate);
|
||||||
|
return feed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::Fetch(Feed* feed)
|
||||||
|
{
|
||||||
|
BFile cacheFile = BFile(feed->Identifier(), B_READ_WRITE | B_CREATE_FILE);
|
||||||
|
BString hash;
|
||||||
|
|
||||||
|
int32 result = fetch(feed->Url(), &cacheFile, &hash, 30);
|
||||||
|
feed->SetHash(hash);
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::Parse(Feed* feed)
|
||||||
|
{
|
||||||
|
if (IsUpdated(feed) == false)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (_IsAtom(feed) == true)
|
||||||
|
return _AtomParse(feed);
|
||||||
|
else if (_IsRss(feed) == true)
|
||||||
|
return _RssParse(feed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
LocalSource::AddFeed(Feed* newFeed)
|
||||||
|
{
|
||||||
|
if (newFeed->Identifier().IsEmpty() == true) {
|
||||||
|
BPath subPath = _SubscriptionPath();
|
||||||
|
subPath.Append(urlToFilename(newFeed->Url()));
|
||||||
|
newFeed->SetIdentifier(subPath.Path());
|
||||||
|
}
|
||||||
|
EditFeed(newFeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
LocalSource::EditFeed(Feed* updated)
|
||||||
|
{
|
||||||
|
Feed* old = GetFeed(updated->Identifier());
|
||||||
|
BFile file(updated->Identifier(), B_READ_WRITE | B_CREATE_FILE);
|
||||||
|
|
||||||
|
if (old->Title() != updated->Title()) {
|
||||||
|
BString title = updated->Title();
|
||||||
|
file.WriteAttrString("Feed:name", &title);
|
||||||
|
}
|
||||||
|
if (old->Url() != updated->Url()) {
|
||||||
|
BString url = updated->Url().UrlString();
|
||||||
|
file.WriteAttrString("META:url", &url);
|
||||||
|
}
|
||||||
|
if (old->Hash() != updated->Hash()) {
|
||||||
|
BString hash = updated->Hash();
|
||||||
|
file.WriteAttrString("Feed:hash", &hash);
|
||||||
|
}
|
||||||
|
if (old->Date() != updated->Date()) {
|
||||||
|
time_t tt_date = updated->Date().Time_t();
|
||||||
|
file.WriteAttr("Feed:when", B_TIME_TYPE, 0, &tt_date, sizeof(time_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
BString type("application/x-feed-source");
|
||||||
|
file.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, type.String(),
|
||||||
|
type.CountChars() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
LocalSource::RemoveFeed(Feed* mortonta)
|
||||||
|
{
|
||||||
|
BEntry entry(mortonta->Identifier().String());
|
||||||
|
entry.Remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::IsUpdated(Feed* feed)
|
||||||
|
{
|
||||||
|
return (GetFeed(feed->Identifier())->Hash() != feed->Hash());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::_IsAtom(Feed* feed)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLDocument xml;
|
||||||
|
xml.LoadFile(feed->Identifier().String());
|
||||||
|
|
||||||
|
if (xml.FirstChildElement("feed"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::_IsRss(Feed* feed)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLDocument xml;
|
||||||
|
xml.LoadFile(feed->Identifier().String());
|
||||||
|
|
||||||
|
if (xml.FirstChildElement("rss"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::_AtomParse(Feed* atomFeed)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLDocument xml;
|
||||||
|
xml.LoadFile(atomFeed->Identifier().String());
|
||||||
|
|
||||||
|
tinyxml2::XMLElement* xfeed = xml.FirstChildElement("feed");
|
||||||
|
|
||||||
|
_AtomRootParse(atomFeed, xfeed);
|
||||||
|
_AtomEntriesParse(atomFeed, xfeed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
LocalSource::_AtomRootParse(Feed* feed, tinyxml2::XMLElement* xfeed)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLElement* xentry = xfeed->FirstChildElement("entry");
|
||||||
|
|
||||||
|
bool set = false;
|
||||||
|
|
||||||
|
_SetTitle(feed, xfeed->FirstChildElement("title"));
|
||||||
|
|
||||||
|
set = _SetDate(feed, xfeed->FirstChildElement("updated"));
|
||||||
|
if (!set)
|
||||||
|
set = _SetDate(feed, xfeed->FirstChildElement("published"));
|
||||||
|
if (!set && xentry)
|
||||||
|
set = _SetDate(feed, xentry->FirstChildElement("updated"));
|
||||||
|
if (!set && xentry)
|
||||||
|
set = _SetDate(feed, xentry->FirstChildElement("published"));
|
||||||
|
|
||||||
|
BString logString(B_TRANSLATE("Channel '%source%' at %url%:\n"));
|
||||||
|
logString.ReplaceAll("%source%", feed->Title().String());
|
||||||
|
logString.ReplaceAll("%url%", feed->Url().UrlString());
|
||||||
|
|
||||||
|
std::cout << logString.String();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
LocalSource::_AtomEntriesParse(Feed* feed, tinyxml2::XMLElement* xfeed)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLElement* xentry;
|
||||||
|
|
||||||
|
xentry = xfeed->FirstChildElement("entry");
|
||||||
|
|
||||||
|
int entryCount = _XmlCountSiblings(xentry, "entry");
|
||||||
|
BObjectList<Entry> entries(entryCount, true);
|
||||||
|
|
||||||
|
BString logString(B_TRANSLATE("\t-%count% entries-\n"));
|
||||||
|
BString entryStr;
|
||||||
|
entryStr << entryCount;
|
||||||
|
logString.ReplaceAll("%count%", entryStr);
|
||||||
|
|
||||||
|
std::cout << logString.String();
|
||||||
|
|
||||||
|
while (xentry) {
|
||||||
|
Entry* newEntry = _AtomEntryParse(feed, xentry);
|
||||||
|
if (newEntry != NULL)
|
||||||
|
entries.AddItem(newEntry);
|
||||||
|
xentry = xentry->NextSiblingElement("entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
feed->SetEntries(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Entry*
|
||||||
|
LocalSource::_AtomEntryParse(Feed* feed, tinyxml2::XMLElement* xentry)
|
||||||
|
{
|
||||||
|
Entry* newEntry = new Entry();
|
||||||
|
|
||||||
|
tinyxml2::XMLElement* xcontent = xentry->FirstChildElement("content");
|
||||||
|
tinyxml2::XMLElement* xmedia = xentry->FirstChildElement("media:group");
|
||||||
|
tinyxml2::XMLPrinter xprinter;
|
||||||
|
|
||||||
|
newEntry->SetTitle(xentry->FirstChildElement("title"));
|
||||||
|
newEntry->SetPostUrl(xentry->FirstChildElement("link")->Attribute("href"));
|
||||||
|
newEntry->SetFeedTitle(feed->Title());
|
||||||
|
|
||||||
|
bool set = false;
|
||||||
|
set = newEntry->SetDescription(xentry->FirstChildElement("summary"));
|
||||||
|
if (!set)
|
||||||
|
set = newEntry->SetDescription(xentry->FirstChildElement("description"));
|
||||||
|
if (!set && xmedia)
|
||||||
|
set = newEntry->SetDescription(
|
||||||
|
xmedia->FirstChildElement("media:description"));
|
||||||
|
|
||||||
|
set = _SetDate(newEntry, xentry->FirstChildElement("updated"));
|
||||||
|
if (!set)
|
||||||
|
set = _SetDate(newEntry, xentry->FirstChildElement("published"));
|
||||||
|
|
||||||
|
if (feed->Date() == NULL || feed->Date() < newEntry->Date())
|
||||||
|
feed->SetDate(newEntry->Date());
|
||||||
|
|
||||||
|
if (xcontent) {
|
||||||
|
xcontent->Accept(&xprinter);
|
||||||
|
newEntry->SetContent(xprinter.CStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
return newEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::_RssParse(Feed* feed)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLDocument xml;
|
||||||
|
xml.LoadFile(feed->Identifier().String());
|
||||||
|
|
||||||
|
tinyxml2::XMLElement* xchan = xml.FirstChildElement("rss")->FirstChildElement("channel");
|
||||||
|
|
||||||
|
_RssRootParse(feed, xchan);
|
||||||
|
_RssEntriesParse(feed, xchan);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
LocalSource::_RssRootParse(Feed* feed, tinyxml2::XMLElement* xchan)
|
||||||
|
{
|
||||||
|
_SetTitle(feed, xchan->FirstChildElement("title"));
|
||||||
|
_SetDate(feed, xchan->FirstChildElement("lastBuildDate"));
|
||||||
|
|
||||||
|
BString logString(B_TRANSLATE("Channel '%source%' at %url%:\n"));
|
||||||
|
logString.ReplaceAll("%source%", feed->Title().String());
|
||||||
|
logString.ReplaceAll("%url%", feed->Url().UrlString());
|
||||||
|
|
||||||
|
std::cout << logString.String();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
LocalSource::_RssEntriesParse(Feed* feed, tinyxml2::XMLElement* xchan)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLElement* xitem;
|
||||||
|
|
||||||
|
xitem = xchan->FirstChildElement("item");
|
||||||
|
|
||||||
|
int entryCount = _XmlCountSiblings(xitem, "item");
|
||||||
|
BObjectList<Entry> entries = BObjectList<Entry>(entryCount, true);
|
||||||
|
|
||||||
|
BString logString(B_TRANSLATE("\t-%count% entries-\n"));
|
||||||
|
BString entryStr;
|
||||||
|
entryStr << entryCount;
|
||||||
|
logString.ReplaceAll("%count%", entryStr);
|
||||||
|
|
||||||
|
std::cout << logString.String();
|
||||||
|
|
||||||
|
while (xitem) {
|
||||||
|
entries.AddItem(_RssEntryParse(feed, xitem));
|
||||||
|
xitem = xitem->NextSiblingElement("item");
|
||||||
|
}
|
||||||
|
feed->SetEntries(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Entry*
|
||||||
|
LocalSource::_RssEntryParse(Feed* feed, tinyxml2::XMLElement* xitem)
|
||||||
|
{
|
||||||
|
Entry* newEntry = new Entry();
|
||||||
|
|
||||||
|
newEntry->SetTitle(xitem->FirstChildElement("title"));
|
||||||
|
newEntry->SetDescription(xitem->FirstChildElement("description"));
|
||||||
|
_SetDate(newEntry, xitem->FirstChildElement("pubDate"));
|
||||||
|
newEntry->SetPostUrl(xitem->FirstChildElement("link"));
|
||||||
|
newEntry->SetContent(xitem->FirstChildElement("content:encoded"));
|
||||||
|
newEntry->SetFeedTitle(feed->Title());
|
||||||
|
|
||||||
|
if (feed->Date() == NULL || feed->Date() < newEntry->Date())
|
||||||
|
feed->SetDate(newEntry->Date());
|
||||||
|
|
||||||
|
return newEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::_SetTitle(Feed* feed, tinyxml2::XMLElement* elem)
|
||||||
|
{
|
||||||
|
if (elem != NULL && feed->Title().IsEmpty() == true)
|
||||||
|
return feed->SetTitle(elem->GetText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::_SetDate(Feed* feed, const char* dateCStr)
|
||||||
|
{
|
||||||
|
if (dateCStr == NULL)
|
||||||
|
return false;
|
||||||
|
return feed->SetDate(feedDateToBDate(dateCStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::_SetDate(Feed* feed, tinyxml2::XMLElement* elem)
|
||||||
|
{
|
||||||
|
if (elem == NULL)
|
||||||
|
return false;
|
||||||
|
return _SetDate(feed, elem->GetText());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::_SetDate(Entry* entry, const char* dateStr)
|
||||||
|
{
|
||||||
|
if (dateStr == NULL)
|
||||||
|
return false;
|
||||||
|
return entry->SetDate(feedDateToBDate(dateStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
LocalSource::_SetDate(Entry* entry, tinyxml2::XMLElement* elem)
|
||||||
|
{
|
||||||
|
if (elem == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return _SetDate(entry, elem->GetText());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Count the amount of siblings to an element of given type name
|
||||||
|
int
|
||||||
|
LocalSource::_XmlCountSiblings (tinyxml2::XMLElement* xsibling,
|
||||||
|
const char* sibling_name)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
while (xsibling) {
|
||||||
|
count++;
|
||||||
|
xsibling = xsibling->NextSiblingElement(sibling_name);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BPath
|
||||||
|
LocalSource::_SubscriptionPath()
|
||||||
{
|
{
|
||||||
BPath subPath;
|
BPath subPath;
|
||||||
find_directory(B_USER_SETTINGS_DIRECTORY, &subPath);
|
find_directory(B_USER_SETTINGS_DIRECTORY, &subPath);
|
||||||
subPath.Append("Pogger");
|
subPath.Append("Pogger");
|
||||||
subPath.Append("Subscriptions");
|
subPath.Append("Subscriptions");
|
||||||
BDirectory subDir(subPath.Path());
|
return subPath;
|
||||||
if (subDir.InitCheck() == B_ENTRY_NOT_FOUND) {
|
|
||||||
subDir.CreateDirectory(subPath.Path(), &subDir);
|
|
||||||
|
|
||||||
subPath.Append("Haiku Project");
|
|
||||||
Feed defaultSub(BUrl("https://www.haiku-os.org/blog/index.xml"),
|
|
||||||
BEntry(subPath.Path()));
|
|
||||||
defaultSub.SetTitle("Haiku Project");
|
|
||||||
defaultSub.Filetize();
|
|
||||||
}
|
|
||||||
|
|
||||||
BEntry feedEntry;
|
|
||||||
BPath feedPath;
|
|
||||||
BStringList feeds;
|
|
||||||
|
|
||||||
while (subDir.GetNextEntry(&feedEntry) == B_OK
|
|
||||||
&& feedEntry.GetPath(&feedPath) == B_OK)
|
|
||||||
feeds.Add(feedPath.Path());
|
|
||||||
return feeds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,44 @@
|
||||||
|
|
||||||
class LocalSource : public FeedSource {
|
class LocalSource : public FeedSource {
|
||||||
public:
|
public:
|
||||||
static BStringList Feeds();
|
LocalSource();
|
||||||
|
|
||||||
const char* name = "Local";
|
static BObjectList<Feed> Feeds();
|
||||||
|
|
||||||
|
static Feed* GetFeed(const char* identifier);
|
||||||
|
|
||||||
|
static bool Fetch(Feed* feed);
|
||||||
|
static bool Parse(Feed* feed);
|
||||||
|
|
||||||
|
static void AddFeed(Feed* newFeed);
|
||||||
|
static void EditFeed(Feed* updated);
|
||||||
|
static void RemoveFeed(Feed* mortonta);
|
||||||
|
|
||||||
|
static bool IsUpdated(Feed* feed);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool _IsAtom(Feed* feed);
|
||||||
|
static bool _IsRss(Feed* feed);
|
||||||
|
|
||||||
|
static bool _AtomParse(Feed* feed);
|
||||||
|
static void _AtomRootParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
static void _AtomEntriesParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
static Entry* _AtomEntryParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
|
||||||
|
static bool _RssParse(Feed* feed);
|
||||||
|
static void _RssRootParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
static void _RssEntriesParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
static Entry* _RssEntryParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
|
||||||
|
static bool _SetTitle(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
|
||||||
|
static bool _SetDate(Feed* feed, const char*);
|
||||||
|
static bool _SetDate(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
static bool _SetDate(Entry* entry, const char*);
|
||||||
|
static bool _SetDate(Entry* entry, tinyxml2::XMLElement*);
|
||||||
|
|
||||||
|
static int _XmlCountSiblings(tinyxml2::XMLElement*, const char*);
|
||||||
|
static BPath _SubscriptionPath();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
111
src/RssFeed.cpp
111
src/RssFeed.cpp
|
@ -1,111 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020, Jaidyn Levesque <jadedctrl@teknik.io>
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "RssFeed.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <Catalog.h>
|
|
||||||
|
|
||||||
#include "App.h"
|
|
||||||
#include "Entry.h"
|
|
||||||
#include "Util.h"
|
|
||||||
|
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
|
||||||
#define B_TRANSLATION_CONTEXT "RssFeed"
|
|
||||||
|
|
||||||
|
|
||||||
RssFeed::RssFeed()
|
|
||||||
{
|
|
||||||
fTitle = BString("");
|
|
||||||
fXmlUrl = BString("");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RssFeed::RssFeed(Feed* feed)
|
|
||||||
: RssFeed::RssFeed()
|
|
||||||
{
|
|
||||||
SetTitle(feed->Title());
|
|
||||||
SetXmlUrl(feed->XmlUrl());
|
|
||||||
SetCachePath(feed->CachePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
RssFeed::Parse()
|
|
||||||
{
|
|
||||||
tinyxml2::XMLDocument xml;
|
|
||||||
fEntries = BObjectList<Entry>(5, true);
|
|
||||||
|
|
||||||
Feed::Parse();
|
|
||||||
|
|
||||||
xml.LoadFile(CachePath().String());
|
|
||||||
tinyxml2::XMLElement* xchan = xml.FirstChildElement("rss")->FirstChildElement("channel");
|
|
||||||
|
|
||||||
RootParse(xchan);
|
|
||||||
ParseEntries(xchan);
|
|
||||||
|
|
||||||
Filetize();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
RssFeed::RootParse(tinyxml2::XMLElement* xchan)
|
|
||||||
{
|
|
||||||
_SetTitle(xchan->FirstChildElement("title"));
|
|
||||||
_SetDate(xchan->FirstChildElement("lastBuildDate"));
|
|
||||||
|
|
||||||
BString logString(B_TRANSLATE("Channel '%source%' at %url%:\n"));
|
|
||||||
logString.ReplaceAll("%source%", fTitle.String());
|
|
||||||
logString.ReplaceAll("%url%", fXmlUrl.UrlString());
|
|
||||||
|
|
||||||
std::cout << logString.String();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
RssFeed::EntryParse(tinyxml2::XMLElement* xitem)
|
|
||||||
{
|
|
||||||
Entry* newEntry = new Entry();
|
|
||||||
|
|
||||||
newEntry->SetTitle(xitem->FirstChildElement("title"));
|
|
||||||
newEntry->SetDescription(xitem->FirstChildElement("description"));
|
|
||||||
newEntry->SetDate(xitem->FirstChildElement("pubDate"));
|
|
||||||
newEntry->SetPostUrl(xitem->FirstChildElement("link"));
|
|
||||||
newEntry->SetContent(xitem->FirstChildElement("content:encoded"));
|
|
||||||
newEntry->SetFeedTitle(fTitle);
|
|
||||||
|
|
||||||
if (fDate == NULL || fDate < newEntry->Date())
|
|
||||||
fDate = newEntry->Date();
|
|
||||||
|
|
||||||
_AddEntry(newEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
RssFeed::ParseEntries(tinyxml2::XMLElement* xchan)
|
|
||||||
{
|
|
||||||
tinyxml2::XMLElement* xitem;
|
|
||||||
|
|
||||||
xitem = xchan->FirstChildElement("item");
|
|
||||||
|
|
||||||
int entryCount = _XmlCountSiblings(xitem, "item");
|
|
||||||
fEntries = BObjectList<Entry>(entryCount, true);
|
|
||||||
|
|
||||||
BString logString(B_TRANSLATE("\t-%count% entries-\n"));
|
|
||||||
BString entryStr;
|
|
||||||
entryStr << entryCount;
|
|
||||||
logString.ReplaceAll("%count%", entryStr);
|
|
||||||
|
|
||||||
std::cout << logString.String();
|
|
||||||
|
|
||||||
while (xitem) {
|
|
||||||
EntryParse(xitem);
|
|
||||||
xitem = xitem->NextSiblingElement("item");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020, Jaidyn Levesque <jadedctrl@teknik.io>
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
#ifndef RSS_FEED_H
|
|
||||||
#define RSS_FEED_H
|
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
|
|
||||||
#include "Feed.h"
|
|
||||||
|
|
||||||
|
|
||||||
class RssFeed: public Feed {
|
|
||||||
public:
|
|
||||||
RssFeed();
|
|
||||||
RssFeed(Feed*);
|
|
||||||
|
|
||||||
void Parse();
|
|
||||||
void RootParse(tinyxml2::XMLElement*);
|
|
||||||
void EntryParse(tinyxml2::XMLElement*);
|
|
||||||
void ParseEntries(tinyxml2::XMLElement*);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
Ŝarĝante…
Reference in New Issue