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.