Simon Fell > Its just code > January 2002

Saturday, January 19, 2002

Last week i said i want to tweak the date/time stamp on the weblog posts to include the currently playing track from WinAmp.
After shipping PocketSOAP 1.2.2 this morning I starting hacking Radio to do this.

Although I've used Manila and Frontier before, this is the first time I've a taken a real look at the scripting facilities of Radio/Frontier. As it took a while to find the right set of incantations, i thought i'd document them here for future Radio scripting newbies.

So, first up I looked through all the prefs, found the Item template, noticed that this includes generating the timestamp line, score !. I tweaked this, and did a new post, looks good, my changes appear, but the Item template is evaluated at rendering time, so any change to it is applied to all the items on the page, not just the new ones. So if you alter the item templete to directly show the current track, the same track will appear on each item, rather than the previous items remembering what was playing at that time.

Hmmm, we really need to store the trackname along with the item, rather than just trying to do it in the item template, so where are the items stored ?. I originally thought they were text files in the www drive, I've seen "Radio has a file based CMS" stated by a few folks, so the items are disk files right ? I trawled the www directory, and no sign, just the little templates that call <%radio.macros.viewWeblog ()%>. OK, open radio, look in Windows, ahh, there a weblogData.root sounds promising

Quickly i found under posts a list of items, each with a text and when field, bingo, here's our weblog items. That makes it easier, we can add our own data to each post, i expand the last post, goto the Table menu, New Scalar, string, and create a string field called tunz, and stick some text in the value.

Now, we need to go back to the item template, and modify it to try and find a tunz field for the post, and display it in the right place. First i looked in the help at the standard macros to see if any of they gave me the post object, no luck :(. Time to trawl the web, I found one of Dave's tutorials, Going crazy with macros, making a random deciphering of the script in there, it appears to walk the list of posts in the posts table, so sounds like a good start, lets grab that.

<%
local (s = "");
local (adrblog = radio.weblog.init ());
local (adrposts = @adrblog^.posts, i);
for i = sizeof (adrposts^) downto 1 {
   local (adrpost = @adrposts^ [i]);
   if (adrpost^.when >= monthStart) and (adrpost^.when <= monthEnd) {
      s = s + "<p>" + adrpost^.when + ": " + string (adrpost^.text) + "</p>"
      }
   };
return (s)
%>

adrposts looks like it refers to the weblogData.posts table, so we need to extract the current item from that table, rather than walking the whole list. Back to the list of macros, there a postNum macro that refers to the postNum this matches the number in the posts table, but how do we use the macro inside our script ?, lets try the obvious

<%
local (adrblog  = radio.weblog.init ());
local (adrposts = @adrblog^.posts, i);
local (adrpost  = @adrposts^.[postNum] ) ;
%>

Nope, no good, that gives an error, so does macros.postNum and radio.macros.postNum (guesses based on looking at the frontier docs)

In desperation more than anything else, i just nested the macro inside the existing script block

<%
local (adrblog = radio.weblog.init ()); 
local (adrposts = @adrblog^.posts); 
local (postNum = "<%paddedItemNum%>" ) ;
local (adrpost = @adrposts^.[postNum]) ; 
%>

I was shocked to find out that this actually works, but hey, progress is good :)

Now we just need to check to see if a tunz field exists and if it does, pull the value out to display, if it doesn't, show some default, looking a few other examples, i found the defined function, so we end up with

<%
local (adrblog = radio.weblog.init ()); 
local (adrposts = @adrblog^.posts); 
local (postNum = "<%paddedItemNum%>" ) ;
local (adrpost = @adrposts^.[postNum]) ; 
local (tunz = "[no music !]" ) ;
if defined ( adrpost^.tunz )
{
   tunz = adrpost^.tunz ;
};
return  tunz ;
%>

Cool, it works, i add tunz fields to a couple more posts in the table, then post a new weblog item to get the page re-rendered, and my entries appear !.

That's the display end taken care off, now we need to somehow detect that there's been a post and create the tunz field on that post item. More trusty Google work turns up another of Dave's docs, talking about callbacks for post/publish/delete, exactly what we're looking for.

No example in Dave's doc, so start digging, Dave says all the scripts in user.radio.callbacks.postItem get called when a user posts an item. Open Radio back up, radio.root, expand the tree user -> radio -> callbacks -> postItem. Just one item in there "Item #1" with a value (nil). Looks like a place holder, lets rename it and create our script. I rename it to grabTunz, and set the type to script. Then i put in the following script

on post ( adrPost ) 
	local ( np = dll.call("G:\\Source\\vc\\nowPlaying\\Debug\\nowPlaying.dll", "NowPlaying") );
	adrPost^.tunz = np ;

We'll come back to the dll.call thing later. Hit compile, no errors, back to the homepage, post a new item, hmmm, nothing. I look around some of the other scripts in radio.root and notice that their names in the table match the names in the script, perhaps they're suposed to be the same, open the script up, and change it to

on grabTunz ( adrPost ) 
	local ( np = dll.call("G:\\Source\\vc\\nowPlaying\\Debug\\nowPlaying.dll", "NowPlaying") );
	adrPost^.tunz = np ;

Hit compile again, no errors, back to the homepage, post a new item, wow ! bingo ! it works !, the timestamp line on the new post includes the currently playing winamp track. Success.!



The DLL

As I'm sure you've noticed the the last script magically obtains the current track from WinAmp. I conviently had some C# code from Jason Whittingtons MSNMangler [which updates your MSN IM Name with the current WinAmp track], my original plan was to try and re-use that by using the COM interop features in .NET. But Radio can only indirectly call COM objects, it can run VBScript, which can make COM calls, so i could of piped it up that way, but i also noticed that Radio can call DLL's directly, and that looked a bit cleaner. I whipped up the DLL in VC6, following the steps in the Writing Frontier DLLs article. In order to find the current track from WinAmp, the code simply walks all the top level windows, look at the window titles, and extracts the track name part from it, there's probably a way you can plug-in winamp directly to find it as well, but this works fine for now. You can grab a copy of the built DLL if you want to run this yourself.

Tuesday, January 15, 2002

The urban legend that you can't use a MSTK2.0 high level client with an Apache SOAP 2.2 server continues to circulate. Its just not true !

The trouble is, people building SOAP services with Apache SOAP stop once they have an Apache SOAP based clienting working, the Apache client will send type information in the SOAP payload, so the server can decode it without any further info. But this is not the only mode of operation, in addition, you can configure type mappings in the server that take effect when there is no type information directly in the payload. [such as with a WSDL based client like MSTK2.0]

What you need to do on the server end, is to add isd:map entries to the deploymentDescriptor, that map from elementNames to deserializer objects, and conviently, there's a samples of this in Apache SOAP 2.2, see \\samples\\interop\\deploymentDescriptor.xml, see that there's entries for each parameter used in the interopLab service, that details the deserializer to use.

So, if you have an Apache 2.2 server up and running with the samples deployed, you can try this out. For the MSTK2.0 client, you'll need a WSDL file that describes the service, fortuantly there's plenty of these kicking around, just grab one and change the soap:address to point to your server. Now run up a MSTK2.0 client, initialize it from the WSDL file, and off you go.

The Interoperability page in the Apache docs details this, and also includes details on setting up the equivilent client side mapping, if you've got an Apache client calling a MSTK2.0 server.

There's a zip file, with a suitable WSDL file, and VBScript/MSTK2.0 client here

Use tcpTrace or TcpTunnelGui to prove that there's no type information in the request.

Unfortuantly there's no WSDL generation tools for Apache SOAP 2.2, so you get to write the WSDL by hand, but its entirely doable, and it does work.