Monday, November 08, 2010 at 12:05 AM.

on viewSubscriptions () {
		<<3/20/02; 9:43:09 AM by DW
			<<Don't try to work with channels that don't have channelTitles. In theory this is never supposed to happen. But enough users are getting broken by this, I felt it was time to work around the problem.
		<<3/19/02; 5:27:30 PM by DW
			<<Support for partnerFeed attribute on GET request.
		<<1/24/02; 7:26:51 PM by DW
			<<Minor tweaks and reworks to make the page work better. Moved the Unsubscribe button to the bottom of the page.
		<<1/13/02; 11:18:05 PM by JES
			<<If a searchArg named url is present, use its value for the value of the URL-entry box. This makes it possible to create a link on your website, which allows people to subscribe their copy of Radio to your RSS feed.
		<<1/9/02; 12:52:03 AM by JES
			<<Decode entities in the channel name in the success message, which appears after subscribing to a new channel.
		<<1/9/02; 12:47:39 AM by JES
			<<Moved the Delete button to a similar location to where it is on the News page -- it's no longer on its own, to the right of the subscriptions table. Added <nobr/> tags to the date field in the subscriptions table, so that the dates won't wrap to multiple lines.
		<<1/6/02; 4:25:04 AM by JES
			<<Added table css classes.
		<<1/4/02; 3:32:20 PM by JES
			<<Commented out some debugging code.
		<<12/12/01; 11:16:32 AM by DW
			<<Workaround crashing bug in kernel.
		<<12/10/01; 1:38:16 AM by JES
			<<Decode entities in the channel title -- no more ' in the page.
		<<12/8/01; 12:50:36 PM by DW
			<<Adding a new subscription should provide feedback. "You are now subscribed to the blah blah feed."
			<<Improved the error message when a subscription doesn't work.
		<<11/24/01; 3:55:33 PM by DW
			<<Added help link.
		<<11/14/01; 10:00:27 AM by DW
		<<1/23/01; 3:12:53 PM by DW
		<<2/12/01; 10:00:38 AM by DW
			<<Clean up and debug.
			<<Add headers to columns.
			<<Fixed a major bug in unsubscribing. If you unsubscribed from more than one channel, it would change the indexing and believe it or not it would unsubscribe from the wrong channels. Oy. Now we scan in reverse order and pad the checkbox names with 0's so things get deleted in the correct order.
		<<2/12/01; 4:40:42 PM by PBS
			<<Channels with quotes and angle brackets in the description would message up the rendering. Fixed.
			<<Link title for the XML button, so it's consistent with other pages.
		<<2/24/01; 6:16:29 PM by PBS
			<<Help links.
		<<3/4/01; 5:05:00 PM by JES
			<<Toss the HotList cache when subscribing or unsubscribing to/from a channel.
		<<3/4/01; 6:13:06 PM by JES
			<<Toss the homepage cache when unsubscribing from a channel.
		<<3/5/01; 8:47:23 PM by PBS
			<<Fixed a typo when tossing the hot list cache.
		<<7/26/01; 4:04:24 PM by JES
			<<Don't error if a channel's channeldescription or channellink aren't defined.
	local (pta = html.getpagetableaddress ());
	<<scratchpad.params = pta^
	pta^.title = radio.string.getlocalizedstring ("subscriptions.title");
	local (adrdata = xml.aggregator.init ());
	local (xmlImg = radio.images.systemImageRef ("icons/xml", flFileUrl:pta^.radioResponder.flSameMachine));
	local (htmltext = "", indentlevel = 0);
	on add (s) {
		htmltext = htmltext + string.filledstring ("\t", indentlevel) + s + "\r\n"};
	local (errorstring = "", initialurl = "http://", adrnewservice = nil);
	if pta^.method == "POST" {
		local (args);
		new (tabletype, @args);
		webserver.parseargs (pta^.requestbody, @args);
		if defined (args.subscribe) {
			if sizeof (args.url) > sizeof (initialurl) {
				if not xml.aggregator.subscribeService (args.url, @errorstring, @adrnewservice) {
					initialurl = args.url}}};
		if defined (args.unsubscribe) {
			delete (@args.unsubscribe);
			try {delete (@args.url)};
			local (i, adr, adrservice);
			for i = sizeof (args) downto 1 {
				adr = @args [i];
				adrservice = @adrdata^.services [number (nameof (adr^))];
				xml.aggregator.unsubscribeService (nameof (adrservice^))}}};
	local (sortedtable);
	bundle { //fill sorted table
		local (adrsort);
		new (tabletype, @sortedtable);
		for adr in @adrdata^.services {
			if defined (adr^.compilation) {
				if sizeof (adr^.compilation) > 0 {
					try { //missing channeltitle doesn't stop us -- 3/20/02 DW
						local (channeltitle = adr^.compilation.channeltitle);
						if sizeof (channeltitle) > 0 {
							adrsort = @sortedtable.[adr^.compilation.channeltitle];
							if defined (adrsort^) { //collision
								local (ct = 1);
								loop {
									adrsort = @sortedtable.[adr^.compilation.channeltitle + "." + ct++];
									if not defined (adrsort^) {
							adrsort^ = nameof (adr^)}}}}}};
		<<local (oldtarget = target.set (@sortedtable))
		<<table.sortby ("Value")
		<<target.set (oldtarget)
		<<scratchpad.sortedtable = sortedtable; edit (@scratchpad.sortedtable)
	add ("<form method=\"POST\">"); indentlevel++;
	bundle { //add the URL entry box
		if pta^.method == "GET" { //1/13/02 JES: try to set initialurl
			if defined (pta^.radioResponder.getArgs.url) {
				initialurl = pta^.radioResponder.getArgs.url};
			if defined (pta^.radioResponder.getArgs.partnerFeed) { //something like "nyt.arts"
				local (feed = pta^.radioResponder.getArgs.partnerFeed);
				local (part1 = string.nthfield (feed, ".", 1));
				local (part2 = string.nthfield (feed, ".", 2));
				local (adrurl =[part1].[part2]);
				if defined (adrurl^) {
					initialurl = adrurl^}}};
		add ("<p>" + radio.string.getlocalizedstring ("subscriptions.subscribeIntro") + "</p>");
		add ("<blockquote>"); indentlevel++;
		add ("<table>"); indentlevel++;
		add ("<tr>"); indentlevel++;
		add ("<td align=\"left\">" + "URL" + ":</td>");
		add ("<td valign=\"top\"><input type=\"text\" name=\"url\" size=\"55\" maxlength=\"255\" value=\"" + initialurl + "\"></td>");
		add ("<td><input type=\"submit\" name=\"subscribe\" value=\"" + radio.string.getlocalizedstring ("subscriptions.addButton") + "\">" + "</td>");
		add ("</tr>"); indentlevel--;
		<<add ("<tr><td colspan=\"3\" align=\"right\">" + radio.userInterface.helpLink ("addChannel") + "</td></tr>")
		add ("</table>"); indentlevel--;
		add ("</blockquote>"); indentlevel--;
		if errorstring != "" {
			add ("<font color=\"red\">" + radio.string.getlocalizedstring ("subscriptions.cantSubscribeBecause") + "\"" + errorstring + "\"</font><br>")};
		if adrnewservice != nil {
			<<12/12/01; 11:16:51 AM by DW
				<<Site of a crashing bug in Radio 7.1b36. 
				<<Workaround provided by Lawrence Lee. 
				<<(Good work!!)
			local (t);
			new (tabletype, @t);
			t.channeltitle = radio.string.decodeEntities (adrnewservice^.compilation.channeltitle);
			add (radio.string.getlocalizedstring ("subscriptions.confirmSubscription", @t))}};
		<<add ("<br>")
	add ("<p>");
	<<bundle //add unsubscribe button
		<<add ("<table align=\"right\" cellspacing=\"0\" cellpadding=\"1\"><tr><td valign=\"top\">  <input type=\"submit\" name=\"unsubscribe\" value=\"" + radio.string.getlocalizedstring ("subscriptions.unsubscribeButton") + "\"></td></tr></table>")
	bundle { //add the text at the top of the page
		add (radio.string.getlocalizedstring ("subscriptions.tableIntro") + " " + radio.userInterface.helpLink ("The Subscriptions page"))};
	add ("</p>");
	add ("<blockquote>"); indentlevel++;
	add ("<table><tr>"); indentlevel++;
	add ("<td>"); indentlevel++;
	bundle { //add subscription table
		add ("<table class=\"dwsFrameTable\" cellspacing=\"0\" cellpadding=\"0\"><tr bgcolor=\"" + + "\"><td>"); indentlevel++;
		add ("<table class=\"dwsTable\" border=\"0\" cellspacing=\"1\" cellpadding=\"5\">"); indentlevel++;
		local (i, adrsort, adr, adrsource, ct = 0);
		<<bundle //add header
			<<add ("<tr bgcolor=\"" + + "\"><td> </td><td><b>Name</b></td><td align=\"right\"><b>Updated</b></td><td align=\"right\"><b>Ct</b></td><td> </td></tr>")
		for adrsort in @sortedtable {
			adr = @adrdata^.services.[adrsort^];
			if defined (adr^.compilation) {
				if sizeof (adr^.compilation) > 0 {
					local (title = adr^.compilation.channeltitle);
					local (channeldescription = "", channellink = "");
					if defined (adr^.compilation.channeldescription) {
						channeldescription = radio.string.decodeEntities (adr^.compilation.channeldescription)};
					if defined (adr^.compilation.channellink) {
						channellink = adr^.compilation.channellink};
					add ("<tr bgcolor=\"" + + "\">"); indentlevel++;
					bundle { //add checkbox
						local (name = nameof (adr^));
						local (checkboxval = "xxx ");
						if false {
							checkboxval = checkboxval + "checked"};
						add ("<td class=\"dwsTableCell\" valign=\"top\"><input type=\"checkbox\" name=\"" + string.padwithzeros (indexOf (adr^), 8) + "\" value=" + checkboxval + "></td>")};
					if sizeof (title) > 50 {
						title = string.mid (title, 1, 50) + ".."};
					add ("<td class=\"dwsTableCell\" valign=\"top\" title=\"" + radio.html.translateToEntities (channeldescription) + "\" nowrap><a href=\"" + channellink + "\">" + radio.string.decodeEntities (title) + "</a></td>");
					add ("<td class=\"dwsTableCell\" align=\"right\" valign=\"top\"><nobr><font size=\"-1\">" + radio.string.getDateString (adr^.timeLastChange, false) + "</font></nobr></td>");
					add ("<td class=\"dwsTableCell\" align=\"right\" valign=\"top\"><font size=\"-1\">" + adr^.ctChanges + "</font></td>");
					add ("<td class=\"dwsTableCell\" valign=\"top\"><a href=\"" + nameof (adr^) + "\" title=\"" + + "\">" + xmlimg + "</a></td>");
					add ("</tr>"); indentlevel--}}};
		add ("</table>"); indentlevel--;
		add ("</td></tr></table>"); indentlevel--};
	add ("</td>"); indentlevel--;
	add ("</tr></table>"); indentlevel--;
	add ("</blockquote>"); indentlevel--;
	add ("<br><a name=\"unsubscribeButton\"><input type=\"submit\" name=\"unsubscribe\" value=\"Unsubscribe\"></a>");
	add ("</form>"); indentlevel--;
	return (htmltext)}
<<bundle //test code
	<<html.setpagetableaddress (@scratchpad.params)
	<<viewSubscriptions ()

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.