Rubberducking ahead! Mostly I’m recording my thought process so I don’t reinvent the wheel later.
The first decisions that need to be made here are about file structure.
The root will be a
sioc:Site (and also a
single-user sites this will probably display recent blog posts or
whatever in the HTML, but the linked data will be the site info.
Three (four, if you count HEAD) HTTP verbs are legal, auth permitting. * GET/HEAD - Self-evident, and generally permitted regardless of authentication. * PUT - Will update the non-list site attributes. * POST - Will add list-type site attributes. * DELETE - Will delete the resource. (Pretty rare at the site level, hopefully).
Four attributes are lists of items:
space_of: Any resource. That doesn’t narrow things down much, but a
sioc:Space(which a Site is a specific type of) can hold… anything.
sioc:UserGroup. A UserGroup is just a group of members; literally the only unique attribute it has is
has_member. This is also inherited from Space.
sioc:UserAccount. This is a Site-specific attribute, and conceivably a single-user site could have no UserGroup, just a direct administrator.
sioc:Container. A Container is anything that contains other things, so that would be a Blog or Forum. (Why a UserGroup isn’t just another Container I do not know.)
Most random resources POSTed here are going to bounce with a 501 Not Implemented. I’m sure things will come up down the line that just need posted to the site generically, but I can’t think of any offhand.
I am inclined to say a Wirebird will have a default UserGroup at
/users/, but if not the client will have to POST one before
UserAccounts can be created.
POSTing a UserAccount directly to the Site will (auth permitting) promote that UserAccount to administrator status, creating it if necessary (and adding it to the default UserGroup).
Containers being POSTed here will be sitewide forums and such. For reading feeds, we won’t need those yet.
HTTP verbs will look like the Site ones.
UserGroups only have one list-type attibute of interest:
Having urls built from usernames bring up the (well, an) endless REST
debate: since the client is (probably) choosing the username, should it
just build the url and PUT the data directly to
whatever? I think I’m going with the former.
/@username may be aliases to the same thing, we’ll see.
SIOC doesn’t have users, just accounts. (Actual humans get to be
UserAccounts can have all sorts of attributes, but for our purposes we will only be concerned with:
sioc:Container. For the most part, Containers POSTed here will be already-existing things, but we’ll allow one type (so far) to be created by POST: a SubscriptionList.
Because we’re also going to be ActivityPub-compatible, a UserAccount
will also be a
stream:Actor. This means it needs an Inbox and Outbox,
Despite being a specific subtype, SubscriptionLists don’t have any different attributes than base Containers. Here again, the attribute we’re most interested in is:
This is where things get a little hairy. The Item is going to be a (representation of a) Weblog - does it live under the SubscriptionList? How about the Weblog’s Posts? For a single-user site, it doesn’t really matter, but for a multi-user site should there be a shared area? Would doing so be potential exposure of something (even if we restrict GET to subscribers)?
Our urls can get kind of long:
Or I could put it all in a shared space, which could reduce them to:
I know RESTful urls aren’t supposed to matter, but I think I’ll go with the latter form. The cache directory can itself be a Container, of course.
This seems like a log of work to get all the way down to subscribing, which is as simple as… POSTing an url to the SubscriptionList. But for minimum-viable, most of the above stuff will get initialized on database creation and won’t need to be edited.
When the url gets POSTed, we hand things off to XML::Feed. If the target isn’t a feed itself, XML::Feed includes a find_feeds function. It also doesn’t care if it’s RSS or Atom; the interface is the same.
Once we have the feed parsed, it’s pretty simple to build the Item:
whatever url find_feeds found goes in
sioc:feed, $feed->title goes in
sioc:name, $feed->link goes in
The polling process is pretty simple: retrieve all the
subscriber_of and their
use XML::Feed to get the current feed for each
Store each item in the cache, and put a link in each
This last bit gets a little sticky, since ActivityPub expects Inbox
entries to be wrapped in an Action, but luckily it will assume a Create
if it’s given just a naked Object. So each feed entry will be saved as
sioc:Post and a
streams:Article, and eventually when we have an
ActivityPub API we can read the Inbox directly that way.
There is one thing that’s iffy about both RSS and Atom feeds, and that’s the unpredictability of what you end up with in the “author” fields. You can have authors attached to the individual entries or the overall feed, and the authors can be a plaintext string, an url, an email address, or various combinations thereof. And since the point of Wirebird is to be a social network, that’s not good. We may not care about reliably identifying the author of news articles, but if I’m reading a friend’s blog I want to be able to link that data properly.
But that’s for down the road.