WebSPL and WSF Tutorial ======================= This is a short tutorial to WebSPL and WSF. It is not a reference manual, so also have a look at the "wsf" module, the "cgi" module and related modules in the module references. If you have not yet set up your Web Server to handle webspl scripts, please have a look at the README file or the 'What is SPL?' chapter in this manual. WebSPL Introduction ------------------- WebSPL is an SPL runtime for CGI programming. There are two implementations of WebSPL: The CGI script "webspl.cgi", for integrating WebSPL with the Apache webserver using an 'action-handler', and "webspld", a stand-alone HTTP server. The SPL sources inlcude an example "httpd.conf" file for using "webspl.cgi" with the Apache webserver. The "webspld" program prints its own usage info when called with '--help'. The interesting thing about WebSPL is, that, other than usual CGI scripts, a WebSPL script is not executed once for each HTTP request. Instead, there is just one (virtual) SPL process for a session and the WebSPL script can pause it's execution at any time, wait for the user to do something and continue execution as soon as the webbrowser sends the next HTTP request for the session. This is a tutorial - so here is our first example program: /* -- webspltut/webspltut00.webspl -- */ Whenever task_pause() is called, the program execution stops and what has been written since the last task_pause() (or the beginning of the program execution) is displayed in the web browser. As soon as the user reacts (i.e. clicks on "next" in this example), the execution of the program is continued and task_pause() returns. The query string parameter 'sid' is special: It always contains the session id and must not be used for anything else in WebSPL applications. If the script is using SPL tasks, it is also possible to include the ID of the task which should be woken up in the 'sid' parameter. E.g. WSF is doing that. WSF Introduction ---------------- WSF (WebSPL Forms) is a library for doing web application development with WebSPL. WSF provides a widget based interface and so allows the development of web applications with a look&feel like normal GUI programs. But it is a bad choice for creating dynamic webpages since example given the browser back button must not be used in WSF applications. For this (and other) reasons it is recommended to open WSF applications in browser popup windows without browser toolbars. There are two base classes in WSF: WsfComponent and WsfDocument. The WsfDocument object handles a browser window. Usually there is only one instance of that object in a program, but if you want to use WSF to manage multiple windows, one WsfDocument instance per browser window is required. This WsfDocument instance is usually (i.e. per convention, there is no technical reason for doing so) assigned to a global variable named "page". The content of a browser window is organised as a tree of WsfComponent objects. The root component must be assigned to the "root" member variable of the "page" object. The WsfComponent object itself has a member variable called "children" which is an array/hash of the child components. In most cases objects derived from WsfComponent are used instead of WsfComponent directly. One of these derived objects is WsfDisplay from the "wsf_display" module (WsfDocument and WsfComponent are declared in the "wsf" module). The HTML text which should be displayed in a WsfDisplay object is simply passed to the constructor. So here is a very simple WSF "Hello World" application: /* -- webspltut/webspltut01.webspl -- */ The page.main() does never return. The default behavior of the WsfComponent object is to simply print the html content of all its children in its get_html() method. So it is pretty useful as a simple container for other more complex objects derived from WsfComponent: /* -- webspltut/webspltut02.webspl -- */ But this application doesn't do much. So, lets create our own object derived from WsfComponent which implements a simple counter: /* -- webspltut/webspltut03.webspl -- */ So, when creating your own WSF Components, you need to overload the get_html() method with something which returns the HTML code for your component and overload the main() method with the main program for the task managing this component. The task executing the main() method is created automatically by the object constructor (and killed by the destroy() method). The function task_co_return() must be called by main() to wait for user events. The variable 'dirty' must be set to 1 when anything happend with an effect on the get_html() output. The root element of the HTML code generated by get_html() must have its 'id' attribute set to the value of the 'id' variable. There are methods (such as the add_href() used in the example) for creating links, formulas, etc. Have a look at the module reference for the "wsf" module for a full overview of the WsfDocument and the WsfComponent objects. Adding Menus ~~~~~~~~~~~~ Applications always have some type of menu. There is a generic WSF component called WsfMenu (from the "wsf_menu" module) which implements nice menus. The menu entries are function pointers which are called when the entry has been clicked: /* -- webspltut/webspltut04.webspl -- */ The HTML code generated by WsfMenu should be the first real code in the webpage. So the example is using a simple WsfComponent as root component and WsfMenu as its first child (0). The 2nd child (1) is set to WsfDisplay objects by the menu callbacks to display messages. Example Application: A very simple CMS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now lets write a real application: a very simple CMS. Don't expect too much: It is not much more than just a web-based file manager for HTML files. Do not wonder: the login for the login screen is covered in the next section. /* -- webspltut/webspltut05.webspl -- */ Please run the application before you continue reading this tutorial (just to make sure you know what this is all about). The application defines two Objects: Editpage for dialog which lets the user edit a page and Newpage for the dialog which lets the user create new pages. The application also defines the function create_menu() for (re-)creating the menu. The main program just creates the page object, the root component, calls create_menu() and enters page.main(). The create_menu() function .......................... This function creates the menu as child component of the root component. The "Pages" menu is dynamically generated from the list of *.html files in the current directory. Whenever files are added or removed, create_menu() must be called in order to update this menu. A closure is used to store the filename to be opened together with the callback function which is called when an entry in the "Pages" menu is clicked. The Editpage Object ................... Editpage is directly dervied from WsfComponent. The filename of the file to be edited is passed as an argument to the constructor and copied to the variable 'filename' there. The methods get_html() and main() are overloaded. The get_html() method simply displays an HTML form for editing the page. The page content is loaded from the HTML file. Note how the 'xml::' and 'js::' encoding functions are used to ensure the correctness of the generated HTML code. The main() method simply writes back the data to the file, or removes the file if the user clicked on the "Remove" button. Since main() is automatically called in an endless-loop, I did not add the "while (1) { ... }" loop here explicitly. When main() is removing the file, the menu is updated by calling create_menu() and the Editpage component is replacing itself with a simple WsfComponent instance. The Newpage Object .................. The Newpage opject (also directly dervied from WsfComponent) is even simpler. get_html() just displays an HTML form again. When the form is submitted and main() returns from task_co_return(), it simply creates the new page (after cleaning up the filename), calls create_menu() and replaces itself with an Editpage instance for the newly created file. Note that a Here-Document with indenting character is used when creating the HTML text for the new page. This avoids ugly leading blanks in the HTML file while keeping the indenting in the program intact. Login screen and opening a popup window ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The last example is using a pretty nice mechanism for doing authentication and opening a popup window. Before creating the 'page' object and passing control to WSF, this code is executed: /* -- webspltut/webspltut06.spl -- */ There is nothing wrong with already accepting parameters at program startup. The program expects username and password in the query string parameters "user" and "pass". If they are not present or wrong, the login screen will be printed and the program exits. Only if correct authentication tokens are passed, the program continues execution and initializes the WSF framework. It has shown that this approach is very useful and much easier than actually including this step in the application itself. One of the benefits of this approach is that a developer can pass the authentication tokens already in the query string and so bypass the additional screen. That's very comfortable for testing the application during the development. Some applications even allow 'deep-linking' to some dialogs this way to make testing easier. This approach also allows to open the application in a popup window easily. Opening a popup window makes sense since we do not want the user to click on the browser 'back' button or bookmark any deep links of the application. It doesn't really prevent the user from doing such stuff - but it makes it hard enough to avoid most of the troubles. Opening the popup window is simply done by printing the javascript code and then calling task_pause(). As soon as the the browser tries to load the content of the popup window, the session is resumed and task_pause() returns. W2T Introduction ---------------- The W2T (Web 2.0 Toolkit) module provides another framework for web application development. W2T applications only send an initial XML document (usually XHTML or SVG) to the browser and everything after is done using AJAX XML RPC calls. The W2T module is basically a collection of SPL and JavaScript functions which provide functionality for updating the XML DOM tree displayed in the browser window from server-side SPL and call server-side SPL event callbacks from browser-side JavaScript code. Here is a small W2T example program which is using SVG. Note that at the time of this writing Firefox 1.5 is the only browser which can be used as frontend for W2T SVG programs: /* -- webspl_demo/w2tdemo_svg.webspl -- */ The W2T module also includes some JavaScript helper functions for doing animations and other funny stuff. W2T is under development and a more detailed documentation will be provided when it is finished.