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

system.verbs.builtins.html.directory.viewDirectory

on viewDirectory (adroutline, directoryHomeUrl, remainingPath="", identifier="", opmlUrl="", flIcons=true, flLinkText=false, flTopLevel=false,  flDirectoryInABox=false, title="", template="", commentTemplate="", suggestALinkTemplate="", foldericon="", linkicon="", flDisplayTitle=false, flAddOpmlUrlToSearchArgs=false, adrdirectories=nil, ownername=user.prefs.name, owneremail=user.prefs.mailAddress, flUntaint=true, miniXmlIcon="", miniXmlCoffeeMugIcon="", flAllLevels=false, cssPrefix="directory", adrInSiteInclusionCallback=nil, linktarget="") {
	<<Changes
		<<6/13/02; 12:53:34 PM by JES
			<<New optional parameter, linktarget, specifies the value of the target attribute for links in the directory. Needed for the directory-in-a-frame feature in RCS.
		<<6/5/02; 4:54:52 PM by JES
			<<New optional parameter, adrInSiteInclusionCallback, which is passed to html.directory.walk.
			<<Fixed link generation for in-site inclusion for directories rendered in Manila sites. When a link is to another directory in the same site, use ~[msgnum]~ for the path to the sub-directory.
			<<Don't add the Edit With Radio button here. This code is meant to run in the wsf context, and therefore doesn't know about msgNum's.
		<<6/4/02; 3:24:31 PM by JES
			<<Added a few pixels of padding to the top of the table cell which contains the icon.
		<<5/10/02; 2:59:13 PM by JES
			<<Given the address of an outline, and some other contextual information, render the outline as an HTML directiry.
			<<Cribbed from manilaSuite.directory.viewDirectory.
		<<12/19/01; 2:42:16 PM by PBS
			<<Call manilaSuite.sendMail rather than tcp.sendMail, so user callbacks get called, so text gets converted (bug fix from David Bayly on Frontier-Server list).
		<<12/13/01; 1:40:41 PM by PBS
			<<Fixed a date coercion bug in international systems. The bug was that a date wasn't being coerced. See the shortDate subroutine.
		<<03/20/01; 9:50:47 PM by PBS
			<<The page title of the top-level of each directory was not getting set in the cache -- the title in the cache was the empty string. Now it's cached.
		<<3/13/01; 9:01:41 PM by DW
			<<The page title of the top-level of each directory was the empty string. Uck. Added a conditional test, if it's empty, set it to the subject of the topmost message, topMsgSubject. 
		<<3/6/01; 10:39:17 AM by DW
			<<Add {subItems} macro, says how many items are displayed at the level you're viewing. I got tired of counting the number of new SOAP implementations by hand. A good problem to have!!
			<<Also, to make it legal, I also had to add an assignment in the "directorytemplate" branch in manilaSuite.themes.getLegalMacrosForObject
		<<3/1/01; 2:03:06 PM by DW
			<<Fix two bugs in shortDate subroutine.
		<<2000:
			<<11/9/2000; 6:24:10 AM by DW
				<<Created.
			<<11/9/2000; 5:52:41 PM by DW
				<<Added params. If flTopLevel, we know to render the top level of the directory. In this case msgnum must be set to the msg containing the outline for the directory to render.
			<<11/16/2000; 7:20:51 AM by DW
				<<Include author information, number of hits, etc; at the bottom of the directory display.
			<<11/16/2000; 10:34:18 AM by DW
				<<msgnum can be a path now, like 4/theWeb/publications/news/general, which allows you to start at a deeply nested point in a directory.
			<<11/17/2000; 7:49:02 AM by DW
				<<Suggest a link feature.
			<<11/22/2000; 7:07:00 AM by DW
				<<Finish up templatization.
			<<11/22/00; 5:02:45 PM by PBS
				<<Don't read from or save in cache if flDirectoryInABox is true.
			<<11/23/2000; 4:38:25 PM by DW
				<<When displaying in a box, don't display comments.
			<<11/24/2000; 8:07:56 AM by DW
				<<Don't set the page title if flDirectoryInABox is true.
			<<11/24/2000; 7:19:24 PM by DW
				<<Relax rule about inclusion, now included directories can be anywhere
					<<Add a new branch to the cache in each dg message, called xmlstructs
						<<The name of each item is its url. If the value is a boolean false, that means we weren't able to read or parse the file. Don't retry. 
						<<The cache is filled when you display the level containing the link. Only files whose extension are .opml are checked. 
						<<The url that's generated keys off the name of the item, get its link to locate the xmlstruct in the cache.
					<<Changed parts
						<<manilaSuite.directory.viewDirectory
						<<manilaSuite.directory.walk
						<<manilaSuite.directory.initMessageTable
			<<11/26/00; 1:21:32 PM by PBS
				<<Add the external editor button ("Pike button") below directories if the viewer has edit privileges.
				<<Inhibit title display, unless the pref is turned on. (It's off by default.)
				<<Static rendering fix -- check that searchArgs is defined before referencing it. This fixes the viewDirectoryBox macro on the static version of pages.
				<<Fixed HTML errors -- there were tags being added that are in the directory template. They were hard-coded, they needed to be removed.
			<<11/26/2000; 7:48:14 PM by DW
				<<Added support for the directories table.
			<<11/26/00; 9:57:01 PM by PBS
				<<Fixed an HTML bug -- with the suggest a link page, the table was not being closed. Now it is. (An earlier HTML fix was half-right -- I had commented out a closing table tag, but the wrong one.)
			<<11/27/2000; 9:11:14 AM by DW
				<<Getting directories displaying correctly again.
				<<It seems clear to me that the change that Brent made can't work. He's commenting out a closing-table tag without commenting the balancing opening-table tag. If this works, the previous code must have been wrong in some way. It clearly doesn't work, this screen shot illustrates.
				<<http://www.scripting.com/images/misalignedDirectory.gif
				<<It seems clear to me that the directory items must be a table. Each item is a row in a table, if it's not a table, what table will enclose them? I just don't see the logic.
				<<Anyway, I think what we're generating now is right. 
			<<11/27/00; 12:37:46 PM by PBS
				<<There were a number of small bugs fixed.
				<<1. The outer table didn't also add outer tr and td tags. So you could have <table..><table..>, which is illegal. Now it does add tr and td tags (and close them later).
				<<2. The suggest-a-link page page was adding outer tr and td tags, but the directory rendering was not. So now neither do. (See above.)
				<<3. When building a directory page, the outer table was getting tossed. A line that read htmltext = s should have read htmltext = htmltext + s. But the closing outer table tag was still being added, so you had an extra closing table tag with directories, but not with the suggest-a-link page.
				<<4. An HTML validation issue was fixed -- a tr tag cannot have a height attribute. The height attribute was moved to the enclosed td tag. There's still a validation issue with the textarea tag -- wrap is not allowed. But that's all throughout Manila, and no good solution exists for removing it.
			<<12/5/2000; 8:43:53 PM by DW
				<<Add an expiration facility for cached pages. Search the script for adrexpires to see how it works. Quite simple.
			<<12/12/2000; 5:17:10 PM by DW
				<<When rendering a directory box, hand-construct the URL for linking to included directories. 
			<<12/14/2000; 9:01:05 AM by DW
				<<Add support for {opmlUrl} feature.
			<<12/15/2000; 1:37:10 AM by DW
				<<Suggest-a-link email comes from the submitter
	
	local (flusecache = true); //set this false if you're debugging
	
	local (linktargetstring = "");
	bundle { //set defaults for optional parameters
		if identifier == "" {
			identifier = string.popFileFromAddress (adroutline)};
		if template == "" {
			template = html.directory.data.defaultPrefs.template};
		if commentTemplate == "" {
			commentTemplate = html.directory.data.defaultPrefs.commentTemplate};
		if suggestALinkTemplate == "" {
			suggestALinkTemplate = html.directory.data.defaultPrefs.suggestALinkTemplate};
		if foldericon == "" {
			foldericon = html.directory.data.defaultPrefs.foldericon};
		if linkicon == "" {
			linkicon = html.directory.data.defaultPrefs.linkicon};
		if miniXmlIcon == "" {
			miniXmlIcon = html.directory.data.defaultPrefs.miniXmlIcon};
		if miniXmlCoffeeMugIcon == "" {
			miniXmlCoffeeMugIcon = html.directory.data.defaultPrefs.miniXmlCoffeeMugIcon};
		if adrdirectories == nil { //store in system.temp.opmlDirectories
			adrdirectories = @system.temp.opmlDirectories};
		if linktarget != "" {
			linktargetstring = " target=\"" + linktarget + "\""}};
	
	local (flDirectoryHomeUrlEndsWithSlash = false);
	bundle { //make sure directoryHomeUrl does not end with a '/', and set flDirectoryHomeUrlEndsWithSlash
		if directoryHomeUrl endswith "/" {
			directoryHomeUrl = string.delete (directoryHomeUrl, sizeOf (directoryHomeUrl), 1);
			flDirectoryHomeUrlEndsWithSlash = true}};
	
	local (directoriesUrl); //used for determining in-site inclusions for Manila sites
	bundle { //set lowerDirectoriesUrl
		<<Explanation
			<<In a Manila site, a directory's url is always [siteUrl]/directory/[msgnum].
			<<manilaSuite.directory.viewDirectory passes in this URL for the value of directoryHomeUrl. What we do is strip off the [msgnum] part, so that we can check to see if a link node begins with [siteUrl]/directory/. If so, then we know that the link is actually an in-site inclusion -- an inclusion of a directory in another message in this site.
			<<This broke when the meaning of directoryHomeUrl changed in order to be able to support directories rendered outside of Manila.
		local (s = string.lower (directoryHomeUrl));
		local (sizeLastPart = sizeOf (string.nthField (s, "/", string.countFields (s, "/"))));
		directoriesUrl = string.delete (s, sizeOf (s) - sizeLastPart + 1, sizeLastPart)};
	
	<<local (thisPageUrl = string.delete (homePageUrl, sizeof (homePageUrl), 1) + pta^.uri)
	local (pta = html.getPageTableAddress ());
	local (thisPageUrl = "http://" + pta^.host + pta^.uri);
	local (flOuterTables = not flDirectoryInABox, flShowStatsLine = not flDirectoryInABox);
	local (flSkipLeadingComment = false); //if true we don't display the first comment at the current level
	
	local (searchArgs, linkArgs="");
	bundle { //if the method is GET, fill searchArgs, and set linkArgs
		new (tabletype, @searchArgs);
		if pta^.method == "GET" {
			if defined (pta^.searchArgs) { //PBS 11/26/00: search args not defined when static rendering
				webserver.parseargs (pta^.searchargs, @searchArgs)}};
		bundle { //set linkArgs -- these are appended to folder-links
			local (linkArgsTable); new (tableType, @linkArgsTable);
			if defined (searchArgs.url) {
				linkArgsTable.url = searchArgs.url}
			else {
				if flAddOpmlUrlToSearchArgs {
					linkArgsTable.url = opmlUrl}};
			if defined (searchArgs.remoteInclude) {
				if linkArgs != "" {
					linkArgs = linkArgs + "&"};
				linkArgsTable.remoteInclude = searchArgs.remoteInclude};
			if defined (searchArgs.cssUrl) {
				linkArgsTable.cssUrl = searchArgs.cssUrl};
			if defined (searchArgs.flIcons) {
				linkArgsTable.flIcons = searchArgs.flIcons};
			if defined (searchArgs.flMinimalTemplate) {
				linkArgsTable.flMinimalTemplate = searchArgs.flMinimalTemplate};
			if defined (searchArgs.flXmlButton) {
				linkArgsTable.flXmlButton = searchArgs.flXmlButton};
			if defined (searchArgs.flLinkText) {
				linkArgsTable.flLinkText = searchArgs.flLinkText};
			linkArgs = webserver.encodeArgs (@linkArgsTable);
			if sizeOf (linkArgs) > 0 { //prepend a '?'
				linkArgs = "?" + linkArgs}}};
	
	local (messagetouser = "");
	bundle { //get the message from search args if it's present
		if defined (searchArgs.message) {
			messagetouser = searchArgs.message}};
	
	local (path, origpath);
	bundle { //set path, origpath
		path = remainingpath;
		origpath = path};
	
	local (adrdirectoryinfo = html.directory.initDirectoryTable (identifier, adrdirectories));
	html.directory.compileIfDirty (adroutline, identifier, adrdirectoryinfo, ownername, owneremail);
	
	local (ctpagehits = 0);
	bundle { //bump hit counter for page
		local (adrhit = @adrdirectoryinfo^.hits.[pta^.uri]);
		if defined (adrhit^) {
			adrhit^++}
		else {
			adrhit^ = 1};
		ctpagehits = adrhit^};
	on postprocess (s) { //fast macro substitution
		s = string.replaceall (s, "[[hits]]", ctpagehits);
		if sizeof (messagetouser) > 0 {
			s = "<font color=\"red\">" + messagetouser + "</font><br><br>" + s};
		<<if not flDirectoryInABox //PBS 11/26/00: add the external editor button
			<<if defined (pta^.adrMemberInfo)
				<<if mainResponder.discuss.memberCanEdit (adrMsg, pta^.adrMemberInfo, pta)
					<<s = s + "\r<p>" + manilaSuite.pike.editMessageButton (adrMsg, "Edit this directory in an outline in Radio UserLand.", pta)
		if not flDisplayTitle { //PBS 11/26/00: possibly turn off display of the title
			html.directory.stripTitle (pta)};
		return (s)};
	
	local (adrcachepage, adrcachetitle, adrexpires);
	bundle { //serve from cache if possible
		adrcachepage = @adrdirectoryinfo^.cache.pages.[pta^.uri];
		adrcachetitle = @adrdirectoryinfo^.cache.titles.[pta^.uri];
		adrexpires = @adrdirectoryinfo^.cache.expires.[pta^.uri];
		if flusecache {
			if pta^.method == "GET" {
				if not flDirectoryInABox {
					if not defined (searchArgs.command) {
						<<Note
							<<If we didn't check for the command, the suggest-a-link feature wouldn't work. 
								<<We'd return the cached "content page" for the URL. 
								<<If a command is present that's a clue that we can't use the cache.
						local (flexpired = true);
						if defined (adrexpires^) {
							if clock.now () < adrexpires^ {
								flexpired = false}};
						if not flexpired {
							if defined (adrcachepage^) and defined (adrcachetitle^) { //serve from cache
								pta^.title = adrcachetitle^;
								return (postprocess (adrcachepage^))}}}}}}};
	local (crumblist = {}, adrlastxstruct, rules);
	local (adritem = html.directory.walk (adroutline, identifier, path, @crumblist, @adrlastxstruct, @rules, @opmlUrl, adrdirectories, ownername, owneremail, adrInSiteInclusionCallback));
	bundle { //build the page title
		if not flDirectoryInABox {
			local (s = "");
			for crumb in crumblist {
				s = s + crumb + " > "};
			s = string.mid (s, 1, sizeof (s) - 3);
			if sizeof (s) == 0 {
				if sizeOf (title) > 0 {
					pta^.title = title;
					adrcachetitle^ = title}} //PBS 03/20/01: cache title of top-level page
			else {
				pta^.title = s;
				adrcachetitle^ = s}}};
	local (htmltext = "", indentlevel = 0);
	on add (s) {
		htmltext = htmltext + string.filledString ("\t", indentlevel) + s + "\r"};
	if flOuterTables {
		add ("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">"); indentlevel++;
		add ("<tr><td>");indentlevel++}; //PBS 11/27/00: outer tr and td tags needed, to avoid <table..><table..>
	local (crumbstring);
	bundle { //set crumbstring
		if sizeof (crumblist) == 0 {
			local (comment = html.directory.getFirstComment (adritem));
			if sizeof (comment) == 0 {
				crumbstring = "<b>Top</b>"}
			else {
				crumbstring = "<b>Top</b>: " + comment;
				flSkipLeadingComment = true}}
		else {
			crumbstring = html.directory.getBreadcrumbString (crumblist, pta^.uri, linkArgs, flDirectoryHomeUrlEndsWithSlash)}};
	local (flsaveincache = false);
	local (macroStartChars = "{", macroEndChars = "}");
	bundle { //build the directory page body in htmltext
		bundle { //get macroStartChars and macroEndChars
			if defined (pta^.macroStartCharacters) {
				macroStartChars = pta^.macroStartCharacters};
			if defined (pta^.macroEndCharacters) {
				macroEndChars = pta^.macroEndCharacters}};
		local (ownername, owneremail, modified, created);
		bundle { //get ownername, owneremail, etc
			on shortdate (d) {
				d = date (d); //PBS 12/13/01: d is a string but it needs to be a date
				local (day, month, year, hour, minute, second);
				date.get (d, @day, @month, @year, @hour, @minute, @second);
				return (month + "/" + day + "/" + string.mid (year, 3, 2))};
			local (nomad = xml.getaddress (adrlastxstruct, "opml"));
			nomad = xml.getaddress (nomad, "head");
			ownername = xml.getvalue (nomad, "ownerName");
			owneremail = xml.getvalue (nomad, "ownerEmail");
			modified = shortdate (xml.getvalue (nomad, "dateModified"));
			created = shortdate (xml.getvalue (nomad, "dateCreated"))};
		local (command = "normal");
		case pta^.method {
			"GET" {
				if defined (searchArgs.command) {
					command = searchArgs.command}};
			"POST" {
				if defined (pta^.postargs.command) {
					command = pta^.postargs.command}}};
		case command {
			"normal" {
				local (flAtLeastOneLink = false, directoryBodyText, ctsubitems = 0);
				bundle { //render the current level into directoryBodyText
					directoryBodyText = "";
					local (indentlevel = 0);
					on add (s) {
						directoryBodyText = directoryBodyText + string.filledString ("\t", indentlevel) + s + "\r"};
					if flOuterTables {
						add ("<table class=\"" + cssPrefix + "Table\" cellspacing=\"0\" cellpadding=\"5\" border=\"0\">"); indentlevel++};
					if path beginswith "/" {
						path = string.delete (path, 1, 1)};
					
					local (itemlist = {});
					on addtoitemlist (s) {
						ctsubitems++;
						itemlist = itemlist + {s}};
					on addItem (adr, flLinkFolders=true) {
						if nameof (adr^) beginswith "/" { //it's not an outline
							return ("")};
						local (text = adr^.["/atts"].text, url = "", icon = "", flfolder = false, type = "text");
						bundle { //decode any entities in the XML text
							text = xml.entityDecode (text, flAlphaEntities:true)};
						bundle { //skip over rules
							if string.lower (text) beginswith "<rules>" {
								<<add ("<tr><td valign=\"top\"> </td><td>" + rules.columns.value + "</td></tr>")
								return}};
						for adrsub in adr { //see if it has subs
							if not (nameof (adrsub^) beginswith "/") { //it's an outline
								local (title);
								bundle { //set the url
									if flTopLevel {
										url = homePageUrl + "directory/" + origpath + "/" + html.directory.getcanonicalname (text)}
										<<bundle //unnecessarily verbose
											<<url = homePageUrl + "directory"
											<<if not ((url endsWith "/") or (origpath beginsWith "/"))
												<<url = url + "/"
											<<url = url + origpath
											<<if not (origpath endsWith "/")
												<<url = url + "/"
											<<url = url + html.directory.getcanonicalname (text)
									else {
										<<url = directoryHomeUrl + "/" + html.directory.getcanonicalname (text)
										<<url = pta^.uri
										url = directoryHomeUrl;
										if not (remainingPath beginsWith "/") {
											url = url + "/"};
										url = url + remainingPath;
										if not (url endsWith "/") {
											url = url + "/"};
										url = url + html.directory.getcanonicalname (text)};
									url = url + linkArgs};
								bundle { //set the title
									title = "";
									try {
										if adrsub^.["/atts"].isComment {
											local (s = xml.entityDecode (adrsub^.["/atts"].text, flAlphaEntities:true));
											if not (s beginswith "<") {
												title = " title=\"" + s + "\""}}}};
								if flLinkFolders {
									icon = "<a class=\"" + cssPrefix + "IconLink\" href=\"" + url + "\"" + title + ">" + foldericon + "</a>"};
								flfolder = true;
								type = "folder";
								break}};
						if not flfolder { //might be a comment or a link (or..?)
							try { //handle comment
								if adr^.["/atts"].isComment {
									if flDirectoryInABox { //skip comments in boxes
										return};
									if flSkipLeadingComment {
										flSkipLeadingComment = false;
										return};
									text = string.replaceall (commentTemplate, macrostartchars + "comment" + macroendchars, text);
									<<text = "<i>" + text + "<br><br></i>"
									type = "comment"}};
							try { //handle link
								if defined (adr^.["/atts"].url) or defined (adr^.["/atts"].htmlUrl) { //new version which understands all kinds of nodes with URLs
									if defined (adr^.["/atts"].url) {
										url = adr^.["/atts"].url}
									else {
										url = adr^.["/atts"].htmlUrl};
									if string.lower (url) beginswith string.lower (directoriesUrl) { //it's a link into the directory on this site
										local (linkremainder = string.delete (url, 1, sizeof (directoriesUrl)));
										if flTopLevel {
											url = directoriesUrl + origpath + "/~" + linkremainder + "~"}
										else {
											url = pta^.uri + "/~" + linkremainder + "~"};
										icon = "<a class=\"" + cssPrefix + "IconLink\" href=\"" + url + "\">" + foldericon + "</a>";
										type = "folder"}
									else {
										if string.lower (url) endswith ".opml" {
											local (adrincache = html.directory.getInclusionFromCache (identifier, url, adrdirectories));
											if typeof (adrincache^) == stringtype {
												text = text + " (Error: " + adrincache^ + ")"};
											type = "folder";
											flfolder = true;
											<<local (linkurl)
											<<bundle //set the linkurl
												<<linkurl = html.directory.getcanonicalname (text)
												<<if flTopLevel
													<<linkurl = homePageUrl + "directory/" + origpath + "/" + linkurl
												<<else
													<<linkurl = pta^.uri + "/" + linkurl
													<<linkurl = linkurl + linkArgs
											bundle { //set the linkurl
												url = html.directory.getcanonicalname (text);
												if flTopLevel {
													url = homePageUrl + "directory/" + origpath + "/" + url}
												else {
													if pta^.uri endswith "/" {
														url = pta^.uri + url}
													else {
														url = pta^.uri + "/" + url};
													url = url + linkArgs}};
											icon = "<a class=\"" + cssPrefix + "IconLink\" href=\"" + url + "\">" + foldericon + "</a>"}
										else {
											icon = "<a class=\"" + cssPrefix + "IconLink\" href=\"" + url + "\"" + linktargetstring + ">" + linkicon + "</a>";
											type = "link";
											flAtLeastOneLink = true}}}}};
								<<if adr^.["/atts"].type == "link" //old version
									<<url = adr^.["/atts"].url
									<<if string.lower (url) beginswith string.lower (directoryHomeUrl) //it's a link into the directory on this site
										<<local (linkremainder = string.delete (url, 1, sizeof (directoryHomeUrl)))
										<<if flTopLevel
											<<url = homePageUrl + "directory/" + origpath + "/~" + linkremainder + "~"
										<<else
											<<url = pta^.uri + "/~" + linkremainder + "~"
										<<icon = "<a class=\"" + cssPrefix + "IconLink\" href=\"" + url + "\">" + foldericon + "</a>"
										<<type = "folder"
									<<else
										<<if string.lower (url) endswith ".opml"
											<<local (adrincache = html.directory.getInclusionFromCache (identifier, url, adrdirectories))
											<<if typeof (adrincache^) == stringtype
												<<text = text + " (Error: " + adrincache^ + ")"
											<<type = "folder"
											<<flfolder = true
											<<local (linkurl)
											<<bundle //set the linkurl
												<<linkurl = html.directory.getcanonicalname (text)
												<<if flTopLevel
													<<linkurl = homePageUrl + "directory/" + origpath + "/" + linkurl
												<<else
													<<linkurl = pta^.uri + "/" + linkurl
													<<linkurl = linkurl + linkArgs
											<<bundle //set the linkurl
												<<url = html.directory.getcanonicalname (text)
												<<if flTopLevel
													<<url = homePageUrl + "directory/" + origpath + "/" + url
												<<else
													<<url = pta^.uri + "/" + url
													<<url = url + linkArgs
											<<icon = "<a class=\"" + cssPrefix + "IconLink\" href=\"" + url + "\">" + foldericon + "</a>"
										<<else
											<<icon = "<a class=\"" + cssPrefix + "IconLink\" href=\"" + url + "\">" + linkicon + "</a>"
											<<type = "link"
											<<flAtLeastOneLink = true
						local (xmlLinks = "", itemtext);
						if defined (adr^.["/atts"].xmlUrl) { //set xmlLinks to the coffee mug and xml button links
							local (xmlurl = adr^.["/atts"].xmlUrl);
							local (xmlLink = "<a href=\"" + xmlurl + "\" title=\"Click to view the XML source for "" + string.replaceAll (text, "\"", """) + "".\">" + miniXmlIcon + "</a>");
							local (coffeeMugLink = "<a href=\"http://127.0.0.1:5335/system/pages/subscriptions?url=" + string.urlEncode (xmlurl) + "\" title=\"Click to subscribe to "" + string.replaceAll (text, "\"", """) + "" in Radio UserLand.\">" + miniXmlCoffeeMugIcon + "</a>");
							xmlLinks = coffeeMugLink + " " + xmlLink + " "};
						if flIcons {
							if flOuterTables {
								if (type == "text") or (type == "comment") {
									itemtext = "<td> </td><td class=\"" + cssPrefix + "Comment\">" + xmlLinks + text + "</td>"}
								else {
									<<bundle //move the icon down by a few pixels; JES 5/15/02: Commented out. Now accomplished with a style attribute. Much smaller, easier to read the HTML, and renders faster in Netscape.
										<<explanation
											<<Comments must be aligned at the top for multi-line elements, but it looks awkward for single-line items if it's flush with the top.
										<<icon = "<table cellspacing=\"0\" cellpadding=\"0\"><tr><td height=\"2\"></td></tr><tr><td>" + icon + "</td></tr></table>" //PBS 11/27/00: tr tags may not have height attributes -- moved height attribute to td tag (validation issue)
									if flLinkText {
										text = "<a class=\"" + cssPrefix + "ItemLink\" href=\"" + url + "\">" + text + "</a>"};
									itemtext = "<td class=\"" + cssPrefix + "Icon\" align=\"center\" valign=\"top\" style=\"padding-top:5px\">" + icon + "</td><td class=\"" + cssPrefix + "Item\" valign=\"top\">" + xmlLinks + text + "</td>"}}
							else {
								itemtext = "<td class=\"" + cssPrefix + "Icon\" align=\"center\" valign=\"top\" style=\"padding-top:5px\">" + icon + "</td><td class=\"" + cssPrefix + "Item\">" + xmlLinks + text + "</td>"}}
						else { //no icons
							if (type == "text") or (type == "comment") {
								itemtext = "<td class=\"" + cssPrefix + "Comment\">" + xmlLinks + text + "</td>"}
							else {
								local (cssClass = cssPrefix + "Item");
								if type == "folder" {
									cssClass = cssPrefix + "Folder"};
								if type == "folder" and (not flLinkFolders) {
									itemtext = "<td class=\"" + cssClass + "\">" + xmlLinks + text + "</td>"}
								else {
									itemtext = "<td class=\"" + cssClass + "\">" + xmlLinks + "<a class=\"" + cssPrefix + "ItemLink\" href=\"" + url + "\"" + linktargetstring + ">" + text + "</a></td>"}}};
						addtoitemlist (itemtext);
						return (type)};
					if defined (rules.allLevels.value) { //rules in the outline can override the flAllLevels input parameter
						if rules.allLevels.value {
							flAllLevels = true}};
					if flAllLevels { //add this level and all sub-levels
						on addlevel (adrparent) {
							local (adr);
							for adr in adrparent {
								if addItem (adr, false) == "folder" {
									addlevel (adr)}}};
						addlevel (adritem)}
					else { //add only items at this level
						local (adr);
						for adr in adritem {
							addItem (adr)}};
					bundle { //spew out items in itemlist
						if defined (rules.columns.value) {
							local (cols = number (rules.columns.value));
							local (i, j, ix, itemsincol = (sizeof (itemlist) + (cols - 1)) / cols);
							for i = 1 to itemsincol {
								add ("<tr>"); indentlevel++;
								for j = 1 to rules.columns.value {
									ix = i + ((j - 1) * itemsincol);
									if ix <= sizeof (itemlist) {
										add (itemlist [ix] + "<td> </td>")}};
								add ("</tr>"); indentlevel--};
							ix = 1}
						else {
							local (item);
							for item in itemlist {
								add ("<tr>" + item + "</tr>")}}};
					if flOuterTables {
						add ("</table>"); indentlevel--}};
				local (suggestalink = "");
				bundle { //build the suggest-a-link part
					if flShowStatsLine {
						if flAtLeastOneLink { //heuristic, only ask for suggestions if the page has at least one link
							local (args = searchArgs);
							<<new (tabletype, @args)
							args.command = "suggestALink";
							local (suggestalinkurl = pta^.uri + "?" + webserver.encodeArgs (@args));
							suggestalink = string.replaceall (suggestALinkTemplate, macrostartchars + "url" + macroendchars, suggestalinkurl)}}};
				bundle { //flow the computed elements through the template
					if flDirectoryInABox {
						htmltext = directoryBodyText}
					else {
						local (s = template);
						s = string.replaceall (s, macrostartchars + "breadcrumbs" + macroendchars, crumbstring);
						s = string.replaceall (s, macrostartchars + "directory" + macroendchars, directoryBodyText);
						s = string.replaceall (s, macrostartchars + "editor" + macroendchars, ownername);
						s = string.replaceall (s, macrostartchars + "lastModDate" + macroendchars, modified);
						s = string.replaceall (s, macrostartchars + "hits" + macroendchars, "[[hits]]");
						s = string.replaceall (s, macrostartchars + "suggestLink" + macroendchars, suggestalink);
						s = string.replaceall (s, macrostartchars + "opmlUrl" + macroendchars, opmlUrl); //12/14/2000; 9:00:58 AM by DW
						s = string.replaceall (s, macrostartchars + "subItems" + macroendchars, ctsubitems); //3/6/01; 10:28:36 AM by DW
						htmltext = htmlText + s}}; //PBS 11/27/00: was htmltext = s -- which tossed the outer table tag
				if not flDirectoryInABox {
					flsaveincache = true}};
			"suggestALink" {
				local (defaultname = "", defaultemail = "");
				bundle { //get defaults
					if defined (pta^.adrmemberinfo) {
						defaultemail = nameof (pta^.adrmemberinfo^);
						try {defaultname = pta^.adrmemberinfo^.personalinfo.name}}};
				add (crumbstring);
				add ("<p class=\"suggestALinkExplanation\">Please enter a site name, URL and (optionally) explain why you believe the site should be included in this category.</p>");
				<<add ("<form method=\"POST\" action=\"" + pta^.uri + "\">"); indentlevel++
				add ("<form class=\"suggestALinkForm\" method=\"POST\">"); indentlevel++;
				add ("<table cellspacing=\"10\" cellpadding=\"0\">"); indentlevel++;
				add ("<tr><td>Site name:</td><td><input type=text size=\"50\" name=\"title\" value=\"\"></td></tr>");
				add ("<tr><td>URL:</td><td><input type=text size=\"50\" name=\"suggestedUrl\" value=\"http://\"></td></tr>");
				add ("<tr><td valign=\"top\" width=\"100\">Why should this link be in this category?</td><td><textarea rows=\"3\" cols=\"38\" wrap=\"virtual\" name=\"description\"></textarea></td></tr>");
				add ("<tr><td>Your name:</td><td><input type=text size=\"50\" name=\"name\" value=\"" + defaultname + "\"></td></tr>");
				add ("<tr><td>Your email address:</td><td><input type=text size=\"50\" name=\"email\" value=\"" + defaultemail + "\"></td></tr>");
				add ("<tr><td> </td><td><input type=\"submit\" name=\"submit\" value=\"Submit\"></td></tr>");
				add ("</table>"); indentlevel--;
				add ("<input name=\"command\" type=\"hidden\" value=\"" + "sendASuggestion" + "\">");
				<<add ("<input name=\"referer\" type=\"hidden\" value=\"" + referer + "\">")
				<<add ("<input name=\"editor\" type=\"hidden\" value=\"" + base64.encode (editoremail, infinity) + "\">")
				add ("</form>"); indentlevel--;
				add ("<p class=\"suggestALinkExplanation\">When you click on Submit, an email will be sent to " + ownername + ", the editor of this directory page. <i>There is no guarantee that the suggested site will appear in this directory.</i></p>")};
			"sendASuggestion" {
				local (mailtext = "");
				on add (s) {
					mailtext = mailtext + s + "\r\n"};
				add ("A link was suggested in this category:");
				add ("");
				add (thisPageUrl);
				add ("");
				add ("Details follow..");
				add ("");
				add (pta^.postargs.name + ", " + pta^.postargs.email + ", suggested adding the site named \"" + pta^.postargs.title + "\" at this URL:");
				add ("");
				add (pta^.postargs.suggestedUrl);
				add ("");
				add ("When asked why this link would be valuable, " + pta^.postargs.name + " said: \"" + pta^.postargs.description + "\".");
				add ("");
				add (clock.now ());
				if defined (manilaSuite.sendMail) {
					manilaSuite.sendMail (owneremail, "Suggested Link: " + pta^.postargs.title, mailtext, pta^.postargs.email)}
				else { //most likely running in Radio
					tcp.sendMail (owneremail, "Suggested Link: " + pta^.postargs.title, mailtext, pta^.postargs.email)};
				local (args = searchArgs); //start with the args we already have
				<<new (tabletype, @args)
				if defined (pta^.searchArgs) {
					webserver.parseArgs (pta^.searchArgs, @args)};
				args.command = "normal";
				args.message = "Your suggestion was sent to the editor of this page. Thank you for participating.";
				mainresponder.redirect (pta^.uri + "?" + webserver.encodeArgs (@args));
				return ("")}}};
	if flOuterTables {
		add ("</td></tr>"); indentlevel--; //PBS 11/27/00: close td and tr
		add ("</table>"); indentlevel--};
	if flUntaint { //untaint htmltext
		local (replaceWith = "&#" + number (macrostartchars[1]) + ";");
		if sizeOf (macrostartchars) > 1 {
			replaceWith = replaceWith + string.delete (macrostartchars, 1, 1)};
		htmltext = string.replaceAll (htmltext, macrostartchars, replaceWith)};
	if flsaveincache {
		adrcachepage^ = htmltext;
		adrexpires^ = clock.now () + (60 * 15)}; //the page rendering expires every 15 minutes
	return (postprocess (htmltext))};

<<bundle //test code
	<<local (t); new (tableType, @t); html.setPageTableAddress (@t)
	<<bundle //set up pagetable
		<<t.uri = "/foo/testDirectory/directoriesAtUserland"
		<<t.method = "GET"
	<<local (adroutline = @system.temp.windowTypes.windows[1].window)
	<<webBrowser.displayText (viewDirectory (adroutline, "http://localhost/foo/testDirectory", "directoriesAtUserland/radioUserland", title:"Test directory"))
	<<html.deletePageTableAddress ()



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.