How To

How do I remove the tool bar?

Set the value of "Deployment Mode" to true using the dispatcher editor in the application configuration. This can be also done programatically in the #initialize method of the application’s root component.

initialize
| app |
app := self registerAsApplication: 'app-name'.
app preferenceAt: #deploymentMode put: true

What is the most simple way to serve files from a Seaside application?

The most convenient way to serve files like pictures or stylesheets from a Seaside application is WAFileLibrary. See the class comment of WAFileLibrary for more information about how to use it.

The following assumes that you have defined MyFileLibrary as explained in the class comment.

First you will need to setup the root of your library. Typically this can be returned by a method.

library
^ MyFileLibrary

To display an image:

"#myPicturePng is the method in which the picture is stored"
html image url: (self library urlOf: #myPicturePng)

To display an image button (within a form):

html imageButton
url: (self library urlOf: #myButtonGif);
callback: [ self commit ]

To add a stylesheet and a javascript:

updateRoot: anHtmlRoot
super updateRoot: anHtmlRoot.
anHtmlRoot stylesheet url: (self library urlOf: #myStyleCss).
anHtmlRoot javascript url: (self library urlOf: #myJavascriptJs).

Advanced users might want to use Apache 2.

How do I access the initial request fields in my root component or task?

To access the initial request fields in a root component or task override #initialRequest:. You will get a request object as argument that you can ask for #fields.

How do I open a popup and answer a value to the caller?

To open a popup window you use #popupAnchor, which supports the same protocol as #anchor but opens the link in a new window. Instead of calling on self and thus replacing the parent, start a new render loop for the new window:

ParentComponent>>renderContentOn: html
html popupAnchor
callback: [ WARenderLoop new call: PopupComponent new ];
with: 'Popup'

In your popup component PopupComponent add a link or submit button with a callback along the following lines:

PopupComponent>>renderContentOn: html
html anchor
callback: [
self session closePopupAndContinue.
self answer: Time now ];
with: 'Close and Answer'

This will close the window and return the object passed to #answer: to the caller WARenderLoop new call: PopupComponent new.

How do I clean up external resources owned by Seaside sessions?

When a Seaside session is not used during a certain duration, it expires. After a session has expired, it cannot be used any more.

By default, this duration is 10 minutes (600 seconds) but it can be set to any other desired value using the "config" application ("Session Expiry" field). It can also be set programmatically:

app preferenceAt: #sessionExpirySeconds put: 1200

From time to time, Seaside processes expired sessions (see #unregisterExpiredHandlers). Expired sessions are removed from Seaside, making them candidates for future garbage collections, then they are sent #unregistered. The standard implementation of #unregistered in WASession is empty.

If it is necessary to release resources held by an expired Seaside session, this can be done by subclassing WASession and by reimplementing the #unregistered method.

How do I subclass WASession?

It is sometimes necessary to subclass WASession. Typical situations for subclassing WASession.

  • Need to clean-up external resources when Seaside session expires (reimplement #unregistered)
  • Need to attach some data to the Seaside session using additional instance variables
  • Need to include additional behavior, statistics, monitoring (reimplement #responseForRequest:)
  • Seaside applications are assigned their session class using the "config" application ("Session Class" field). It can also be set programmatically
app preferenceAt: #sessionClass put: MySession

How do I subclass WAApplication?

It is sometimes necessary to subclass WAApplication, for instance when it is needed to display a session expiration page.

When you subclass WAApplication, make sure that the subclass reimplements #description on the class side, this will ensure that you can differentiate the WAApplication subclasses in the "config" application. Once the WAApplication is in place you can use the "config" application to create your Seaside application.

New Seaside applications can also be created programmatically by carefully reimplementing #applicationWithPath: on the class side of your root component.

How do I get the requester’s IP address?

It is sometimes necessary to know the IP address of the requester. For instance you may want to produce a different behavior when the request comes from "localhost".

The IP address is normally available from the native request object. The native request object is obtained by sending #nativeRequest to the current Seaside request object, like this:

renderContentOn: html
nativeRequest := self session currentRequest nativeRequest

The native request object will be a Kom request, a Swazoo request or a WebToolkit request depending on the exact environment.

For instance, with Squeak:

ipAddress := nativeRequest remoteAddress.

How do I display an expiration page?

When the user wants to reuse an expired Seaside session, Seaside silently starts a new session. It is sometimes necessary to change this default behavior and notify the user that the session has expired.

First thing to do is to subclass WAApplication. Next thing is to re-implement either #handleExpiredRequest: or #expiryPathFor:.

In #handleExpiredRequest: you are given a chance to build a redirect response to another Seaside application that will display the expiration component.

Below is a very simple example based on reimplementing #expiryPathFor:

expiryPathFor: aRequest
^ self basePath , '?expired=true'

The expired=true field can be obtained by implementing #initialRequest: in the application root component, for instance:

initialRequest: aRequest
super initialRequest: aRequest.
(aRequest fields includesKey: 'expired')
ifPresent: [ restartedAfterExpiration := true ]

The rendering can then take expiration into consideration, for instance

renderContentOn: html 
restartedAfterExpiration
ifTrue: [ self renderExpiredOn: html ]
ifFalse: [ self renderNormalOn: html ]

How do I make Seaside Cincom Smalltalk serve shorter URLs?

If you would like to make the Cincom Smalltalk Seaside application server manage all the path-information on its own and to let it appear in the root like:

http://localhost:8008/counter

then you need to load the SeasideShortPath parcel that can be found in the contributed/other/Seaside/BonusPack directory starting with Cincom Smalltalk 7.3

How do I reset the Seaside administrator password?

To view

(WADispatcher default entryPoints at: 'config') preferenceAt: #login.
(WADispatcher default entryPoints at: 'config') preferenceAt: #password.

and to change:

(WADispatcher default entryPoints at: 'config')
preferenceAt: #login put: 'new id'.
(WADispatcher default entryPoints at: 'config')
preferenceAt: #password put: 'new password'.

How do I purge all existing sessions that might be open?

WARegistry clearAllHandlers.
Smalltalk garbageCollect.

How do I provide an anchor where the document is created after the link is clicked?

With the code shown below the MIME document is created at render time, but the data I want sent is dependent upon the fields on the form.

html anchor
document: anObject mimeType: mimeTypeString fileName: fileNameString;
text: 'Export (csv)'.

Is there a convenient way to make the document be created after the link is clicked?

Yes, this is a common problem. One solution is to create an anchor and redirect:

html anchor
callback: [
self session returnResponse: (WAResponse
document: anObject
mimeType: aMimeType
fileName: aString) ];
with: 'Export'.

The argument anObject can be a string, byte-array or file-stream:

(FileStream readOnlyFileFullyNamed: '/tmp/test.pdf') binary

To find out the mime-type aMimeType of a filename you can let Seaside do the guessing:

MIMEType forFileNameReturnSingleMimeTypeOrDefault: '/tmp/test.pdf'

How do I secure a Seaside application for deployment?

  • remove all not needed applications
  • put the remaining applications into deployment mode
  • remove the config application or choose a good username and password (not the default!)
  • make sure you’re protected against SQL-injection

How does Seaside protect me against XSS?

Per default all output is escaped for HTML unless especially reqested by using the #html: method.

How do I change the title of a page?

In a visible component implement:

updateRoot: anHtmlRoot
super updateRoot: anHtmlRoot.
anHtmlRoot title: 'the new pageTitle'

How do I use google analytics?

Google analytics is a powerful and free analytic service provided by google. To use it in seaside:

  1. You need to have a google account and to register your site name to google analytics.
  2. You need to configure a bit the registered site in google analytics so as to exclude all dynamic variables of the url (_s, _k and friends). The easiest solution is to simply remove all parameters by creating a custom filter. See the screenshot below [1].
  3. You need to insert a link in your main seaside component (one that is always rendered).
  • in your main component, add a method, say #renderGAnalyticsOn: that is called from #renderContentOn:
renderGAnalyticsOn: html
self class uacct isEmptyOrNil ifTrue: [ ^ self ].
html script: 'var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src=''" + gaJsHost + "google-analytics.com/ga.js'' type=''text/javascript''%3E%3C/script%3E"));'
.
html script: 'var pageTracker = _gat._getTracker(', self class uacct printString, ');
pageTracker._initData();
pageTracker._trackPageview();'
  • then, add a class side method where you store the key google provided for your site (YouMainComponent class)
uacct
^'you google analytics key for a given domain'
  • note: a better solution is to create a special component as in pier where a dedicated widget can be added to the header for instance (it’s invisible, and always rendered)
    • You just select it then hit ’settings’ and you enter the key provided by google in the form).

[1] - Filter configuration in google analytics Screenshot

How do I get the IP address or the user?

It is sometimes necessary to know the IP address of the requester. For instance you may want to produce a different behavior when the request comes from "localhost".

The IP address is normally available from the native request object. The native request object is obtained by sending #nativeRequest to the current Seaside request object, like this:

renderContentOn: html
nativeRequest := self session currentRequest nativeRequest

The native request object will be a Kom request, a Swazoo request or a WebToolkit request depending on the exact environment. For instance, with Squeak:

ipAddress := nativeRequest remoteAddress.

This gets you a ByteArray. ipAddress asString does not behave as a beginning user might expect. ipAddress asIpString provides a much more usable result.