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

system.verbs.builtins.op.outlineToXml

on outlineToXml (adrOutline, ownerName=user.prefs.name, ownerEmail=user.prefs.mailAddress, adrCloud=nil, version=nil, ownerId=nil) {
	<<Changes:
		<<3/18/06; 3:15:10 AM by DW
			<<Implement user.prefs.flGenerateOpml2.
				<<If it's true, and the caller has not provided a version string, set it to 2.0.
			<<Implement user.prefs.opmlOwnerId.
				<<If the OPML version number is 2.0 or greater, and the caller has not provided an ownerId, and user.prefs.opmlOwnerId is not empty, use it as the ownerId.
		<<3/6/06; 7:42:41 AM by DW
			<<Support for OPML 2.0.
			<<1. New optional param, version, if it's not nil, we replace the version that's generated by the kernel with the version that's supplied.
			<<2. New optional param, ownerId, if it's not nil, we replace ownerEmail with ownerId.
		<<6/12/05; 4:12:14 AM by DW
			<<Set appstring to "OPML Editor" if it's the OPML editor.
		<<1/28/03; 4:28:20 PM by JES
			<<Encode entities in the window title. Prevents formation of mal-formed XML when the outline window contains less-than or greater-than characters.
		<<5/24/02; 3:02:46 PM by JES
			<<Add an XML comment to the top of the file, saying what application generated the OPML. Cleaned up change notes.
		<<12/20/01; 8:50:05 AM by DB
			<<Fixed target setting and set more window attributes before calling the kernel
		<<12/19/01; 9:43:48 AM by DMB
			<<Added adrCloud parameter
		<<7/19/01; 1:34:43 PM by JES
			<<Preserve the window title, by patching the xml returned by the kernelCall.
		<<7/18/01; 8:33:08 PM by JES
			<<Copy the outline to a temporary outline contained in a local table object, instead of to @[windowTitle]^, to prevent the possibility of stomping on objects in the odb.
		<<2/18/01; 4:58:37 PM by PBS
			<<Preserve size, expansion state, scroll state, and window title.
		<<1/1/01; 12:58:02 PM by DW
			<<Preserve the dateCreated attribute of the OPML document. 
		<<1/1/01; 12:25:24 PM by DW
			<<In the 11/17 change the cure was worse than the disease. Now all outlines have a <title> of "localOutline". Not good. To work around this, I create a local outline with the same name as the original, then pass its address to the kernel.
		<<11/17/00; 4:18:33 PM by JES
			<<Convert a copy of the outline to opml instead of converting the outline itself, since if the outline is in text mode, the last-typed changes aren't added to the opml.
		<<09/17/00; 4:06:33 PM by PBS
			<<Kernelized. Also added ownerName and ownerEmail optional parameters.
		<<9/7/00; 2:53:03 PM by JES
			<<Set the target to oldTarget before returning xmltext.
		<<8/25/00; 11:13:16 AM by DW
			<<Version 1.0d2. Add <ownerName> and <ownerEmail> to the <head>.
		<<07/27/00; 3:24:37 PM by PBS
			<<Encode > characters as >, so the generated XML can be compiled later.
		<<7/26/00; 6:38:36 PM by DW
			<<Encode ampersands, quotes and less-thans in attributes.
		<<7/19/00; 6:30:02 PM by DW
			<<Created.
	if date.versionLessThan (Frontier.version (), "7.0b21") { //script version
		on encode (s) {
			s = string.replaceall (s, "&", "&");
			s = string.replaceall (s, "\"", """);
			s = string.replaceall (s, "<", "<");
			s = string.replaceall (s, ">", ">"); //PBS 07/27/00: encode > characters, so XML can be compiled later
			return (s)};
		local (scrollstate, expansionstate, windowtop, windowleft, windowheight, windowwidth, windowtitle);
		bundle { //set state variables based on the original window
			local (oldtarget = target.set (adroutline));
			scrollstate = op.getscrollstate ();
			expansionstate = op.getexpansionstate ();
			window.getSize (adroutline, @windowwidth, @windowheight);
			window.getPosition (adroutline, @windowleft, @windowtop);
			windowtitle = window.gettitle (adroutline)};
			<<target.set (oldtarget)
		local (xmltext = "", indentlevel = 0);
		on add (s) {
			xmltext = xmltext + string.filledstring ("\t", indentlevel) + s + "\r"};
		add ("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
		local (localoutline);
		localoutline = adroutline^;
		local (oldtarget = target.set (@localoutline));
		add ("<outlineDocument version=\"1.0d2\">"); indentlevel++;
		bundle { //add <head>
			add ("<head>"); indentlevel++;
			add ("<title>" + xml.entityEncode (windowtitle, true) + "</title>");
			add ("<dateCreated>" + date.netstandardstring (timecreated (adroutline)) + "</dateCreated>");
			add ("<dateModified>" + date.netstandardstring (timemodified (adroutline)) + "</dateModified>");
			add ("<ownerName>" + ownerName + "</ownerName>"); //PBS 09/17/00: ownerName is now an optional parameter to this verb
			add ("<ownerEmail>" + ownerEmail + "</ownerEmail>"); //PBS 09/17/00: ownerEmail is now an optional parameter to this verb
			bundle { //add expansion state
				local (expansionlist = string (expansionstate), num, s = "");
				for num in expansionlist {
					s = s + num + ","};
				s = string.delete (s, sizeof (s), 1); //delete last comma
				add ("<expansionState>" + s + "</expansionState>")};
			add ("<vertScrollState>" + scrollstate + "</vertScrollState>");
			bundle { //add <windowXxx> elements
				add ("<windowTop>" + windowtop + "</windowTop>");
				add ("<windowLeft>" + windowleft + "</windowLeft>");
				add ("<windowBottom>" + (windowtop + windowheight) + "</windowBottom>");
				add ("<windowRight>" + (windowleft + windowwidth) + "</windowRight>")};
			add ("</head>"); indentlevel--};
		bundle { //add <body>
			add ("<body>"); indentlevel++;
			op.fullexpand ();
			op.firstsummit ();
			on visitLevel () {
				local (s);
				loop {
					s = "<outline text=\"" + encode (op.getlinetext ()) + "\"";
					bundle { //add attributes from refcon, if there are any
						local (data = op.getrefcon ());
						if typeof (data) == binarytype { //has attributes
							local (attstable);
							unpack (@data, @attstable);
							for adr in @attstable {
								s = s + " " + nameof (adr^) + "=\"" + encode (adr^) + "\""}}}; //7/26/00 DW
					if script.isComment () {
						s = s + " isComment=\"true\""};
					if op.go (right, 1) {
						add (s + "\>"); indentlevel++;
						visitLevel ();
						add ("</outline>"); indentlevel--;
						op.go (left, 1)}
					else {
						add (s + "\/>")};
					if not op.go (down, 1) {
						break}}};
			visitLevel ();
			add ("</body>"); indentlevel--};
		add ("</outlineDocument>"); indentlevel--;
		target.set (oldTarget); // 09/07/00 JES
		return (xmltext)}
	else { //kernel version
		local (localoutline);
		local (adrtempoutline = @localoutline);
		adrtempoutline^ = adroutline^;
		local (oldTarget = target.set (adroutline));
		bundle { //PBS 02/18/01, dmb 12/20/01: duplicate window size, pos, title, expansion & scroll state
			local (windowTitle = window.getTitle (adroutline)); //use title of window, not name of outline object
			if windowTitle == "" { //if title == "", use name of object
				windowTitle = nameOf (adroutline^)};
			windowTitle = xml.entityEncode (windowTitle, true); //JES 1/28/03: prevent malformed XML
			local (horiz, vert, hpos, vpos);
			window.getSize (adroutline, @horiz, @vert);
			window.getPosition (adroutline, @hpos, @vpos);
			local (expansionState = op.getExpansionState ());
			local (scrollState = op.getScrollState ());
			target.set (adrtempoutline);
			window.setTitle (adrtempoutline, windowTitle);
			window.setSize (adrtempoutline, horiz, vert);
			window.setPosition (adrtempoutline, hpos, vpos);
			op.setExpansionState (expansionState);
			op.setScrollState (scrollState)};
		settimecreated (adrtempoutline, timecreated (adrOutline));
		
		local (xmltext);
		if date.versionLessThan (Frontier.version (), "7.1b43") { // no adrCloud
			on kernelCall (adrOutline, ownerName, ownerEmail) {
				kernel (op.outlineToXml)};
			xmltext = kernelCall (adrtempoutline, ownerName, ownerEmail)}
		else {
			on kernelCall (adrOutline, ownerName, ownerEmail, adrCloud) {
				kernel (op.outlineToXml)};
			xmltext = kernelCall (adrtempoutline, ownerName, ownerEmail, adrCloud)};
		try {target.set (oldTarget)};
		<<bundle //patch the <title> element
			<<local (ixstart = string.patternMatch ("<title>", xmltext) + 7)
			<<local (ixend = string.patternMatch ("</title>", xmltext))
			<<xmltext = string.delete (xmltext, ixstart, ixend - ixstart)
			<<xmltext = string.insert (windowTitle, xmltext, ixstart)
		bundle { //initialize and use user.prefs.flGenerateOpml2
			if not defined (user.prefs.flGenerateOpml2) {
				user.prefs.flGenerateOpml2 = false};
			if version == nil {
				if user.prefs.flGenerateOpml2 {
					version = "2.0"}}};
		bundle { //2.0, if version is non-nil, do a substitution
			if version != nil {
				local (i, sizex =sizeof (xmltext));
				local (ixstart, ixend);
				for i = 1 to sizex { //find the 1st return
					if xmltext [i] == '\r' {
						ixstart = i;
						break}};
				for i = ixstart+1 to sizex { //find the 2nd return
					if xmltext [i] == '\r' {
						ixend = i;
						break}};
				xmltext = string.delete (xmltext, ixstart+1, ixend-ixstart-1); //replace the <opml> element
				xmltext = string.insert ("<opml version=\"" + version + "\">", xmltext, ixstart+1)}};
		bundle { //initialize and use user.prefs.opmlOwnerId
			if not defined (user.prefs.opmlOwnerId) {
				user.prefs.opmlOwnerId = ""};
			if ownerId == nil {
				if user.prefs.opmlOwnerId != "" {
					if not date.versionLessThan (version, "2.0") {
						ownerId = user.prefs.opmlOwnerId}}}};
		bundle { //2.0, if ownerId is not nil, relace ownerEmail
			if ownerId != nil {
				local (searchfor = "<ownerEmail>" + ownerEmail + "</ownerEmail>");
				local (replacewith = "<ownerId>" + ownerId + "</ownerId>");
				xmltext = string.replace (xmltext, searchfor, replacewith)}};
		bundle { //add a comment saying what application generated the OPML
			<<Example comment:
				<<<!-- OPML generated by Radio UserLand v8.0.5 on Fri, 24 May 2002 20:43:14 GMT -->
			local (appstring);
			bundle { //set appstring
				if system.environment.isRadio {
					appstring = "Radio UserLand "}
				else { //Frontier or OPML Editor
					local (flopmleditor = false);
					if defined (system.environment.isOpmlEditor) {
						if system.environment.isOpmlEditor {
							flopmleditor = true}};
					if flopmleditor {
						appstring = "OPML Editor "}
					else {
						appstring = "UserLand Frontier "}};
				appstring = appstring + "v" + frontier.version ()};
			local (commenttext = "<!-- OPML generated by " + appstring + " on " + date.netStandardString (clock.now ()) + " -->\r\n");
			local (ix = string.patternMatch ("<opml ", xmltext));
			xmltext = string.insert (commenttext, xmltext, ix)};
		return (xmltext)}}
<<bundle //test code
	<<local (s =op.outlinetoxml (@scratchpad.testoutline, version:"2.0", ownerId:"http://blogs.opml.org/mail/dave"))
	<<wp.newtextobject (s, @scratchpad.testtext)
	<<edit (@scratchpad.testtext)



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.