Refactor into a 'Source'-based structure

This commit is contained in:
Jaidyn Ann 2021-05-06 16:28:29 -05:00
parent b2d74a8760
commit d65c834a6f
22 changed files with 639 additions and 761 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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()));
} }

View File

@ -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");
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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; while (msg->HasData("feeds", B_RAW_TYPE, i)) {
msg->FindStrings("feedPaths", &paths); msg->FindData("feeds", B_RAW_TYPE, i, &data, &size);
fDownloadQueue->Add(paths); fDownloadQueue->AddItem((Feed*)data);
fMessageRunner->SetCount(-1);
_SendProgress(); _SendProgress();
}
fMessageRunner->SetCount(-1);
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();
entries = feed.NewEntries();
entriesCount = entries.CountItems(); entriesCount = entries.CountItems();
feedTitle = feed.Title(); feedTitle = feedBuffer->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++) LocalSource::EditFeed(feedBuffer);
entries.ItemAt(i)->Filetize();
entries.MakeEmpty();
}
if (feedBuffer->IsAtom() || feedBuffer->IsRss()) {
send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed)); send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed));
}
else {
send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed));
}
free(feedBuffer); free(feedBuffer);
} }

View File

@ -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;
}; };

View File

@ -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();
} }

View File

@ -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;

View File

@ -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;
} }

View File

@ -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;
}; };

View File

@ -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"

View File

@ -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);
}; };

View File

@ -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);
} }

View File

@ -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;
} }

View File

@ -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();
}; };

View File

@ -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");
}
}

View File

@ -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