View this PageEdit this PageAttachments to this PageHistory of this PageHomeRecent ChangesSearch the SwikiHelp Guide
Hotspots: Admin Pages | Turn-in Site |
Current Links: Cases Final Project Summer 2007

Advanced SSP Tactics

Developing web applications and web user interfaces for SmallTalk applications with SmallTalk Server Pages is among the easiest processes of your development cycle. Working with SSP files is very similar to working with other web languages, such as PHP. Code can be organized with include files, parameters can be sent across pages, sessions can be created and more. For M9, I was able to create our entire web UI using one main file and splitting up tasks into pages that were included files displayed based on parameters.

Query String Parameters
You will quickly find that parameters with SmallTalk Server Pages files are easy to implement. For example, lets say that you had a very long index.ssp file with sections of code for adding nodes, adding edges and then running algorithms that you wanted to splitup and only have active when a user clicks a link in the sidebar corresponding to that code section.

First off we should create include files (.inc) for those code sections. For clarity, we will call them "modes" - will contain the code for manipulating nodes, for edges and for algorithms.

Include files can contain SmallTalk code typically found in SSP files as well as XHTML markup.

Here is some example code:
<% mode := request anyParameterValueAt: 'mode'.
(mode = '1') ifTrue: [ %>
<!-- #include file= "" --> <!-- include file containing node code -->
<% ].

(mode = '2') ifTrue: [ %>
<!-- #include file= "" --> <!-- include file containing edges code -->
<% ].

(mode = '3') ifTrue: [ %>
<!-- #include file= "" --> <!-- include file containing alg code -->
<% ].%>

I am setting mode equal to "request anyParameterValueAt: 'mode' ". This grabs the value for the query string parameters in the URL. If this code was saved in my index.ssp file and the URL in the user's browser ended with index.ssp?mode=2, then the value for mode would be 2. Then, basic ifTrue logic checks to see what if the mode matches the mode being tested and if so, loads the include file with the code for that section.

File includes must be done outside of the <% %> call - the same way you would generally carry out XHTML code. Also, if you want to put comments in that area, you can use the
<!-- -->

Similarly you can use query string parameters for error handling. For example, if someone has an incorrect login, you can send them back to the login page, but with an error parameter that shows a message.

error := request anyParameterValueAt: 'error'.
(error = '1') ifTrue: [ 
response write: '<strong style="color:#ff0000">Incorrect Login, Try Again</strong><br/><br/>'.
]. %>

This will display "Incorrect Login, Try Again" in red if the user is on the login.ssp?error=1 page.

Here is how you can handle sending a user to a page like that:
response redirectTo: ('yourfile.ssp?error=1').

Instead of using response write, we are making use of redirectTo which acts like header location in PHP, simply shuttling users off to the appropriate page once processed.

Menu Manipulation with Cookies
If you look around at websites you visit everyday like Facebook or digg, you will notice that the menu available to you varies depending on whether you are logged in or out. This is obviously a necessity as logged out users shouldn't have access to their profile page, etcetera. Assuming you set a cookie similar to below when the user logs in,

userCookie := HTTPCookie named: 'user' value: username,',',password. 
userCookie expireAfterDays: 3. 
response addCookie: userCookie.
"username and password are variables set elsewhere above"
"note: don't explicitly use this code as additional code is necessary to break up the concatenated username and password, this is just an example"

You can check their cookie in the menu to show differing links depending on their logged in status.

userCookie := request cookieValueAt: 'user'. 

This is the basic line of SmallTalk for checking the cookie's value. Then we can check to see if that cookie's size contains anything with

(userCookie size) = 0 ifFalse:[ ].

Inside of the ifFalse, which only executes if there is something in the cookie, meaning the user is logged in, we can response write the appropriate links.

Here is what a final menu might look like.

