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.