Sunday, April 10, 2011 at 1:07 AM.
radio2Suite.buildRss
on buildRss (adruser, adrfeed, maxitems=50, theDay=nil) {
<<Changes
<<4/9/11; 12:52:15 PM by DW
<<Call the callback scripts at the end of the build.
<<3/15/11; 10:33:18 AM by DW
<<Get cloud prefs from the top level, not the feed. Makes no sense to have each feed specify its cloud without having a global pref.
<<3/14/11; 3:52:54 PM by DW
<<Support addNamespaceToRssFeed callbacks.
<<3/12/11; 11:35:30 PM by DW
<<If an item has an element named "thumbnail" add a media:thumbnail element, and declare the namespace at the top of the file. Fixed the names of sub-elements of microblog:archive. If we're about to output an item without a guid, synthesize one.
<<3/12/11; 6:04:40 PM by DW
<<The <generator> element now says Radio2 v0.51. Add a <docs> element.
<<3/11/11; 4:48:16 AM by DW
<<Callbacks.
<<3/9/11; 1:54:53 AM by DW
<<Only add the microblog:archive element if the calendar is non-empty.
<<3/2/11; 7:34:05 PM by DW
<<Stop the michegas with the guids and the enclosures. We have a better solution already in there.
<<3/2/11; 8:04:32 AM by DW
<<Cache the size and type in the stats sub-table for each item, so I don't have to get them via HTTP every time the feed is rebuilt. Cut down on network traffic.
<<3/1/11; 2:02:31 PM by DW
<<If guid is empty and the item has an enclosure, use its url as the guid.
<<3/1/11; 8:48:23 AM by DW
<<If adrfeed^.stats.htmlUrl is available use it in the channel-level link element.
<<2/28/11; 3:57:54 PM by DW
<<<microblog:archive> now contains <startDay> and <endDay> elements that tell when the calendar-structured archive begins and ends.
<<2/28/11; 8:39:20 AM by DW
<<Use radio2Suite.writeStaticFile to save the file. First support for <microblog:archive> element. Still need to establish <startDay>.
<<2/25/11; 4:38:12 PM by DW
<<Include the microblog namespace.
<<2/24/11; 12:30:33 PM by DW
<<Get guid from the item table, if it's available. If it's not, it's the same as link.
<<2/22/11; 9:17:32 PM by DW
<<Add optional param, theDay. If non-nil, only output items for that day, and save to a file in a calendar-structured folder.
<<We ignore the maxItems param when doing a daily feed, we want to get them all, no matter what.
<<No longer doing the legacy saves. Sorry to anyone who was subscribed to these feeds.
<<2/18/11; 7:28:01 PM by DW
<<If a shortened URL is available use it.
<<2/9/11; 10:21:44 AM by DW
<<Change default maxitems to 25.
<<1/18/11; 7:14:48 PM by DW
<<If title, link or description are empty, don't include them.
<<1/13/11; 10:42:41 AM by DW
<<Save the feed to the user's silo-safe bucket on S3.
<<1/11/11; 11:50:23 AM by DW
<<Made maxitems an optional param, defaults to 100.
<<1/7/11; 9:39:41 AM by DW
<<Both legacy saving options are now controlled by prefs. For everyone but me, these will always be disabled. A little bit of a mess to be cleaned up at a later date.
<<12/14/10; 11:54:15 AM by DW
<<Don't set the RSS url, we're making it a preference, not a stat.
<<12/14/10; 11:44:08 AM by DW
<<Use adrdata^.prefs.s3Path.
<<12/29/09; 7:20:38 PM by DW
<<Created.
local (adrdata = radio2Suite.init (), rsstext = "", now = clock.now (), nownss = date.netstandardstring (now));
local (rssUrl, ctitems = 0, flMediaRssNamespace = false);
radio2suite.initfeed (adrfeed);
bundle { //build rsstext
local (indentlevel = 0);
on add (s, flskipifempty=false) {
if flskipifempty { //3/11/11 by DW
if sizeof (s) == 0 {
return}};
rsstext = rsstext + string.filledstring ("\t", indentlevel) + s + "\r"};
on encode (s) {
if system.environment.isMac {
return (xml.entityEncode (latinToMac.macToLatin (s), true))}
else {
return (xml.entityEncode (s, true))}};
add ("<?xml version=\"1.0\"?>");
add ("<rss version=\"2.0\" xmlns:microblog=\"http://microblog.reallysimple.org/\"<%namespace%>>"); indentlevel++;
add ("<channel>"); indentlevel++;
bundle { //add header elements
bundle { //title -- 2/22/11 by DW
local (title = adrfeed^.prefs.title);
if theDay != nil {
title = title + " (" + date.shortstring (theDay) + ")"};
add ("<title>" + encode (title) + "</title>")};
bundle { //link -- 3/1/11 by DW
local (link);
if defined (adrfeed^.stats.htmlUrl) {
link = adrfeed^.stats.htmlUrl}
else {
link = encode (adrfeed^.prefs.link)};
add ("<link>" + link + "</link>")};
add ("<description>" + encode (adrfeed^.prefs.description) + "</description>");
add ("<language>" + encode (adrfeed^.prefs.language) + "</language>");
bundle { //pubDate
if theDay == nil {
add ("<pubDate>" + nownss + "</pubDate>")}
else {
add ("<pubDate>" + date.netstandardstring (theDay) + "</pubDate>")}};
add ("<lastBuildDate>" + nownss + "</lastBuildDate>");
add ("<generator>" + radio2Info.name + " v" + radio2Info.version + "</generator>"); //3/12/11 by DW
<<add ("<generator>" + frontier.getprogramname () + "</generator>")
add ("<docs>http://cyber.law.harvard.edu/rss/rss.html</docs>"); //3/12/11 by DW
bundle { //add cloud, 3/15/11 by DW
with adrdata^.prefs.cloud {
add ("<cloud domain=\"" + server + "\" port=\"" + port + "\" path=\"" + path + "\" registerProcedure=\"" + registerProcedure + "\" protocol=\"" + protocol + "\" />")}};
bundle { //archive, 2/28/11 by DW
if defined (adrfeed^.stats.feedUrl) and (sizeof (adrfeed^.calendar) > 0) {
on archiveDay (theDay) {
local (day, month, year, hour, minute, second);
date.get (theDay, @day, @month, @year, @hour, @minute, @second);
return (year + "-" + string.padwithzeros (month, 2) + "-" + string.padwithzeros (day, 2))};
local (url = adrfeed^.stats.feedUrl, fname = string.lastfield (url, "/"));
local (urlArchive = string.delete (url, sizeof (url) - sizeof (fname) + 1, sizeof (fname)));
add ("<microblog:archive>"); indentlevel++;
add ("<microblog:url>" + urlArchive + "</microblog:url>");
add ("<microblog:filename>" + fname + "</microblog:filename>");
add ("<microblog:startDay>" + archiveDay (mainresponder.calendar.getfirstday (@adrfeed^.calendar)) + "</microblog:startDay>");
add ("<microblog:endDay>" + archiveDay (mainresponder.calendar.getlastday (@adrfeed^.calendar)) + "</microblog:endDay>");
add ("</microblog:archive>"); indentlevel--}};
bundle { //callbacks, 3/11/11 by DW
local (adrscript);
for adrscript in @adrdata^.callbacks.addToRssChannel {
while typeof (adrscript^) == addresstype {
adrscript = adrscript^};
try {
add (adrscript^ (adruser, adrfeed), true)}}}};
bundle { //add items
local (adrcal = @adrfeed^.calendar);
on visit (adritem) {
local (fllinkfull = false);
on addopt (name, val) { //1/18/11 by DW
val = string.trimwhitespace (val);
if sizeof (val) > 0 {
add ("<" + name + ">" + encode (val) + "</" + name + ">")}};
add ("<item>"); indentlevel++;
addopt ("title", adritem^.title);
addopt ("description", adritem^.description);
bundle { //link, 2/18/11 by DW
if defined (adritem^.linkShortened) {
addopt ("link", adritem^.linkShortened);
fllinkfull = true}
else {
addopt ("link", adritem^.link)}};
if defined (adritem^.enclosure) {
local (url = adritem^.enclosure);
if sizeof (url) > 0 {
local (type, length);
if defined (adritem^.stats.enclosureType) {
type = adritem^.stats.enclosureType;
length = adritem^.stats.enclosureLength}
else {
tcp.httpGetTypeLength (url, @type, @length, 5);
adritem^.stats.enclosureType = type;
adritem^.stats.enclosureLength = length};
add ("<enclosure url=\"" + url + "\" length=\"" + length + "\" type=\"" + type + "\" />")}};
if defined (adritem^.thumbnail) { //3/12/11 by DW
local (url = adritem^.thumbnail);
if sizeof (url) > 0 {
add ("<media:thumbnail url=\"" + url + "\" />");
flMediaRssNamespace = true}};
add ("<pubDate>" + date.netstandardstring (adritem^.when) + "</pubDate>");
bundle { //guid
local (guid = "");
if defined (adritem^.guid) { //post was created on or after 2/24/11
guid = adritem^.guid}
else {
guid = adritem^.link}; //post was created before 2/24/11
if sizeof (guid) == 0 { //3/12/11 by DW
add ("<guid isPermaLink=\"false\">" + encode (string.hashmd5 (string (adritem^.when))) + "</guid>")}
else {
add ("<guid>" + encode (guid) + "</guid>")}};
if fllinkfull {
addopt ("microblog:linkFull", adritem^.link)}; //2/25/11 by DW
bundle { //callbacks, 3/11/11 by DW
local (adrscript);
for adrscript in @adrdata^.callbacks.addToRssItem {
while typeof (adrscript^) == addresstype {
adrscript = adrscript^};
try {
add (adrscript^ (adruser, adrfeed, adritem), true)}}};
add ("</item>"); indentlevel--;
return (++ctitems < maxitems)}; //2/9/11 by DW
if theDay == nil {
mainresponder.calendar.visitReverseChronologic (adrcal, @visit)}
else {
local (adrday = mainresponder.calendar.getDayAddress (adrcal, theDay), i);
for i = sizeof (adrday^) downto 1 {
visit (@adrday^ [i])}}};
add ("</channel>"); indentlevel--;
add ("</rss>"); indentlevel--};
bundle { //namespace substitution, 3/12/11 by DW
local (ns = "");
if flMediaRssNamespace {
ns = " xmlns:media=\"http://search.yahoo.com/mrss/\""};
bundle { //callbacks, 3/14/11 by DW
local (adrscript);
for adrscript in @adrdata^.callbacks.addNamespaceToRssFeed {
while typeof (adrscript^) == addresstype {
adrscript = adrscript^};
try {
local (s = adrscript^ (adruser, adrfeed));
if sizeof (s) > 0 {
ns = ns + " " + s}}}};
rsstext = string.replace (rsstext, "<%namespace%>", ns)};
bundle { //save the file
if theDay == nil {
local (url);
url = radio2Suite.writeStaticFile (adruser, nameof (adrfeed^), rsstext, "text/plain"); //2/28/11 by DW
adrfeed^.stats.feedUrl = url;
adrfeed^.stats.ctSaves++;
adrfeed^.stats.whenLastSave = now;
bundle { //call the callbacks, 4/9/11 by DW
local (adrscript);
for adrscript in @adruser^.callbacks.buildRss {
while typeof (adrscript^) == addresstype {
adrscript = adrscript^};
try {
adrscript^ (adruser, adrfeed, rsstext)}}}}
else {
local (replath = file.getdatepath ("/", theDay) + nameof (adrfeed^));
radio2Suite.writeStaticFile (adruser, replath, rsstext, "text/plain")}};
<<bundle //old code
<<bundle //save it to S3
<<if adrdata^.prefs.s3enabled
<<if theDay == nil
<<local (replath = nameof (adruser^) + "/" + nameof (adrfeed^))
<<local (s3path = adrdata^.prefs.s3path + replath)
<<s3.newobject (s3path, rsstext, "text/plain")
<<adrfeed^.stats.feedUrl = adrdata^.prefs.s3url + replath
<<adrfeed^.stats.ctSaves++
<<adrfeed^.stats.whenLastSave = now
<<else
<<local (s3path = adrdata^.prefs.s3path + nameof (adruser^) + "/" + file.getdatepath ("/", theDay) + nameof (adrfeed^))
<<s3.newobject (s3path, rsstext, "text/plain")
<<bundle //save it to the user's S3 bucket, 1/13/11 by DW
<<if adruser^.prefs.s3bucket.enabled and (adruser^.prefs.s3bucket.path != "")
<<local (path = adruser^.prefs.s3bucket.path)
<<if theDay == nil
<<if not (path beginswith "/")
<<path = "/" + path
<<if not (path endswith "/")
<<path = path + "/"
<<s3.newobject (path + nameof (adrfeed^), rsstext, "text/plain")
<<if adruser^.prefs.s3bucket.url != ""
<<adrfeed^.stats.feedUrl = adruser^.prefs.s3bucket.url + nameof (adrfeed^)
<<else
<<adrfeed^.stats.feedUrl = "http://s3.amazonaws.com/" + path + nameof (adrfeed^)
<<else
<<s3.newobject (path + file.getdatepath ("/", theDay) + nameof (adrfeed^), rsstext, "text/plain")
<<bundle //legacy saves, just for DW's first two feeds
<<bundle //save it to S3
<<if defined (adrfeed^.prefs.s3Path) //1/7/11 by DW
<<s3.newobject (adrfeed^.prefs.s3Path, rsstext)
<<bundle //save in archive, 12/16/10 by DW
<<local (path = adrfeed^.prefs.s3ArchivePath + file.getdatepath ("/", flLastSeparator:false) + ".xml")
<<s3.newobject (path, rsstext)
<<adrfeed^.stats.ctSaves++
<<adrfeed^.stats.whenLastSave = now
<<bundle //save it to fresca, if enabled
<<if defined (adrfeed^.prefs.fresca)
<<if adrfeed^.prefs.fresca.enabled
<<local (domain = adrfeed^.prefs.fresca.domain, path = adrfeed^.prefs.fresca.path)
<<myServerFarmSuite.writeToFresca (domain, path + "rss.xml", rsstext)
<<myServerFarmSuite.writeToFresca (domain, path + file.getdatepath ("/", flLastSeparator:false) + ".xml", rsstext)
return (true)};
bundle { //test code
buildRss (@config.radio2.users.dave, @config.radio2.users.dave.feeds.["linkblog.xml"])}
This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.