<div id="sidebar">
userCookie := request cookieValueAt: 'user'.
(userCookie size) = 0 ifTrue: [
	response write: '<li><a href=index.ssp>Home</a></li>'.
	response write: '<li><a href=about.html>About</a></li>'.
	response write: '<li><a href=login.ssp>Login</a></li>'.
(userCookie size) = 0 ifFalse:[ 
	response write: '<li><a href=index.ssp>Home</a></li>'.
	response write: '<li><a href=index.ssp?mode=1>Add Nodes</a></li>
	response write: '<li><a href=index.ssp?mode=2>Add Edges</a></li>
	response write: '<li><a href=index.ssp?mode=3>Run Algorithms</a></li>
	response write: '<li><a href=logout.ssp>Logout</a></li>'.

The menu is wrapped in a sidebar div and HTML ordered list elements ul and li so that it can easily be styled with some appropriate CSS, but that's a little out of the scope of this article.. anyhow:

	border:1px solid #eee;
ul li{

Alternatively, instead of response writing everything and putting unnecessary strain on the SmallTalk server, you can let XHTML handle that by closing off the SmallTalk code with %> then running the XHTML for the links and then opening the <% up again to close it with a bracket. In example, the first part would be something like:

userCookie := request cookieValueAt: 'user'.
(userCookie size) = 0 ifTrue: [
	<li><a href="index.ssp">Home</a></li>
	<li><a href="about.html">About</a></li>
	<li><a href="login.ssp">Login</a></li>
<% ]. %>

This tactic is commonly used with PHP development when you have a large chunk of XHTML formatted text that you would rather not make echo friendly.

In addition to setting a cookie when the user logins in, it is ideal to create a cookie with your GraphModel, although sessions might not be necessary if you are not dealing with GraphModel objects. Similar to cookies, sessions let you store user-specific data amongst pages but unlike cookies you can store multiple items in a session.

Here is the basic syntax:

session at: 'SomeThing' put: Your.Object new.

You don't necessarily have to put and object, you can also put a variable set elsewhere. When you want to access that value you can do something on your "session at:". Assuming I gave my session my graph model object and my particular SmallTalk application had ways for accessing that object, I could easily access that information. For example, if I wanted to create a list of nodes in my GraphModel:

	(session at: 'GraphModel') adjList nodeList do: [:each|
		response write: each name,'<br/>'.

Again, this code is specific to our particular SmallGraph application and accessors.

If I wanted to combine what I have discussed about with query string parameters, I can display red X's next to each node list element that the user can click to delete them:

	(session at: 'GraphModel') adjList nodeList do: [:each|
		response write: '[<a style=color:#ff0000;text-decoration:none; href=DeleteNodes.ssp?id=',each iD printString,'>X</a>]&nbsp;&nbsp;',each name,'<br/>'.

If you look closely, the X's are linked to a DeleteNodes.ssp file with a "id" query string parameter set to the id from each node.

The delete nodes ssp file is extremely simple with only a few lines to get the job done, with the last line redirecting the user to the page they came from, where there would be one less node.

id := request anyParameterValueAt:'id'.
(session at: 'GraphModel') removeNode: ((session at: 'GraphModel') adjList nodeList at: id asNumber).
response redirectTo: ('index.ssp?mode=1').

Limiting the number of files you use
When doing PHP development, one thing I like to do when tinkering around with forms is to set the form action to the current page so that I can have the action code on the same page as the form code. This is useful in certain scenarios, which are up to you to decide - namely, when you don't want to have extraneous files all over the place.

In PHP you would set the action to $_SERVER['PHP_SELF'] but in SmallTalk you set the form action to
request serverVariableAt: 'PATH_INFO'

PATH_INFO is the virtual path of the script being executed so in an example case its value would be "yourfolder/login.ssp". However with some configurations that doesn't work and in those cases I've had success with SCRIPT_NAME, which is also a virtual path of the script being executed.

Usually you just put the PHP or SmallTalk code you want to execute once the form is submitted at the top of the page before the form, but you need to do something else so that this code is not automatically executed.

To explain this concept more clearly, lets say I have a simple change password form as below.

<form action="<% request serverVariableAt: 'PATH_INFO'. %>" method="post">
<label for="password" style="width:160px;"><b>Current Password:</b> </label>
<input name="password" type="password" id="password" size="20"/><br/>

<label for="newpassword" style="width:160px;"><b>New Password:</b> </label>
<input name="newpassword" type="password" id="password" size="20"/><br/>

<label for="confirmnewpassword" style="width:160px;"><b>Confirm New Password:</b> </label>
<input name="confirmnewpassword" id="password" type="password" size="20"/><p style="font-size:10px;padding:0;margin:0;">*You will be logged out after a successful password change.</p><br/>
<input type="hidden" name="action" value="change_pwd"/>
<input type="submit" id="submit" value="Change Password"/>

There are two important lines to look at here. The first line starts the form and sets the action equal to the PATH_INFO thing I mentioned earlier. The next thing to look out is the input type="hidden" line, which submits an extra, hidden behind-the-scenes parameter along with the form. This hidden value will be used to check whether the SmallTalk code I will include above the form should be executed - eg, right after the form has been submitted.

Now for example SmallTalk code to go above the form.

myAction := request anyFormValueAt: 'action'.
(myAction = 'change_pwd') ifTrue: [
   "do something meaningful, change password"
	response redirectTo: ('user_account.ssp').


Similar to anyParameterValueAt which gets the value of particular query string parameters, anyFormValueAt gets the value of particular POST forms. In this case, I am checking to see if the hidden input action is equal to the value of "change_pwd" and if so, it will execute the appropriate code to change the user's password. To recap, I am checking the form input's action as a safeguard so that this code is not run when the page is loaded and is only run when the form has been submitted.

Links to this Page