Monday, April 04, 2011 at 1:08 AM.

radio2Suite.main

on main (flIncludeHistory=true, flBoxVisible=true) {
	<<Changes
		<<3/23/11; 9:01:12 PM by DW
			<<If there is a message, the editbox is visible. If it's a POST, the editbox is visible.
		<<3/22/11; 11:43:20 AM by DW
			<<New optional param, flBoxVisible, default true. Determines if the whole array of edit controls is initially visible. 
		<<3/21/11; 8:51:54 AM by DW
			<<Optional param, flIncludeHistory, default true.
		<<3/17/11; 1:59:39 AM by DW
			<<Fixed bug where if you added a link after editing, the link wouldn't get shortened and the link wouldn't appear in the feed.
		<<3/16/11; 8:58:11 AM by DW
			<<For new posts that don't already have shortened urls, after creating a short url, also set the guid to that short url. Otherwise there was an inconsistency, these posts would have long-url guids. There's no actual reason this wouldn't work, it's just sloppy. 
		<<3/13/11; 1:54:12 AM by DW
			<<Fixed breakage -- if a post doesn't have a thumb, it tried to upload one anyway, with bad results. :-*
		<<3/13/11; 1:35:18 AM by DW
			<<Open architecture for new UI elements. http://changes.scripting.com/#p5506 
		<<3/13/11; 12:27:33 AM by DW
			<<Set idHistoryHIghlight when saving changes to a post after editing, so it stays highlighted. It is still being edited after it's saved.
		<<3/12/11; 11:06:15 PM by DW
			<<New local, idHistoryHIghlight. It's non-zero if the user clicks on an Edit icon to edit a post -- then it contains the ID of the post being edited. 
		<<3/11/11; 8:37:16 PM by DW
			<<The title/link boxes section is initially visible if the title is not empty. There was other code here and in the template related to hiding and showing but this is all we are using, so I simplified the code in both places.
		<<3/11/11; 12:55:06 PM by DW
			<<Lots of changes rolling up to v0.5.
		<<3/9/11; 8:59:34 AM by DW
			<<Implement the prompt and the podcast button option.
		<<3/8/11; 11:26:24 AM by DW
			<<Change the title of the home page from the domain to the word "Home".
		<<3/7/11; 10:06:47 AM by DW
			<<Update call to radio2Suite.securityChallenge to new conventions. 
		<<3/6/11; 5:31:34 PM by DW
			<<Call a routine to determine if shortening is enabled, this way we can use the user's settings or the global ones. 
		<<3/6/11; 12:34:36 PM by DW
			<<Factored code that generates history.
		<<3/2/11; 8:13:14 AM by DW
			<<The default value for enclosure is "" not nil.
		<<3/1/11; 11:14:55 PM by DW
			<<Allocate the enclosure before creating the post. 
		<<3/1/11; 1:51:32 PM by DW
			<<Fix bug detecting posts without enclosures.
		<<3/1/11; 12:20:58 PM by DW
			<<Enclosures.
		<<2/25/11; 12:11:46 PM by DW
			<<When you select an item from the popup menu, the history redisplays to show the contents of that feed.
		<<2/24/11; 9:01:20 PM by DW
			<<Use the xmlViewer app to view the RSS feed.
				<<http://scripting.com/stories/2011/02/25/viewingRssInABrowser.html
		<<2/24/11; 1:47:48 PM by DW
			<<Disable code to remove text after hyphen in title. Mistake. Also no need to refresh counts from this code, it's now done from the everyMinute script.
		<<2/23/11; 10:24:43 PM by DW
			<<Remove everything to the right of the leftmost pipe character and other title cleanups.
				<<http://changes.scripting.com/#p5073
		<<2/23/11; 11:34:16 AM by DW
			<<Add "view-source:" to the XML url so you can skip right to looking at the source. Finally. Makes everything work so much better. Makes it easier to show people new stuff in the XML.
		<<2/22/11; 11:49:27 AM by DW
			<<Add column to the home page showing the short code for the story. Now shortens URLs even if you enter them manually, without the bookmarklet (though I can't imagine why one would do that). Other small fixes. 
		<<2/22/11; 10:45:51 AM by DW
			<<Uses the titleNoise table to clean up titles.
		<<2/21/11; 8:48:22 PM by DW
			<<Get the counts in a separate thread so it doesn't delay the page loading.
		<<2/21/11; 7:49:15 PM by DW
			<<Run the titles through the titleNoise table.
		<<2/21/11; 7:06:21 PM by DW
			<<Call radio2Suite.shortener.getCounts in a try so it doesn't bring down the home page when there's an error.
		<<2/21/11; 11:38:24 AM by DW
			<<Remove the UTM_ garbage from URLs as they come in.
		<<2/20/11; 11:26:20 AM by DW
			<<Pass the address of the user to radio2suite.shortener.create.
		<<2/18/11; 9:45:50 PM by DW
			<<Allow manual entering of URLs in the link box (it had broken in previous release).
		<<1/19/11; 11:43:02 AM by DW
			<<If description comes in empty on a GET, this means that nothing was selected when the user clicked on the bookmarklet. Move the title into the description and empty out the title. It's a Twitter-style entry.
		<<1/19/11; 11:16:44 AM by DW
			<<Account for empty titles, links and descriptions when displaying history.
		<<1/13/11; 6:51:21 AM by DW
			<<Subtle change -- the feed is determined by the value of args.feedMenu -- the value of the popup menu. This way the user can select a different feed, then post the item, skipping the step of hitting the Go button. It's so intuitive taht I thought it was a bug for a while when items would get posted to the "wrong" feed. 
		<<1/7/11; 10:23:58 AM by DW
			<<No more username parameter. We get the user's name from basic authentication.
		<<1/5/11; 7:44:56 AM by DW
			<<If no username is supplied, use the first one.
		<<1/4/11; 11:54:37 AM by DW
			<<Get the args first so we can know the username and do the security challenge before doing the logic. I bit of code reorg is needed. Not much. :-)
		<<12/14/10; 11:15:37 AM by DW
			<<Created. 
			<<javascript:location.href='http://my.reallysimple.org/?link='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)+'&description='+encodeURIComponent(document.getSelection())
	local (pta = html.getpagetableaddress (), adrdata = radio2suite.init (), t, historytext = "", adrpost = nil, adrfeed, args, adruser, username);
	local (imgXmlMini, imgEdit, imgDelete, imgTwitterFeed, imgTwitter, now = clock.now (), flPromptEnabled = true, flTitleLinkBoxesVisible = true, idHistoryHIghlight = 0);
	pta^.title = "Home"; //3/8/11 by DW
	scratchpad.radio2params = pta^; //debugging
	bundle { //set images html
		imgXmlMini = string (radio2Suite.data.html.xml);
		imgEdit = string (radio2Suite.data.html.editIcon);
		imgDelete = string (radio2Suite.data.html.deleteIcon);
		imgTwitterFeed = string (radio2Suite.data.html.twitterFeedIcon);
		imgTwitter = string (radio2Suite.data.html.twitterIcon)};
	if not radio2Suite.securityChallenge (@adruser) {
		return ("")};
	bundle { //set args, based on method, 1/4/11 by DW
		new (tabletype, @args);
		case pta^.method {
			"GET" {
				webserver.parseargs (pta^.searchargs, @args);
				if defined (args.title) { //we came in through the bookmarklet
					if adruser^.prefs.flTitleCleanup {
						args.title = string.trimwhitespace (args.title); //1/19/11 by DW
						args.title = string.multiplereplaceall (args.title, @radio2Suite.data.titleNoise); //2/21/11 by DW
						if args.title contains "|" { //2/23/11 by DW
							args.title = string.nthfield (args.title, "|", 1)};
						<<if args.title contains "-" //2/23/11 by DW
							<<args.title = string.nthfield (args.title, "-", 1)
						args.title = string.addPeriodToSentence (args.title)}; //2/23/11 by DW
					args.description = string.trimwhitespace (args.description);
					if sizeof (args.description) == 0 {
						args.description = args.title;
						args.title = ""}}};
					<<if sizeof (args.title) > 0 //2/9/11 by DW
						<<fltitlefieldhidden = false
			"POST" {
				args = pta^.postargs; //3/1/11 by DW
				<<webserver.parseargs (pta^.requestbody, @args)
				
				if args.storyUrl == "" { //2/18/11 by DW
					if args.storyUrlShortened != "" {
						args.storyUrl = args.storyUrlShortened;
						args.storyUrlShortened = ""}}}}
				<<scratchpad.radio2args = args; edit (@scratchpad.radio2args) //debugging
		else {
			scripterror ("Can't process the request because the method is not GET or POST.")}};
		<<if defined (args.username)
			<<username = args.username
			<<adruser = @adrdata^.users.[username]
		<<else
			<<adruser = @adrdata^.users [1] //1/5/11 by DW -- if no username is supplied, use the first one
			<<username = nameof (adruser^)
	bundle { //set adrfeed to default value
		on setadrfeed () {
			try {
				if defined (args.feedMenu) {
					adrfeed = @adruser^.feeds.[args.feedMenu]; //1/13/11 by DW
					if defined (adrfeed^) {
						adruser^.stats.lastFeedSelected = args.feedMenu;
						return}};
				adrfeed = @adruser^.feeds.[adruser^.stats.lastFeedSelected];
				if defined (adrfeed^) {
					return}};
			adrfeed = @adruser^.feeds [1]};
		if sizeof (adruser^.feeds) == 0 {
			scripterror ("Can't edit a feed because none have been defined.")};
		setadrfeed ()};
	on decode (s) {
		return (string.multiplereplaceall (s, @xml.rss.data.replaceTable, false))};
	on encode (s) {
		return (string.urlencode (s, true))};
	on cleanupfilename (fname) {
		local (i, ch);
		for i = sizeof (fname) downto 1 {
			ch = fname [i];
			if (not string.isalpha (ch)) and (not string.isnumeric (ch)) and (ch != ' ') and (ch != '.') {
				fname = string.delete (fname, i, 1)}};
		fname = string.innerCaseName (fname);
		return (fname);
		};
	<<if not radio2Suite.securityChallenge (adruser)
		<<return ("<p>A valid username and password are required to enter this site.</p>")
	local (htmltext = "", indentlevel = 0);
	on add (s) {
		htmltext = htmltext + string.filledstring ("\t", indentlevel) + s + "\r"};
	bundle { //init t
		new (tabletype, @t);
		t.storyTitle = "";
		t.storyUrl = "";
		t.storyUrlShortened = ""; //2/18/11 by DW
		t.storyDescription = "";
		t.idPost = "";
		t.buttonTitle = "Post";
		t.username = username;
		bundle { //t.message
			if defined (args.message) {
				t.message = args.message}
			else {
				t.message = ""}};
		<<bundle //title field stuff
			<<if fltitlefieldhidden
				<<t.titleFieldType = "hidden"
				<<t.titleLabelClass = "hide"
			<<else
				<<t.titleFieldType = "text"
				<<t.titleLabelClass = "show"
		bundle { //t.newIcon, 3/11/11 by DW
			t.newIcon = "<a href=\"index\">" + string (radio2Suite.data.html.newIcon) + "</a>"}};
	bundle { //handle post
		if pta^.method == "POST" {
			local (adr, i);
			if defined (args.tweetButton) { //12/29/09 by DW
				local (adrpost, urlEnclosure = "", extradata);
				new (tabletype, @extradata);
				if defined (args.storyEnclosure) { //3/1/11 by DW
					if (sizeof (args.storyEnclosure.filename) > 0) and (sizeof (args.storyEnclosure.data) > 0) {
						local (fname = cleanupfilename (args.storyEnclosure.filename));
						<<bundle //clean up fname
							<<local (i, ch)
							<<for i = sizeof (fname) downto 1
								<<ch = fname [i]
								<<if (not string.isalpha (ch)) and (not string.isnumeric (ch)) and (ch != ' ') and (ch != '.')
									<<fname = string.delete (fname, i, 1)
							<<fname = string.innerCaseName (fname)
						local (relpath = file.getdatepath ("/", now) + fname);
						urlEnclosure = radio2Suite.writeStaticFile (adruser, relpath, args.storyEnclosure.data, args.storyEnclosure.["Content-Type"])}};
						<<adrpost^.enclosure = radio2Suite.writeStaticFile (adruser, relpath, args.storyEnclosure.data, args.storyEnclosure.["Content-Type"])
				bundle { //process extra items, 3/13/11 by DW
					local (adrarg);
					for adrarg in @args {
						if string.lower (nameof (adrarg^)) beginswith "extra." {
							local (name = string.delete (nameof (adrarg^), 1, 6));
							if typeof (adrarg^) == tabletype { //it's a file to be uploaded
								if sizeof (adrarg^.data) > 0 {
									local (fname = cleanupfilename (adrarg^.filename));
									local (relpath = file.getdatepath ("/", now) + fname);
									extradata.[name] = radio2Suite.writeStaticFile (adruser, relpath, adrarg^.data, adrarg^.["Content-Type"])}}
							else {
								extradata.[name] = string (adrarg^)}}}};
				if radio2Suite.findPost (adrfeed, args.idPost, @adrpost) {
					radio2Suite.editPost (adrpost, args.storyTitle, args.storyUrl, args.storyDescription, urlEnclosure, @extradata);
					bundle { //this happens if the user adds a link after editing, 3/17/11 by DW
						if (sizeof (adrpost^.link) > 0) and (sizeof (adrpost^.linkShortened) == 0) {
							if radio2Suite.shortener.enabled (adruser) {
								adrpost^.linkShortened = radio2suite.shortener.create (adrpost^.link, adruser)}}};
					t.storyTitle = args.storyTitle;
					t.storyUrl = args.storyUrl;
					t.storyDescription = args.storyDescription;
					t.idPost = args.idPost;
					t.buttonTitle = "Save";
					idHistoryHIghlight = args.idPost} //3/13/11 by DW
				else { //2/22/11 by DW -- rewrite
					adrpost = radio2Suite.newPost (adrfeed, args.storyTitle, args.storyUrl, args.storyUrlShortened, args.storyDescription, urlEnclosure, @extradata);
					if not defined (adrpost^.linkShortened) {
						if radio2Suite.shortener.enabled (adruser) {
							adrpost^.linkShortened = radio2suite.shortener.create (adrpost^.link, adruser);
							adrpost^.guid = adrpost^.linkShortened}}}; //3/16/11 by DW
				if adruser^.prefs.flPublishEnabled {
					radio2Suite.publish (adruser, adrfeed)}};
					<<thread.callscript (@radio2Suite.publish, {adruser, adrfeed})
			if defined (args.goButton) { //12/25/10 by DW
				local (adrnewfeed = @adruser^.feeds.[args.feedMenu]);
				if adrnewfeed != adrfeed {
					<<webserver.redirect (pta, "?feed=" + args.feedMenu)
					webserver.redirect (pta, "?feed=" + args.feedMenu + "&title=" + encode (args.storyTitle) + "&link=" + encode (args.storyUrl) + "&description=" + encode (args.storyDescription));
					return}}}};
	bundle { //get args if it's GET
		if pta^.method == "GET" {
			local (adr, i);
			<<scratchpad.args = args; edit (@scratchpad.args)
			if defined (args.feed) {
				adrfeed = @adruser^.feeds.[args.feed];
				if not defined (adrfeed^) {
					scripterror ("There is no feed with the name \"" + args.feed + "\".")};
				adruser^.stats.lastFeedSelected = args.feed};
			if defined (args.idPost) { //we're editing an existing post
				if radio2Suite.findPost (adrfeed, args.idPost, @adrpost) {
					t.storyTitle = adrpost^.title;
					t.storyUrl = adrpost^.link;
					bundle { //shortened urls, 2/18/11 by DW
						if defined (adrpost^.linkShortened) {
							t.storyUrlShortened = adrpost^.linkShortened}
						else {
							if radio2Suite.shortener.enabled (adruser) {
								t.storyUrlShortened = radio2suite.shortener.create (t.storyUrl, adruser);
								adrpost^.linkShortened = t.storyUrlShortened}}};
					t.storyDescription = adrpost^.description;
					t.idPost = args.idPost;
					t.buttonTitle = "Save";
					idHistoryHIghlight = args.idPost}} //3/12/11 by DW
			else {
				if defined (args.title) {
					t.storyTitle = decode (args.title)};
				if defined (args.link) {
					t.storyUrl = decode (args.link);
					t.storyUrl = string.removeUrlGarbage (t.storyUrl);
					if radio2Suite.shortener.enabled (adruser) {
						t.storyUrlShortened = radio2suite.shortener.create (t.storyUrl, adruser)}};
				if defined (args.description) {
					t.storyDescription = decode (args.description)}}}};
	bundle { //build history table
		if flIncludeHistory { //3/21/11 by DW
			t.historyExpansionState = ""}
		else {
			t.historyExpansionState = "none"};
		historytext = radio2Suite.viewHistory (adruser, adrfeed, idHistoryHIghlight)};
	<<bundle //experiment -- view news here -- 3/5/11 by DW
		<<t.river = radio2suite.viewRiver (adruser)
	bundle { //build feeds menu
		if sizeof (adruser^.feeds) > 1 {
			local (js  = string (radio2Suite.data.feedMenuScript)); //2/25/11 by DW
			local (s = "<select name=\"feedMenu\"" + js + ">", adr, selected);
			for adr in @adruser^.feeds {
				if adr == adrfeed {
					selected = " selected "}
				else {
					selected = ""};
				s = s + "<option value=\"" + nameof (adr^) + "\" " + selected + ">" + adr^.prefs.title + "</option>"};
			t.feedsMenu = s + "</select>"}
		else {
			if sizeof (adruser^.feeds) == 1 {
				local (name = nameof (adruser^.feeds [1]));
				t.feedsMenu = "<input type=\"hidden\" name=\"feedMenu\" value=\"" + name + "\" />";
				if flPromptEnabled {
					local (s = adruser^.prefs.homePagePrompt);
					if sizeof (s) == 0 {
						s = string (radio2Suite.data.strings.homePagePrompt)};
					t.feedsMenu = t.feedsMenu + "<span class=\"leadingQuestion\">" + s + "</span>"}}
			else {
				t.feedsMenu = ""}}};
	bundle { //podcast button, 3/9/11 by DW
		if true { //adruser^.prefs.flPodcastButtonOnHomePage -- wired on, 3/11/11 by DW
			t.podcastButton = "<input type=\"file\" name=\"storyEnclosure\">"}
		else {
			t.podcastButton = ""}};
	bundle { //extra elements, via callbacks, 3/13/11 by DW
		local (adrscript, elementname, elementhtml);
		t.extraElements = "";
		t.titleLinkEnclosurePrompt = "Title, link, enclosure";
		for adrscript in @adrdata^.callbacks.extraHtml {
			while typeof (adrscript^) == addresstype {
				adrscript = adrscript^};
			try {
				if adrscript^ (@elementname, @elementhtml) {
					t.extraElements = t.extraElements + "<tr><td align=\"right\">" +  elementname + ":</td><td>" + elementhtml + "</td></tr> ";
					t.titleLinkEnclosurePrompt = t.titleLinkEnclosurePrompt + ", " + string.lower (elementname)}}};
		t.titleLinkEnclosurePrompt = t.titleLinkEnclosurePrompt + "."};
	bundle { //add the tweetbox
		local (s);
		t.lastTweetByThisUser = "";
		t.initialText = "";
		t.ctChars = sizeof (t.storyDescription);
		t.prompt = "Post to \"" + adrfeed^.prefs.title + ":\"";
		bundle { //xmlicon
			t.xmlIcon = "";
			t.twitterFeedIcon = "";
			if defined (adrfeed^.stats.feedUrl) {
				if sizeof (adrfeed^.stats.feedUrl) > 0 {
					local (url = radio2Suite.data.strings.xmlviewerUrl + adrfeed^.stats.feedUrl);
					t.xmlIcon = "<a href=\"" + url + "\">" + imgXmlMini + "</a>";
					<<url = "http://twitterfeed.com/ping?url=" + string.urlencode (url)
					url = "pingTwitterFeed?url=" +  string.urlencode (url);
					
					local (myurl = "http://" + pta^.host + pta^.path); //3/28/11 by DW
					url = "http://" + adrdata^.prefs.domainRadioReallySimpleApp + "/dancewithtwitter?feed=" +  string.urlencode (adrfeed^.stats.feedUrl) + "&returnto=" + string.urlencode (myurl);
					local (confirmation = "Would you like to connect this feed to your Twitter account?");
					t.twitterFeedIcon = "<a href=\"" + url + "\" onclick=\"return confirm('" + confirmation + "')\">" + imgTwitter + "</a>"}}};
		bundle { //t.linkIcon, 3/11/11 by DW
			if sizeof (t.storyUrl) > 0 {
				t.linkIcon = radio2Suite.utilities.getFavIconHtml (t.storyUrl)}
			else {
				t.linkIcon = ""}};
		bundle { //t.podcastIcon, 3/11/11 by DW
			t.podcastIcon = "";
			if adrpost != nil {
				if defined (adrpost^.enclosure) {
					if sizeof (adrpost^.enclosure) > 0 {
						t.podcastIcon = "<a href=\"" + adrpost^.enclosure + "\">" + string (radio2Suite.data.html.podcastIcon) + "</a>"}}}};
		bundle { //determine if the title/link boxes are visible initially, 3/11/11 by DW
			if sizeof (t.storytitle) > 0 { //title/link boxes are visible initially, so the user can see the title
				t.titleLinkExpansionState = ""}
			else {
				t.titleLinkExpansionState = "none"}};
		t.historyText = historytext; //12/24/10 by DW
		if t.storyUrlShortened == "" { //2/18/11 by DW
			t.storyUrlShortened = t.storyUrl};
		bundle { //t.editBoxExpansionState, 3/22/11 by DW
			if flBoxVisible or (sizeof (t.storyDescription) > 0) or (sizeof (t.message) > 0) { //or (pta^.method == "POST")
				t.editBoxExpansionState = ""}
			else {
				t.editBoxExpansionState = "none"}};
		s = string.multiplereplaceall (string (radio2Suite.data.tweetBox), @t, false, "<%", "%>");
		add (s)};
	return (htmltext)};
bundle { //test code
	html.setpagetableaddress (@scratchpad.r2params);
	main ()}



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.