Completely abstract Sources
This commit is contained in:
parent
d65c834a6f
commit
726d6346a1
4
Makefile
4
Makefile
|
@ -36,11 +36,13 @@ SRCS = \
|
||||||
src/FeedEditWindow.cpp \
|
src/FeedEditWindow.cpp \
|
||||||
src/FeedListItem.cpp \
|
src/FeedListItem.cpp \
|
||||||
src/FeedsView.cpp \
|
src/FeedsView.cpp \
|
||||||
src/LocalSource.cpp \
|
|
||||||
src/MainWindow.cpp \
|
src/MainWindow.cpp \
|
||||||
src/Mimetypes.cpp \
|
src/Mimetypes.cpp \
|
||||||
src/OpenWithMenu.cpp \
|
src/OpenWithMenu.cpp \
|
||||||
src/Preferences.cpp \
|
src/Preferences.cpp \
|
||||||
|
src/SourceListItem.cpp \
|
||||||
|
src/SourceManager.cpp \
|
||||||
|
src/sources/RssAtom.cpp \
|
||||||
src/UpdatesView.cpp \
|
src/UpdatesView.cpp \
|
||||||
src/Util.cpp
|
src/Util.cpp
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,10 @@ SRCS = \
|
||||||
src/Entry.cpp \
|
src/Entry.cpp \
|
||||||
src/Feed.cpp \
|
src/Feed.cpp \
|
||||||
src/FeedController.cpp \
|
src/FeedController.cpp \
|
||||||
src/LocalSource.cpp \
|
|
||||||
src/Notifier.cpp \
|
src/Notifier.cpp \
|
||||||
src/Preferences.cpp \
|
src/Preferences.cpp \
|
||||||
|
src/SourceManager.cpp \
|
||||||
|
src/sources/RssAtom.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
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "Mimetypes.h"
|
#include "Mimetypes.h"
|
||||||
#include "Notifier.h"
|
#include "Notifier.h"
|
||||||
#include "Preferences.h"
|
#include "Preferences.h"
|
||||||
|
#include "SourceManager.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -158,7 +159,8 @@ App::_OpenSourceFile(BMessage* refMessage)
|
||||||
refMessage->FindRef("refs", &entryRef);
|
refMessage->FindRef("refs", &entryRef);
|
||||||
BPath path(&entryRef);
|
BPath path(&entryRef);
|
||||||
|
|
||||||
FeedEditWindow* window = new FeedEditWindow(BString(path.Path()));
|
FeedEditWindow* window = new FeedEditWindow(path.Path(),
|
||||||
|
SourceManager::GetSourceOfType("RssAtom")->fConfigPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
#include <StorageKit.h>
|
#include <StorageKit.h>
|
||||||
|
|
||||||
#include "Daemon.h"
|
|
||||||
#include "Preferences.h"
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,15 +29,14 @@ Entry::~Entry()
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Entry::Filetize()
|
Entry::Filetize(const char* outDirPath)
|
||||||
{
|
{
|
||||||
BFile file(fTitle.String(), B_READ_WRITE);
|
BFile file(fTitle.String(), B_READ_WRITE);
|
||||||
BString outPath = ((App*)be_app)->fPreferences->EntryDir();
|
BDirectory outDir(outDirPath);
|
||||||
BDirectory outDir(outPath.String());
|
|
||||||
time_t tt_date = fDate.Time_t();
|
time_t tt_date = fDate.Time_t();
|
||||||
|
|
||||||
if (outDir.InitCheck() == B_ENTRY_NOT_FOUND) {
|
if (outDir.InitCheck() == B_ENTRY_NOT_FOUND) {
|
||||||
BDirectory().CreateDirectory(outPath.String(), &outDir);
|
BDirectory().CreateDirectory(outDirPath, &outDir);
|
||||||
}
|
}
|
||||||
outDir.CreateFile(fTitle.String(), &file);
|
outDir.CreateFile(fTitle.String(), &file);
|
||||||
|
|
||||||
|
@ -80,15 +77,6 @@ Entry::SetTitle(const char* titleStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Entry::SetTitle(tinyxml2::XMLElement* elem)
|
|
||||||
{
|
|
||||||
if (elem != NULL)
|
|
||||||
return SetTitle(elem->GetText());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BString
|
BString
|
||||||
Entry::Description()
|
Entry::Description()
|
||||||
{
|
{
|
||||||
|
@ -106,15 +94,6 @@ Entry::SetDescription(const char* descStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Entry::SetDescription(tinyxml2::XMLElement* elem)
|
|
||||||
{
|
|
||||||
if (elem != NULL)
|
|
||||||
return SetDescription(elem->GetText());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BString
|
BString
|
||||||
Entry::FeedTitle()
|
Entry::FeedTitle()
|
||||||
{
|
{
|
||||||
|
@ -149,15 +128,6 @@ Entry::SetContent(const char* contentStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Entry::SetContent(tinyxml2::XMLElement* elem)
|
|
||||||
{
|
|
||||||
if (elem != NULL)
|
|
||||||
return SetContent(elem->GetText());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BString
|
BString
|
||||||
Entry::PostUrl()
|
Entry::PostUrl()
|
||||||
{
|
{
|
||||||
|
@ -175,15 +145,6 @@ Entry::SetPostUrl(const char* urlStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Entry::SetPostUrl(tinyxml2::XMLElement* elem)
|
|
||||||
{
|
|
||||||
if (elem != NULL)
|
|
||||||
return SetPostUrl(elem->GetText());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BDateTime
|
BDateTime
|
||||||
Entry::Date()
|
Entry::Date()
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
#include <String.h>
|
#include <String.h>
|
||||||
#include <Url.h>
|
#include <Url.h>
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
|
|
||||||
|
|
||||||
class Entry {
|
class Entry {
|
||||||
public:
|
public:
|
||||||
|
@ -20,26 +18,22 @@ public:
|
||||||
Entry();
|
Entry();
|
||||||
~Entry();
|
~Entry();
|
||||||
|
|
||||||
bool Filetize();
|
bool Filetize(const char* outDirPath);
|
||||||
|
|
||||||
BString Title();
|
BString Title();
|
||||||
bool SetTitle(const char*);
|
bool SetTitle(const char*);
|
||||||
bool SetTitle(tinyxml2::XMLElement*);
|
|
||||||
|
|
||||||
BString Description();
|
BString Description();
|
||||||
bool SetDescription(const char*);
|
bool SetDescription(const char*);
|
||||||
bool SetDescription(tinyxml2::XMLElement*);
|
|
||||||
|
|
||||||
BString FeedTitle();
|
BString FeedTitle();
|
||||||
bool SetFeedTitle(BString);
|
bool SetFeedTitle(BString);
|
||||||
|
|
||||||
BString Content();
|
BString Content();
|
||||||
bool SetContent(const char*);
|
bool SetContent(const char*);
|
||||||
bool SetContent(tinyxml2::XMLElement*);
|
|
||||||
|
|
||||||
BString PostUrl();
|
BString PostUrl();
|
||||||
bool SetPostUrl(const char*);
|
bool SetPostUrl(const char*);
|
||||||
bool SetPostUrl(tinyxml2::XMLElement*);
|
|
||||||
|
|
||||||
BDateTime Date();
|
BDateTime Date();
|
||||||
bool SetDate(BDateTime date);
|
bool SetDate(BDateTime date);
|
||||||
|
@ -48,6 +42,7 @@ private:
|
||||||
BString fTitle;
|
BString fTitle;
|
||||||
BString fDescription;
|
BString fDescription;
|
||||||
BString fFeedTitle;
|
BString fFeedTitle;
|
||||||
|
BString fFeedAddOn;
|
||||||
BDateTime fDate;
|
BDateTime fDate;
|
||||||
BString fPostUrl;
|
BString fPostUrl;
|
||||||
BString fContent;
|
BString fContent;
|
||||||
|
|
30
src/Feed.cpp
30
src/Feed.cpp
|
@ -8,8 +8,6 @@
|
||||||
#include <File.h>
|
#include <File.h>
|
||||||
#include <FindDirectory.h>
|
#include <FindDirectory.h>
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
|
|
||||||
#include "Entry.h"
|
#include "Entry.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
|
@ -21,8 +19,10 @@ Feed::Feed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Feed::Feed(const char* identifier, const char* title, const char* url)
|
Feed::Feed(const char* identifier, const char* source, const char* title,
|
||||||
|
const char* url)
|
||||||
: fTitle(BString(title)),
|
: fTitle(BString(title)),
|
||||||
|
fSource(BString(source)),
|
||||||
fIdentifier(BString(identifier)),
|
fIdentifier(BString(identifier)),
|
||||||
fUrl(BUrl(url))
|
fUrl(BUrl(url))
|
||||||
{
|
{
|
||||||
|
@ -37,7 +37,6 @@ Feed::Feed(Feed* feed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BObjectList<Entry>
|
BObjectList<Entry>
|
||||||
Feed::Entries()
|
Feed::Entries()
|
||||||
{
|
{
|
||||||
|
@ -164,17 +163,32 @@ Feed::SetLastHash(BString hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BString
|
const char*
|
||||||
Feed::Identifier()
|
Feed::Identifier()
|
||||||
{
|
{
|
||||||
return fIdentifier;
|
return fIdentifier.String();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Feed::SetIdentifier(BString id)
|
Feed::SetIdentifier(const char* id)
|
||||||
{
|
{
|
||||||
fIdentifier = id;
|
fIdentifier = BString(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
Feed::Source()
|
||||||
|
{
|
||||||
|
return fSource.String();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Feed::SetSource(const char* source)
|
||||||
|
{
|
||||||
|
fSource = BString(source);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
src/Feed.h
14
src/Feed.h
|
@ -5,9 +5,6 @@
|
||||||
#ifndef FEED_H
|
#ifndef FEED_H
|
||||||
#define FEED_H
|
#define FEED_H
|
||||||
|
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
|
|
||||||
#include <ObjectList.h>
|
#include <ObjectList.h>
|
||||||
|
|
||||||
#include "Entry.h"
|
#include "Entry.h"
|
||||||
|
@ -22,7 +19,8 @@ class BUrl;
|
||||||
class Feed {
|
class Feed {
|
||||||
public:
|
public:
|
||||||
Feed();
|
Feed();
|
||||||
Feed(const char* identifier, const char* title, const char* url);
|
Feed(const char* identifier, const char* source, const char* title,
|
||||||
|
const char* url);
|
||||||
Feed(Feed*);
|
Feed(Feed*);
|
||||||
|
|
||||||
BObjectList<Entry> Entries();
|
BObjectList<Entry> Entries();
|
||||||
|
@ -48,8 +46,11 @@ public:
|
||||||
BString LastHash();
|
BString LastHash();
|
||||||
bool SetLastHash(BString hash);
|
bool SetLastHash(BString hash);
|
||||||
|
|
||||||
BString Identifier();
|
const char* Identifier();
|
||||||
bool SetIdentifier(BString id);
|
bool SetIdentifier(const char* id);
|
||||||
|
|
||||||
|
const char* Source();
|
||||||
|
bool SetSource(const char* source);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
BString fTitle;
|
BString fTitle;
|
||||||
|
@ -57,6 +58,7 @@ protected:
|
||||||
BDateTime fLastDate; // Last time feed was parsed
|
BDateTime fLastDate; // Last time feed was parsed
|
||||||
BUrl fUrl;
|
BUrl fUrl;
|
||||||
BString fIdentifier;
|
BString fIdentifier;
|
||||||
|
BString fSource;
|
||||||
BString fHash;
|
BString fHash;
|
||||||
BString fLastHash;
|
BString fLastHash;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
|
|
||||||
#include "Daemon.h"
|
#include "Daemon.h"
|
||||||
#include "Entry.h"
|
#include "Entry.h"
|
||||||
#include "LocalSource.h"
|
#include "Preferences.h"
|
||||||
|
#include "SourceManager.h"
|
||||||
|
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
@ -71,7 +72,7 @@ FeedController::MessageReceived(BMessage* msg)
|
||||||
}
|
}
|
||||||
case kUpdateSubscribed:
|
case kUpdateSubscribed:
|
||||||
{
|
{
|
||||||
BObjectList<Feed> list = LocalSource::Feeds();
|
BObjectList<Feed> list = SourceManager::Feeds();
|
||||||
fDownloadQueue->AddList(&list);
|
fDownloadQueue->AddList(&list);
|
||||||
_SendProgress();
|
_SendProgress();
|
||||||
break;
|
break;
|
||||||
|
@ -193,7 +194,7 @@ FeedController::_DownloadLoop(void* data)
|
||||||
std::cout << B_TRANSLATE("Downloading feed from ")
|
std::cout << B_TRANSLATE("Downloading feed from ")
|
||||||
<< feedBuffer->Url().UrlString() << "…\n";
|
<< feedBuffer->Url().UrlString() << "…\n";
|
||||||
|
|
||||||
if (LocalSource::Fetch(feedBuffer)) {
|
if (SourceManager::Fetch(feedBuffer)) {
|
||||||
send_data(main, kDownloadComplete, (void*)feedBuffer, sizeof(Feed));
|
send_data(main, kDownloadComplete, (void*)feedBuffer, sizeof(Feed));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -220,16 +221,16 @@ FeedController::_ParseLoop(void* data)
|
||||||
BString feedTitle;
|
BString feedTitle;
|
||||||
BUrl feedUrl = feedBuffer->Url();
|
BUrl feedUrl = feedBuffer->Url();
|
||||||
|
|
||||||
LocalSource::Parse(feedBuffer);
|
SourceManager::Parse(feedBuffer);
|
||||||
entries = feedBuffer->NewEntries();
|
entries = feedBuffer->NewEntries();
|
||||||
entriesCount = entries.CountItems();
|
entriesCount = entries.CountItems();
|
||||||
feedTitle = feedBuffer->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(((App*)be_app)->fPreferences->EntryDir());
|
||||||
entries.MakeEmpty();
|
entries.MakeEmpty();
|
||||||
|
|
||||||
LocalSource::EditFeed(feedBuffer);
|
SourceManager::EditFeed(feedBuffer);
|
||||||
send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed));
|
send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed));
|
||||||
free(feedBuffer);
|
free(feedBuffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
#include "App.h"
|
#include "App.h"
|
||||||
#include "FeedController.h"
|
#include "FeedController.h"
|
||||||
#include "FeedListItem.h"
|
#include "FeedListItem.h"
|
||||||
|
#include "SourceManager.h"
|
||||||
#include "FeedsView.h"
|
#include "FeedsView.h"
|
||||||
#include "LocalSource.h"
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,11 +38,11 @@ FeedEditWindow::FeedEditWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FeedEditWindow::FeedEditWindow(BString identifier)
|
FeedEditWindow::FeedEditWindow(const char* identifier, const char* source)
|
||||||
: FeedEditWindow()
|
: FeedEditWindow()
|
||||||
{
|
{
|
||||||
SetTitle(B_TRANSLATE("Edit feed"));
|
SetTitle(B_TRANSLATE("Edit feed"));
|
||||||
fFeed = LocalSource::GetFeed(identifier);
|
fFeed = SourceManager::GetFeed(identifier, source);
|
||||||
|
|
||||||
fFeedNameText->SetText(fFeed->Title().String());
|
fFeedNameText->SetText(fFeed->Title().String());
|
||||||
fFeedUrlText->SetText(fFeed->Url().UrlString().String());
|
fFeedUrlText->SetText(fFeed->Url().UrlString().String());
|
||||||
|
@ -146,10 +146,10 @@ FeedEditWindow::_SaveFeed()
|
||||||
fFeed->SetTitle(title.String());
|
fFeed->SetTitle(title.String());
|
||||||
fFeed->SetUrl(BUrl(urlString));
|
fFeed->SetUrl(BUrl(urlString));
|
||||||
|
|
||||||
if (fFeed->Identifier().IsEmpty() == true)
|
if (BString(fFeed->Identifier()).IsEmpty() == true)
|
||||||
LocalSource::AddFeed(fFeed);
|
SourceManager::AddFeed(fFeed);
|
||||||
else
|
else
|
||||||
LocalSource::EditFeed(fFeed);
|
SourceManager::EditFeed(fFeed);
|
||||||
|
|
||||||
BMessage edited(kFeedsEdited);
|
BMessage edited(kFeedsEdited);
|
||||||
// BMessage enqueueUpdated(kEnqueueFeed);
|
// BMessage enqueueUpdated(kEnqueueFeed);
|
||||||
|
|
|
@ -26,7 +26,7 @@ enum
|
||||||
class FeedEditWindow : public BWindow {
|
class FeedEditWindow : public BWindow {
|
||||||
public:
|
public:
|
||||||
FeedEditWindow();
|
FeedEditWindow();
|
||||||
FeedEditWindow(BString feedIdentifier);
|
FeedEditWindow(const char* identifier, const char* source);
|
||||||
~FeedEditWindow();
|
~FeedEditWindow();
|
||||||
|
|
||||||
void MessageReceived(BMessage* msg);
|
void MessageReceived(BMessage* msg);
|
||||||
|
|
|
@ -21,7 +21,8 @@ FeedListItem::FeedListItem(Feed* feed)
|
||||||
BStringItem(feed->Title().String(), 0, false),
|
BStringItem(feed->Title().String(), 0, false),
|
||||||
fStatus(kClearStatus),
|
fStatus(kClearStatus),
|
||||||
fFeedUrl(feed->Url()),
|
fFeedUrl(feed->Url()),
|
||||||
fFeedIdentifier(feed->Identifier())
|
fFeedIdentifier(feed->Identifier()),
|
||||||
|
fFeedSource(feed->Source())
|
||||||
{
|
{
|
||||||
if (feed->Title().IsEmpty() == true)
|
if (feed->Title().IsEmpty() == true)
|
||||||
SetText(B_TRANSLATE("Untitled Feed"));
|
SetText(B_TRANSLATE("Untitled Feed"));
|
||||||
|
@ -58,10 +59,17 @@ FeedListItem::DrawItem(BView* owner, BRect frame, bool complete)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BString
|
const char*
|
||||||
FeedListItem::FeedIdentifier()
|
FeedListItem::FeedIdentifier()
|
||||||
{
|
{
|
||||||
return fFeedIdentifier;
|
return fFeedIdentifier.String();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
FeedListItem::FeedSource()
|
||||||
|
{
|
||||||
|
return fFeedSource.String();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ public:
|
||||||
void DrawItem(BView* owner, BRect frame, bool complete);
|
void DrawItem(BView* owner, BRect frame, bool complete);
|
||||||
|
|
||||||
BUrl FeedUrl();
|
BUrl FeedUrl();
|
||||||
BString FeedIdentifier();
|
const char* FeedIdentifier();
|
||||||
|
const char* FeedSource();
|
||||||
|
|
||||||
void SetStatus(int8 status);
|
void SetStatus(int8 status);
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ private:
|
||||||
int8 fStatus;
|
int8 fStatus;
|
||||||
BUrl fFeedUrl;
|
BUrl fFeedUrl;
|
||||||
BString fFeedIdentifier;
|
BString fFeedIdentifier;
|
||||||
|
BString fFeedSource;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <Message.h>
|
#include <Message.h>
|
||||||
#include <GroupView.h>
|
#include <GroupView.h>
|
||||||
#include <LayoutBuilder.h>
|
#include <LayoutBuilder.h>
|
||||||
#include <ListView.h>
|
#include <OutlineListView.h>
|
||||||
#include <ScrollView.h>
|
#include <ScrollView.h>
|
||||||
#include <SeparatorView.h>
|
#include <SeparatorView.h>
|
||||||
#include <StringList.h>
|
#include <StringList.h>
|
||||||
|
@ -21,8 +21,9 @@
|
||||||
#include "FeedController.h"
|
#include "FeedController.h"
|
||||||
#include "FeedEditWindow.h"
|
#include "FeedEditWindow.h"
|
||||||
#include "FeedListItem.h"
|
#include "FeedListItem.h"
|
||||||
#include "LocalSource.h"
|
|
||||||
#include "Notifier.h"
|
#include "Notifier.h"
|
||||||
|
#include "SourceListItem.h"
|
||||||
|
#include "SourceManager.h"
|
||||||
|
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
@ -115,7 +116,7 @@ void
|
||||||
FeedsView::_InitInterface()
|
FeedsView::_InitInterface()
|
||||||
{
|
{
|
||||||
// Feeds list
|
// Feeds list
|
||||||
fFeedsListView = new BListView("feedsList");
|
fFeedsListView = new BOutlineListView("feedsList");
|
||||||
fFeedsScrollView = new BScrollView("feedsScroll", fFeedsListView,
|
fFeedsScrollView = new BScrollView("feedsScroll", fFeedsListView,
|
||||||
B_WILL_DRAW, false, true);
|
B_WILL_DRAW, false, true);
|
||||||
fFeedsListView->SetSelectionMessage(new BMessage(kFeedsSelected));
|
fFeedsListView->SetSelectionMessage(new BMessage(kFeedsSelected));
|
||||||
|
@ -189,7 +190,8 @@ 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->FeedIdentifier());
|
FeedEditWindow* edit = new FeedEditWindow(selected->FeedIdentifier(),
|
||||||
|
selected->FeedSource());
|
||||||
|
|
||||||
edit->Show();
|
edit->Show();
|
||||||
edit->Activate();
|
edit->Activate();
|
||||||
|
@ -212,23 +214,33 @@ FeedsView::_RemoveSelectedFeed()
|
||||||
int32 selIndex = fFeedsListView->CurrentSelection();
|
int32 selIndex = fFeedsListView->CurrentSelection();
|
||||||
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
|
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
|
||||||
|
|
||||||
LocalSource::RemoveFeed(LocalSource::GetFeed(selected->FeedIdentifier()));
|
SourceManager::RemoveFeed(SourceManager::GetFeed(selected->FeedIdentifier(),
|
||||||
|
selected->FeedSource()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
FeedsView::_PopulateFeedList()
|
FeedsView::_PopulateFeedList()
|
||||||
{
|
{
|
||||||
BObjectList<Feed> feeds = LocalSource::Feeds();
|
BObjectList<Source> sources = SourceManager::Sources();
|
||||||
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 < sources.CountItems(); i++) {
|
||||||
|
Source* source = sources.ItemAt(i);
|
||||||
|
SourceListItem* sourceItem = new SourceListItem(source);
|
||||||
|
fFeedsListView->AddItem(sourceItem);
|
||||||
|
fFeedsListView->Expand(sourceItem);
|
||||||
|
|
||||||
|
BObjectList<Feed> feeds = source->Feeds();
|
||||||
|
|
||||||
for (int i = 0; i < feeds.CountItems(); i++) {
|
for (int i = 0; i < feeds.CountItems(); i++) {
|
||||||
Feed* feed = feeds.ItemAt(i);
|
Feed* feed = feeds.ItemAt(i);
|
||||||
FeedListItem* item = new FeedListItem(feed);
|
FeedListItem* feedItem = new FeedListItem(feed);
|
||||||
fFeedsListView->AddItem(item);
|
fFeedsListView->AddUnder(feedItem, sourceItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fFeedsListView->CountItems() < selected)
|
if (fFeedsListView->CountItems() < selected)
|
||||||
|
@ -253,6 +265,7 @@ FeedsView::_UpdateProgress(BMessage* msg, int8 status)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < fFeedsListView->CountItems(); i++) {
|
for (int i = 0; i < fFeedsListView->CountItems(); i++) {
|
||||||
|
if (fFeedsListView->Superitem(fFeedsListView->ItemAt(i)) != NULL) {
|
||||||
FeedListItem* item = (FeedListItem*)fFeedsListView->ItemAt(i);
|
FeedListItem* item = (FeedListItem*)fFeedsListView->ItemAt(i);
|
||||||
if (item->FeedUrl().UrlString() == feedUrl) {
|
if (item->FeedUrl().UrlString() == feedUrl) {
|
||||||
item->SetStatus(status);
|
item->SetStatus(status);
|
||||||
|
@ -260,5 +273,6 @@ FeedsView::_UpdateProgress(BMessage* msg, int8 status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <GroupView.h>
|
#include <GroupView.h>
|
||||||
|
|
||||||
class BMessage;
|
class BMessage;
|
||||||
class BListView;
|
class BOutlineListView;
|
||||||
class BScrollView;
|
class BScrollView;
|
||||||
class BStringView;
|
class BStringView;
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ private:
|
||||||
BButton* fRemoveButton;
|
BButton* fRemoveButton;
|
||||||
BButton* fEditButton;
|
BButton* fEditButton;
|
||||||
BStringView* fProgressLabel;
|
BStringView* fProgressLabel;
|
||||||
BListView* fFeedsListView;
|
BOutlineListView* fFeedsListView;
|
||||||
BScrollView* fFeedsScrollView;
|
BScrollView* fFeedsScrollView;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -135,10 +135,10 @@ Preferences::SetUpdateIntervalIndex(int8 index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BString
|
const char*
|
||||||
Preferences::EntryDir()
|
Preferences::EntryDir()
|
||||||
{
|
{
|
||||||
return fEntryDir;
|
return fEntryDir.String();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ public:
|
||||||
int UpdateIntervalIndex();
|
int UpdateIntervalIndex();
|
||||||
void SetUpdateIntervalIndex(int8 index);
|
void SetUpdateIntervalIndex(int8 index);
|
||||||
|
|
||||||
BString EntryDir();
|
const char* EntryDir();
|
||||||
status_t SetEntryDir(const char* path);
|
status_t SetEntryDir(const char* path);
|
||||||
|
|
||||||
BString EntryOpenWith();
|
BString EntryOpenWith();
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
|
*/
|
||||||
|
#ifndef SOURCE_H
|
||||||
|
#define SOURCE_H
|
||||||
|
|
||||||
|
#include <ObjectList.h>
|
||||||
|
|
||||||
|
#include "Feed.h"
|
||||||
|
|
||||||
|
class BView;
|
||||||
|
|
||||||
|
|
||||||
|
class Source {
|
||||||
|
public:
|
||||||
|
// Constructor should check if the file at configPath exists; if it doesn't,
|
||||||
|
// then the source is likely being created, and ConfigView() is about to be
|
||||||
|
// called.
|
||||||
|
// Source(const char* configPath);
|
||||||
|
|
||||||
|
virtual BObjectList<Feed> Feeds() { return BObjectList<Feed>(); };
|
||||||
|
|
||||||
|
virtual Feed* GetFeed(const char* identifier) { return new Feed(); };
|
||||||
|
|
||||||
|
virtual bool Fetch(Feed* feed) { return false; };
|
||||||
|
virtual bool Parse(Feed* feed) { return false;};
|
||||||
|
|
||||||
|
virtual void AddFeed(Feed* newFeed) { };
|
||||||
|
virtual void EditFeed(Feed* updated) { };
|
||||||
|
virtual void RemoveFeed(Feed* mortonta) { };
|
||||||
|
|
||||||
|
// Check if the given feed has changed since the last update
|
||||||
|
virtual bool IsUpdated(Feed* feed) { return false; };
|
||||||
|
|
||||||
|
// Will be shown in config window when editing source
|
||||||
|
virtual BView* ConfigView() { return NULL; };
|
||||||
|
virtual bool IsConfigurable() { return false; };
|
||||||
|
virtual int8 MaxOfSource() { return -1; };
|
||||||
|
|
||||||
|
// Will be used in the feed edit window
|
||||||
|
virtual BView* EditFeedView() { return NULL; };
|
||||||
|
|
||||||
|
virtual const char* Type() { return "Source"; };
|
||||||
|
virtual const char* Name() { return "Generic"; };
|
||||||
|
|
||||||
|
BString fConfigPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SOURCE_H
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SourceListItem.h"
|
||||||
|
|
||||||
|
#include <Catalog.h>
|
||||||
|
#include <View.h>
|
||||||
|
|
||||||
|
#include "Source.h"
|
||||||
|
#include "Util.h"
|
||||||
|
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "SourceListItem"
|
||||||
|
|
||||||
|
|
||||||
|
SourceListItem::SourceListItem(Source* source)
|
||||||
|
: BStringItem(source->Name(), 0, false),
|
||||||
|
fSource(source)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Source*
|
||||||
|
SourceListItem::FeedSource()
|
||||||
|
{
|
||||||
|
return fSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
|
*/
|
||||||
|
#ifndef SOURCEITEM_H
|
||||||
|
#define SOURCEITEM_H
|
||||||
|
|
||||||
|
#include <StringItem.h>
|
||||||
|
|
||||||
|
class Source;
|
||||||
|
|
||||||
|
|
||||||
|
class SourceListItem : public BStringItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SourceListItem(Source* source);
|
||||||
|
|
||||||
|
Source* FeedSource();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Source* fSource;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SOURCEITEM_H
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SourceManager.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <Catalog.h>
|
||||||
|
#include <Directory.h>
|
||||||
|
#include <Entry.h>
|
||||||
|
#include <File.h>
|
||||||
|
#include <FindDirectory.h>
|
||||||
|
#include <Path.h>
|
||||||
|
|
||||||
|
#include "Feed.h"
|
||||||
|
#include "Source.h"
|
||||||
|
#include "sources/RssAtom.h"
|
||||||
|
#include "Util.h"
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "SourceManager"
|
||||||
|
|
||||||
|
|
||||||
|
BObjectList<Source>
|
||||||
|
SourceManager::Sources()
|
||||||
|
{
|
||||||
|
BDirectory sourceDir = BDirectory(_SourceConfigPath().Path());
|
||||||
|
BEntry sourceEntry;
|
||||||
|
BPath sourcePath;
|
||||||
|
|
||||||
|
BObjectList<Source> sources;
|
||||||
|
|
||||||
|
while (sourceDir.GetNextEntry(&sourceEntry) == B_OK
|
||||||
|
&& sourceEntry.GetPath(&sourcePath) == B_OK)
|
||||||
|
{
|
||||||
|
Source* source = GetSource(sourcePath.Path());
|
||||||
|
if (source != NULL)
|
||||||
|
sources.AddItem(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no sources, create a default RssAtom one
|
||||||
|
if (sources.CountItems() == 0) {
|
||||||
|
sourcePath = _SourceConfigPath();
|
||||||
|
sourcePath.Append("RSS-Atom");
|
||||||
|
RssAtom(sourcePath.Path());
|
||||||
|
sources.AddItem(GetSource(sourcePath.Path()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Source*
|
||||||
|
SourceManager::GetSource(const char* configPath)
|
||||||
|
{
|
||||||
|
BFile config(configPath, B_READ_ONLY);
|
||||||
|
BMessage storage;
|
||||||
|
storage.Unflatten(&config);
|
||||||
|
BString sourceType;
|
||||||
|
|
||||||
|
if (storage.FindString("source_type", &sourceType) == B_OK)
|
||||||
|
if (sourceType == BString("RssAtom"))
|
||||||
|
return new RssAtom(configPath);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Source*
|
||||||
|
SourceManager::GetSource(Feed* feed)
|
||||||
|
{
|
||||||
|
return GetSource(feed->Source());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Source*
|
||||||
|
SourceManager::GetSourceOfType(const char* sourceType)
|
||||||
|
{
|
||||||
|
BObjectList<Source> sources = Sources();
|
||||||
|
|
||||||
|
for (int i = 0; i < sources.CountItems(); i++) {
|
||||||
|
if (sources.ItemAt(i)->Type() == BString(sourceType))
|
||||||
|
return sources.ItemAt(i);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BObjectList<Feed>
|
||||||
|
SourceManager::Feeds()
|
||||||
|
{
|
||||||
|
BObjectList<Source> sources = Sources();
|
||||||
|
BObjectList<Feed> feeds;
|
||||||
|
|
||||||
|
for (int i = 0; i < sources.CountItems(); i++) {
|
||||||
|
Source* sauce = sources.ItemAt(i);
|
||||||
|
BObjectList<Feed> newFeeds = sources.ItemAt(i)->Feeds();
|
||||||
|
feeds.AddList(&newFeeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return feeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Feed*
|
||||||
|
SourceManager::GetFeed(const char* identifier, const char* source)
|
||||||
|
{
|
||||||
|
return GetSource(source)->GetFeed(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
SourceManager::Fetch(Feed* feed)
|
||||||
|
{
|
||||||
|
return GetSource(feed)->Fetch(feed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
SourceManager::Parse(Feed* feed)
|
||||||
|
{
|
||||||
|
return GetSource(feed)->Parse(feed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SourceManager::AddFeed(Feed* newFeed)
|
||||||
|
{
|
||||||
|
return GetSource(newFeed)->AddFeed(newFeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SourceManager::EditFeed(Feed* updated)
|
||||||
|
{
|
||||||
|
return GetSource(updated)->EditFeed(updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SourceManager::RemoveFeed(Feed* mortonta)
|
||||||
|
{
|
||||||
|
return GetSource(mortonta)->EditFeed(mortonta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
SourceManager::IsUpdated(Feed* feed)
|
||||||
|
{
|
||||||
|
return GetSource(feed)->IsUpdated(feed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BPath
|
||||||
|
SourceManager::_SourceConfigPath()
|
||||||
|
{
|
||||||
|
BPath sourcePath;
|
||||||
|
find_directory(B_USER_SETTINGS_DIRECTORY, &sourcePath);
|
||||||
|
sourcePath.Append("Pogger");
|
||||||
|
sourcePath.Append("Sources");
|
||||||
|
|
||||||
|
BDirectory sourceDir = BDirectory(sourcePath.Path());
|
||||||
|
if (sourceDir.InitCheck() == B_ENTRY_NOT_FOUND)
|
||||||
|
sourceDir.CreateDirectory(sourcePath.Path(), &sourceDir);
|
||||||
|
|
||||||
|
return sourcePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
|
*/
|
||||||
|
#ifndef SOURCEMANAGER_H
|
||||||
|
#define SOURCEMANAGER_H
|
||||||
|
|
||||||
|
#include <ObjectList.h>
|
||||||
|
|
||||||
|
#include "Source.h"
|
||||||
|
|
||||||
|
class Feed;
|
||||||
|
|
||||||
|
class SourceManager {
|
||||||
|
public:
|
||||||
|
|
||||||
|
static BObjectList<Source> Sources();
|
||||||
|
|
||||||
|
static Source* GetSource(const char* configPath);
|
||||||
|
static Source* GetSource(Feed* feed);
|
||||||
|
static Source* GetSourceOfType(const char* sourceType);
|
||||||
|
|
||||||
|
static BObjectList<Feed> Feeds();
|
||||||
|
|
||||||
|
static Feed* GetFeed(const char* identifier, const char* source);
|
||||||
|
|
||||||
|
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 BPath _SourceConfigPath();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SOURCEMANAGER_H
|
|
@ -0,0 +1,529 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RssAtom.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <Catalog.h>
|
||||||
|
#include <Directory.h>
|
||||||
|
#include <Entry.h>
|
||||||
|
#include <File.h>
|
||||||
|
#include <FindDirectory.h>
|
||||||
|
#include <Path.h>
|
||||||
|
|
||||||
|
#include "../Feed.h"
|
||||||
|
#include "../Util.h"
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "RssAtom"
|
||||||
|
|
||||||
|
|
||||||
|
RssAtom::RssAtom(const char* configPath)
|
||||||
|
: fConfigPath(BString(configPath))
|
||||||
|
{
|
||||||
|
_LoadConfig();
|
||||||
|
_EnsureSubscriptions();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
RssAtom::Type()
|
||||||
|
{
|
||||||
|
return "RssAtom";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
RssAtom::Name()
|
||||||
|
{
|
||||||
|
return fTitle.String();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BObjectList<Feed>
|
||||||
|
RssAtom::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*
|
||||||
|
RssAtom::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, fConfigPath.String(), title.String(),
|
||||||
|
url.String());
|
||||||
|
feed->SetHash(hash);
|
||||||
|
feed->SetLastDate(lastDate);
|
||||||
|
return feed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::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
|
||||||
|
RssAtom::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
|
||||||
|
RssAtom::AddFeed(Feed* newFeed)
|
||||||
|
{
|
||||||
|
if (BString(newFeed->Identifier()).IsEmpty() == true) {
|
||||||
|
BPath subPath = _SubscriptionPath();
|
||||||
|
subPath.Append(urlToFilename(newFeed->Url()));
|
||||||
|
newFeed->SetIdentifier(subPath.Path());
|
||||||
|
}
|
||||||
|
EditFeed(newFeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RssAtom::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
|
||||||
|
RssAtom::RemoveFeed(Feed* mortonta)
|
||||||
|
{
|
||||||
|
BEntry entry(mortonta->Identifier());
|
||||||
|
entry.Remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::IsUpdated(Feed* feed)
|
||||||
|
{
|
||||||
|
return (GetFeed(feed->Identifier())->Hash() != feed->Hash());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_IsAtom(Feed* feed)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLDocument xml;
|
||||||
|
xml.LoadFile(feed->Identifier());
|
||||||
|
|
||||||
|
if (xml.FirstChildElement("feed"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_IsRss(Feed* feed)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLDocument xml;
|
||||||
|
xml.LoadFile(feed->Identifier());
|
||||||
|
|
||||||
|
if (xml.FirstChildElement("rss"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_AtomParse(Feed* atomFeed)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLDocument xml;
|
||||||
|
xml.LoadFile(atomFeed->Identifier());
|
||||||
|
|
||||||
|
tinyxml2::XMLElement* xfeed = xml.FirstChildElement("feed");
|
||||||
|
|
||||||
|
_AtomRootParse(atomFeed, xfeed);
|
||||||
|
_AtomEntriesParse(atomFeed, xfeed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RssAtom::_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
|
||||||
|
RssAtom::_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*
|
||||||
|
RssAtom::_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;
|
||||||
|
|
||||||
|
_SetTitle(newEntry, xentry->FirstChildElement("title"));
|
||||||
|
newEntry->SetPostUrl(xentry->FirstChildElement("link")->Attribute("href"));
|
||||||
|
newEntry->SetFeedTitle(feed->Title());
|
||||||
|
|
||||||
|
bool set = false;
|
||||||
|
set = _SetDescription(newEntry, xentry->FirstChildElement("summary"));
|
||||||
|
if (!set)
|
||||||
|
set = _SetDescription(newEntry, xentry->FirstChildElement("description"));
|
||||||
|
if (!set && xmedia)
|
||||||
|
set = _SetDescription(newEntry,
|
||||||
|
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
|
||||||
|
RssAtom::_RssParse(Feed* feed)
|
||||||
|
{
|
||||||
|
tinyxml2::XMLDocument xml;
|
||||||
|
xml.LoadFile(feed->Identifier());
|
||||||
|
|
||||||
|
tinyxml2::XMLElement* xchan = xml.FirstChildElement("rss")->FirstChildElement("channel");
|
||||||
|
|
||||||
|
_RssRootParse(feed, xchan);
|
||||||
|
_RssEntriesParse(feed, xchan);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RssAtom::_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
|
||||||
|
RssAtom::_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*
|
||||||
|
RssAtom::_RssEntryParse(Feed* feed, tinyxml2::XMLElement* xitem)
|
||||||
|
{
|
||||||
|
Entry* newEntry = new Entry();
|
||||||
|
|
||||||
|
_SetTitle(newEntry, xitem->FirstChildElement("title"));
|
||||||
|
_SetDescription(newEntry, xitem->FirstChildElement("description"));
|
||||||
|
_SetDate(newEntry, xitem->FirstChildElement("pubDate"));
|
||||||
|
_SetPostUrl(newEntry, xitem->FirstChildElement("link"));
|
||||||
|
_SetContent(newEntry, xitem->FirstChildElement("content:encoded"));
|
||||||
|
newEntry->SetFeedTitle(feed->Title());
|
||||||
|
|
||||||
|
if (feed->Date() == NULL || feed->Date() < newEntry->Date())
|
||||||
|
feed->SetDate(newEntry->Date());
|
||||||
|
|
||||||
|
return newEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_SetTitle(Feed* feed, tinyxml2::XMLElement* elem)
|
||||||
|
{
|
||||||
|
if (elem != NULL && feed->Title().IsEmpty() == true)
|
||||||
|
return feed->SetTitle(elem->GetText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_SetTitle(Entry* entry, tinyxml2::XMLElement* elem)
|
||||||
|
{
|
||||||
|
if (elem != NULL)
|
||||||
|
return entry->SetTitle(elem->GetText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_SetDate(Feed* feed, const char* dateCStr)
|
||||||
|
{
|
||||||
|
if (dateCStr == NULL)
|
||||||
|
return false;
|
||||||
|
return feed->SetDate(feedDateToBDate(dateCStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_SetDate(Feed* feed, tinyxml2::XMLElement* elem)
|
||||||
|
{
|
||||||
|
if (elem == NULL)
|
||||||
|
return false;
|
||||||
|
return _SetDate(feed, elem->GetText());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_SetDate(Entry* entry, const char* dateStr)
|
||||||
|
{
|
||||||
|
if (dateStr == NULL)
|
||||||
|
return false;
|
||||||
|
return entry->SetDate(feedDateToBDate(dateStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_SetDate(Entry* entry, tinyxml2::XMLElement* elem)
|
||||||
|
{
|
||||||
|
if (elem == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return _SetDate(entry, elem->GetText());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_SetDescription(Entry* entry, tinyxml2::XMLElement* elem)
|
||||||
|
{
|
||||||
|
if (elem != NULL)
|
||||||
|
return entry->SetDescription(elem->GetText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_SetContent(Entry* entry, tinyxml2::XMLElement* elem)
|
||||||
|
{
|
||||||
|
if (elem != NULL)
|
||||||
|
return entry->SetContent(elem->GetText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RssAtom::_SetPostUrl(Entry* entry, tinyxml2::XMLElement* elem)
|
||||||
|
{
|
||||||
|
if (elem != NULL)
|
||||||
|
return entry->SetPostUrl(elem->GetText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Count the amount of siblings to an element of given type name
|
||||||
|
int
|
||||||
|
RssAtom::_XmlCountSiblings (tinyxml2::XMLElement* xsibling,
|
||||||
|
const char* sibling_name)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
while (xsibling) {
|
||||||
|
count++;
|
||||||
|
xsibling = xsibling->NextSiblingElement(sibling_name);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RssAtom::_LoadConfig()
|
||||||
|
{
|
||||||
|
BMessage storage;
|
||||||
|
BFile configFile = BFile(fConfigPath.String(), B_READ_ONLY);
|
||||||
|
|
||||||
|
if (configFile.InitCheck() != B_OK) {
|
||||||
|
_CreateConfig();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Unflatten(&configFile);
|
||||||
|
fTitle = storage.GetString("source_name", "Untitled RssAtom Source");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RssAtom::_CreateConfig()
|
||||||
|
{
|
||||||
|
std::cout << "CREATNIG\n";
|
||||||
|
BMessage storage;
|
||||||
|
BFile configFile = BFile(fConfigPath.String(), B_READ_WRITE|B_CREATE_FILE);
|
||||||
|
|
||||||
|
storage.AddString("source_type", "RssAtom");
|
||||||
|
storage.AddString("source_name", "Atom/RSS");
|
||||||
|
storage.Flatten(&configFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RssAtom::_EnsureSubscriptions()
|
||||||
|
{
|
||||||
|
BPath subPath = _SubscriptionPath();
|
||||||
|
BDirectory subDir = BDirectory(subPath.Path());
|
||||||
|
|
||||||
|
// Create "Subscriptions" dir and add default subscription
|
||||||
|
if (subDir.InitCheck() == B_ENTRY_NOT_FOUND) {
|
||||||
|
subDir.CreateDirectory(subPath.Path(), &subDir);
|
||||||
|
|
||||||
|
subPath.Append("Haiku Project");
|
||||||
|
Feed defaultSub(subPath.Path(), fConfigPath.String(), "Haiku Project",
|
||||||
|
"https://www.haiku-os.org/blog/index.xml");
|
||||||
|
AddFeed(&defaultSub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BPath
|
||||||
|
RssAtom::_SubscriptionPath()
|
||||||
|
{
|
||||||
|
BPath subPath;
|
||||||
|
find_directory(B_USER_SETTINGS_DIRECTORY, &subPath);
|
||||||
|
subPath.Append("Pogger");
|
||||||
|
subPath.Append("Subscriptions");
|
||||||
|
return subPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
|
*/
|
||||||
|
#ifndef RSSATOM_H
|
||||||
|
#define RSSATOM_H
|
||||||
|
|
||||||
|
#include <tinyxml2.h>
|
||||||
|
|
||||||
|
#include <Path.h>
|
||||||
|
|
||||||
|
#include "../Source.h"
|
||||||
|
|
||||||
|
class Entry;
|
||||||
|
|
||||||
|
|
||||||
|
class RssAtom : public Source {
|
||||||
|
public:
|
||||||
|
RssAtom(const char* configPath);
|
||||||
|
|
||||||
|
const char* Type();
|
||||||
|
const char* Name();
|
||||||
|
|
||||||
|
BObjectList<Feed> Feeds();
|
||||||
|
|
||||||
|
Feed* GetFeed(const char* identifier);
|
||||||
|
|
||||||
|
bool Fetch(Feed* feed);
|
||||||
|
bool Parse(Feed* feed);
|
||||||
|
|
||||||
|
void AddFeed(Feed* newFeed);
|
||||||
|
void EditFeed(Feed* updated);
|
||||||
|
void RemoveFeed(Feed* mortonta);
|
||||||
|
|
||||||
|
bool IsUpdated(Feed* feed);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _IsAtom(Feed* feed);
|
||||||
|
bool _IsRss(Feed* feed);
|
||||||
|
|
||||||
|
bool _AtomParse(Feed* feed);
|
||||||
|
void _AtomRootParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
void _AtomEntriesParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
Entry* _AtomEntryParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
|
||||||
|
bool _RssParse(Feed* feed);
|
||||||
|
void _RssRootParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
void _RssEntriesParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
Entry* _RssEntryParse(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
|
||||||
|
// These _Sets could probably be better served as a macro :P
|
||||||
|
bool _SetTitle(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
bool _SetTitle(Entry* entry, tinyxml2::XMLElement*);
|
||||||
|
|
||||||
|
bool _SetDate(Feed* feed, const char*);
|
||||||
|
bool _SetDate(Feed* feed, tinyxml2::XMLElement*);
|
||||||
|
bool _SetDate(Entry* entry, const char*);
|
||||||
|
bool _SetDate(Entry* entry, tinyxml2::XMLElement*);
|
||||||
|
|
||||||
|
bool _SetDescription(Entry* entry, tinyxml2::XMLElement*);
|
||||||
|
bool _SetContent(Entry* entry, tinyxml2::XMLElement*);
|
||||||
|
|
||||||
|
bool _SetPostUrl(Entry* entry, tinyxml2::XMLElement*);
|
||||||
|
|
||||||
|
int _XmlCountSiblings(tinyxml2::XMLElement*, const char*);
|
||||||
|
|
||||||
|
void _LoadConfig();
|
||||||
|
void _CreateConfig();
|
||||||
|
void _EnsureSubscriptions();
|
||||||
|
|
||||||
|
BPath _SubscriptionPath();
|
||||||
|
|
||||||
|
BString fTitle;
|
||||||
|
BString fConfigPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RSSATOM_H
|
||||||
|
|
Ŝarĝante…
Reference in New Issue