Tuesday, August 18, 2009 at 7:33 PM.

newsRiverSuite.readingList.beforeScan

<<Changes
	<<1/15/06; 1:54:58 PM by DW
		<<Get title from head, maintain list-level stats for Subscriptions page.
	<<1/14/06; 12:21:50 PM by DW
		<<Detailed code review and testing.
		<<Using PT's reading list as a guinea pig.
			<<http://downloads.oreilly.com/make/philliptorrone.opml
	<<11/8/05; 1:42:07 PM by DW
		<<Created. This is what we do before a scan.
		<<We read each of the reading lists and make sure we're subscribed to all the feeds in each of the lists, and unsub from any of the feeds that came from a reading list that are no longer in reading lists. 
		<<Step-by-step, how we go about this...
			<<Every service that we subscribe to through a reading list has a readingList sub-table. 
			<<It may appear in more than one reading list. Each of the lists that it comes from is pointed to by an element of this table.
			<<If we were subscribed to a feed before it showed up in a reading list, it has a boolean, flManualSub, set to true. We never sweep up (delete) a table with flManualSub set true.
			<<It will be possible to "protect" a feed that came from a reading list by setting is flManualSub to true. 
			<<Every service table now has a readingList sub-table (if it was subscribed to by a reading list) and in that table is an element for each reading list. At the beginning of this routine we set each of these false. Any that are still false at the end of the scan are deleted. If the readingList sub-table is empty and it was not manually subscribed-to, the service is deleted.
local (adrdata = newsRiverSuite.init ());
bundle { //set the elements of each service's readingList table to false
	local (adrlist, adrservice);
	for adrlist in @adrdata^.newsRiver.readingLists {
		local (opmlurl = nameof (adrlist^));
		if defined (adrlist^.feeds) {
			local (adr);
			for adr in @adrlist^.feeds {
				local (xmlurl = nameof (adr^));
				local (adrservice = @adrdata^.services.[xmlurl]);
				if defined (adrservice^) {
					if defined (adrservice^.readingList) { //it was subscribed to by a reading list
						local (adropml = @adrservice^.readingList.[opmlurl]);
						adropml^ = false}}}} //if it's still false at the end of the scan, delete it
		else {
			new (tabletype, @adrlist^.feeds)}};
	for adrservice in @adrdata^.services {
		<<It's conceivable some of the readingList tables contain elements that have not been set false, so we quickly sweep over the whole table and set any that aren't false to false.
		if defined (adrservice^.readingList) {
			local (adritem);
			for adritem in @adrservice^.readingList {
				adritem^ = false}}}};
bundle { //read each OPML reading list, make sure we're subscribed to the feeds they contain
	local (adrlist);
	for adrlist in @adrdata^.newsRiver.readingLists {
		try {
			local (adr, opmlurl = nameof (adrlist^), flchange = false);
			newsRiverSuite.readingList.initList (adrlist);
			xml.compile (tcp.httpreadurl (opmlurl), @xstruct);
			<<scratchpad.xstruct = xstruct
			local (adropml = xml.getaddress (@xstruct, "opml"));
			bundle { //get elements from head
				local (adrhead = xml.getaddress (adropml, "head"));
				try {
					adrlist^.title = xml.getvalue (adrhead, "title")}
				else {
					adrlist^.title = string.nthfield (opmlurl, "/", string.countfields (opmlurl, "/"))}};
			local (adrbody = xml.getaddress (adropml, "body"));
			for adr in adrbody {
				if nameof (adr^) contains "outline" {
					local (xmlurl = adr^.["/atts"].xmlUrl, startticks = clock.ticks ());
					adrlist^.feeds.[xmlurl] = clock.now ();
					local (adrservice = @adrdata^.services.[xmlurl]);
					<<msg (xmlurl)
					if defined (adrservice^) {
						if not defined (adrservice^.readingList) {
							new (tabletype, @adrservice^.readingList);
							adrservice^.flManualSub = true;
							flchange = true}}
					else {
						local (name = "Untitled feed");
						if xml.aggregator.subscribeService (xmlurl, adradrservice: @adrservice) {
							new (tabletype, @adrservice^.readingList);
							adrservice^.flManualSub = false;
							try {name = adrservice^.compilation.channeltitle};
							log.addtooutline ("Subscribe: " + name, outlineUrl:xmlurl, startticks:startticks);
							flchange = true}};
					try {adrservice^.readingList.[opmlurl] = true}}};
			if flchange {
				adrlist^.stats.timeLastChange = clock.now ();
				adrlist^.stats.ctChanges++}}
		else {
			log.addtooutline ("Error: " + tryerror)}}};
bundle { //sweep up all old subs
	local (adrservice);
	for adrservice in @adrdata^.services {
		local (flautomaticsub = false);
		if defined (adrservice^.readingList) {
			flautomaticsub = true;
			if defined (adrservice^.flManualSub) {
				if adrservice^.flManualSub {
					flautomaticsub = false}}};
		if flautomaticsub {
			local (flunsub = true, adritem);
			for adritem in @adrservice^.readingList {
				if adritem^ { //it's still an active item
					flunsub = false;
					break}};
			if flunsub {
				local (xmlurl = nameof (adrservice^), name = "Untitled feed", startticks = clock.ticks ());
				try {name = adrservice^.compilation.channeltitle};
				xml.aggregator.unsubscribeService (xmlurl);
				log.addtooutline ("Unsub: " + name, outlineUrl:xmlurl, startticks:startticks)}}}}



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.