Designing E-Learning 3.0 in gRSShopper - 7
E-Learning 3.0 - Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8 - Part 9 - Part 10 - Part 11 - Part 12 - Part 13
OK, I've put on some Cœur de pirate and am set for today's work.
One of the key attributes of gRSShopper is the harvester. This application scans websites for web feeds which it then retrieves and displays to the user for reading and processing.
In a cMOOC one of the core student activities is to create blogs and submit their feed to the MOOC. I used to have this built into a general form submission function, but I got a lot of spam and other junk. It was also a weak point bad actors could attempt to exploit. So I've written a script, submit.cgi, which is isolated from the other scripts and fairly strict in what it will do.
So here's what it looks like right now:
|Figure 80 - Basic feed submission form|
Now this works - if you submit the feed, it submits it to the database. But I can't see it yet because of a bug in gRSShopper.
'Feed' is a data type, and there is a way to view feeds just like everything else:
|Figure 81 - Feed Editor, Edit tab|
|Figure 82 - Feed Editor, Classify tab|
There are different ways to classify feeds, and this will be useful in the future some time (it was really useful for Moncton Free Press).
But what we're interested in for now is the harvester, which is accessed through the Harvest tab. Here it is:
|Figure 83 - Feed Editor, Harvest tab|
A feed is a content source. For me, that means any content source, whether I'm actively harvesting it or not.
When I create a new blog post, I list the author, and I list the feed, always. Yes, I could distinguish between blog, magazine, newspaper, book, journal, and whatever, but I don't. This is part of what it means to be working with data - the physical incarnation of the data doesn't really matter (though it might be a useful way to classify them).
That means I have four types of feeds (and I've associated colours with them):
Unlinked - this is a feed that has no harvest URL. It's almost always a feed I've created manually in the process of creating a post. The harvester can't and won't harvest it.
On Hold - this is a feed that has a harvest URL, but I haven't approved it for harvesting. These are almost always feeds that have been submitted (through, say, submit.cgi) but which I haven't yet reviewed (I have found I need to review them because the submitted links are often wrong and need fixing - this was one of the most time-consuming problems of previous MOOCs).
Approved - this is a feed that I've approved (by clicking the 'Approved' option in teh Harvester tab). Once I've approved the feed, the harvester will harvest the feed.
Retired - this is a feed I've decided to stop harvesting. I don't simply delete the feed, because I might still have a lot of records associated with the feed (in gRSShopper, when you delete a record, all graph references to the record are also deleted, so there's no broken links, but we lose the information.)
Now when I list feeds , there's a way to filter the listing:
|Figure 83 - List Feeds - search function|
Somewhere in the record creation script I have a line that specifies that new feeds should be classified as 'O' (Orange, standing for 'On Hold'). This is fine, but since then I have been classifying feeds without feed_link values as 'B' (Brown, standing for 'Unlinked'). So I need to edit this - after I convert all the feeds marked 'O' in the database to 'B'.
(more time passes)
OK, I've edited the search form (the search itself hasn't been fixed yet):
|Figure 84 - Feed Search|
I need to add the filter that selects for one or more types, but for now, being able to view all feeds works fine. Also, the intent is to set the default to filter for Green (Active) feeds.
(still more time passes)
OK, I've edited the submit.cgi script to manage incoming feeds (this is actually something I've done a bunch of times over the years, but by breaking this off and making it its own thing maybe I can stop rewriting this part of the code).
Here's what I added:
1. A simple HTML page that will load the actual 'submit feed' form into an iFrame - this makes it a lot easier to keep this page up to date with template changes
2. A slightly modified form, allowing submission of blog web pages as well as feed pages (update: in the first 24 hours one person has already gotten this wrong, so...). Here it is:
|Figure 85 - Submit Feed Form|
|Figure 86 - Submit Feed Error Report|
4. Script to check whether the title, html or link already exist in the database. I don't want to allow duplicates.
This is tricky because people might want to update their information, but I don't want to allow just anyone to update information. It's especially tricky because I pre-added some feeds, so the feed owners won't be able to submit them.
Usually at this point systems offer some method to 'claim your feed', typically by having them insert some key or code into the feed itself. Alternatively, the system could ask you to prove your identity, either using a direct OAuth2 call to your feel (ie., to your blog host) (which most often isn't supported by the blog) or to have the feed point to a Twitter identity (or GitHub, or some other thing) which does support OAuth2, and have you verify that way (that's how IndieWeb does it).
I'm not sure how I want to go forward with this, so I'll leave it for now. Something for the roadmap.
So if I get a duplicate, I invite people to view the feed list (which I still need to create) and to check with me if there's something wrong.
5. Script to test the feed link provided and report back if there's an error. This saves me from deleting feeds with typos in the feed URL.
6. Default the submitted feed to 'O' (On Hold)
7. Save it and send a confirming message.
(more time passes, and we're into the next day)
Now I need to generate the feed list page. I create the page using keywords, like I've done with previous pages:
|Figure 88: Feed List page in the Page Editor|
But when a person submits a feed and needs to see right away what happened if there's an error, I can't just send them to the web page, because it only autopublishes once a day. And even if I published it more frequently, that wouldn't help. What I need is a live version of the page.
The way to access pages directly from the database is to use a page.cgi request. For http://el30.mooc.ca the URL would be as follows:
However, this won't generate the 100% live page, because page.cgi prefers to use a locally cached version for requests like this (this is especially useful for things like posts and modules, etc., which are created once and might never change).
Note: the cache doesn't automatically update; something to fix.
So what you need to do is force page.cgi to give you the most up-to-date version. So we use the 'force' option.
This is the link I give to people when they're creating their feeds and need to see the most up-to-date page (note that using 'force' does not publish the page, nor even update the cache; it's used only to preview the page).
Having done that, I need to made sure I have a nice feed_summary view (there's one that comes with gRSShopper now, but it's a bit ugly). Here's what I created:
|Figure 89 - feed_summary view in the View Editor|
And here's the page that results:
|Figure 90 - Course Feeds page|
A New Type of Post
I've been adding both participant posts and resource posts to the newsletter. but I've decided I don't want them mixed into the same list any more. So I've decided to create a new type of post - I'll call it a 'resource' post.
Just so we know, the default type of post is the 'link' post. That's the post with a title and a link and which is typically created by the harvester or when I create a new post with all the default settings.
If I wanted a different type of post, I would click one of the post_type options in the Publish tab in the Post Editor:
|Figure 91 - Post types in the Publish tab in the Post Editor|
So we'll make it an options. Options are stored in the Optlist (short for 'Option List') table. This table comes pre-defined in gRSShopper, but you can change it any way you want.
Optlist elements are named according to what they define. For example, if the table is 'post' and the option is 'type' then the corresponding Optlist is 'post_type' (one day I should just create a direct link to it in the editor. Something to fix).
Here's the post_type Optlist:
|Figure 92 - Optlist Editor|
The format is: name,value;name;value ...
It's pretty basic and one day I'll write a nice interface, but this works wekk for now (though I'm always forgetting whether name goes first or value goes first - usually I just use exactly the same thing so I don't need to remember).
So I'll just add my new Resource post type:
|Figure 93 - Optlist Editor with Resource added as a data type|
|Figure 94 - Post Editor with new Resource type added|
|Figure 96 - Page editor using keywords to load different types of post|
and this separates link posts from resource posts for me. Note that I'll have to create views for this new type of post: post_resource_email, for example.
As you can see from the image, I also decided to add an article. This is another type of post (maybe I should make 'Article' a different type of content entirely, but I didn't, and I don't feel like changing that). An article is distinct from other types of posts in that it makes greater use of the 'content' element. In gRSShopper you can type your articles right into the form, using the 'write' tab. Here it is:
|Figure 97 - Page Editor, Write tab|
|Figure 98 - post_article_html view in the View Editor|
Notice how the date is formatted (format=nice) using the post creation date (post_crdate).
Notice the use of the post_content data element. This is the content you created in the Write tab. It's saved as text in the database and can be inserted wherever you want.
Also notice the use of keylist command to associate an article with a publication. A publication is something like a journal article, magazine column, whatever. The publication table contains this publication information. Nothing in the course has been published anywhere, so this keylist command does nothing. But if there were a published version of this article, the command would print the 'prefix', then the publication information (format=inpost) and then (if it were defined) the 'postfix'.
Here's what the article looks like on the page:
|Figure 99 - Article page|
|Figure 100 - Page editor using keywords to load different types of post|
|Figure 101 - Finished newsletter|