You should begin by reading Avi's overview of Seaside's configuration system. I'll also assume that you are familiar with using existing configurations, at the very least making simple changes to an application's configuration to set the root component etc. So, the goal of this document is to show you how to add your own application-specific configuration parameters.
With the background out of the way, a quick example will show
you most of what you need to use this framework. Generally the
framework seems designed to help encourage reuse of Seaside
components in multiple applications (throughout these examples I
use the term "application" to mean an instance of
WAApplication, typically created through the Seaside web-based
tool). From the point of view of a component, the configuration
can be thought of as a Dictionary-like collection of associations
which may be customized by the system administrator. In our first
example we have a component
SCFrontDoor, used as a generic store front:
WAComponent subclass: #SCFrontDoor instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: SCSeasideTutorial'This component renders itself with:
renderContentOn: html html heading: self storeName. html hr. html text: 'Welcome to our awesome store!'.
We start with an implementation of storeName which
simply returns a fixed string:
storeName ^'Sir Save-some-dough'
We also need the class-side canBeRoot method:
canBeRoot ^true
We create a seaside application with a path
/seaside/save-some-dough and with SCFrontDoor as its
root component, bring it up in our browser and everything looks
hunky dory. Our application goes into production and months later
we get a request to develop a similar shopping application for a
similar business. So similar, in fact, that we can parameterize
the difference by a handful of values (in this case the value of a
single string: the store name). So, rather than develop a
separate store front class for each store, we will create a new
configuration and allow the system administrator to create as many
store fronts as s/he likes. We begin by creating a subclass of
WASystemConfiguration called
SCExampleConfiguration:
WASystemConfiguration subclass: #SCExampleConfiguration instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'SCSeasideTutorial'In this new class we need only override
#attibutes as
follows:
attributes ^ Array with: (WAStringAttribute key: #storeName group: #store)which obviously specifies that our configuration has a single attribute of type
String named storeName.
The group property is used to group attributes in the web-based
editor. You can see the full list of existing attribute types by
browsing the Seaside-Configuration category...more on that later.
Here we have indicated that each application which "uses"
our configuration will have a value of the property
storeName. We can specify a default value by
implementing an accessor in SCExampleConfiguration:
storeName ^'Default store name...you forgot to set it!'although a default value isn't required. Now, in our browser we open the Seaside configuration tool and configure the /seaside/save-some-dough application adding
SCExampleConfiguration to the "Configuration
Ancestors" section. Your configuration page should have a
section looking like this:
SCFrontDoor>>storeName as follows:
storeName ^self session application preferenceAt: #storeName
Now our component fetches the value of the store name from the
configuration. We have attained our goal: we can have several
Seaside applications which use our SCFrontDoor and the
system administrator can configure each of them to have different
store names. Try this by creating another seaside application at
/seaside/other-store which uses our same root component but with a
different value for the store name (remember to add
SCExampleConfiguration to the Configuration Ancestors
list).
Finally, it is relatively common practice to provide a mechanism for a root component to install itself into a Seaside server. The most common one I've seen is a class side initialize method:
initialize self registerAsApplication: 'blah'If you want your application to include your configuration you need to change this to something like:
initialize | app | app := self registerAsApplication: 'blah'. app configuration addAncestor: SCExampleConfiguration localConfiguration.
WASystemConfiguration subclass: #GoodsAppConfiguration instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'SC-GoodsTools' attributes ^ Array with: (WAStringAttribute key: #goodsHost group: #Goods) with: (WANumberAttribute key: #goodsPortNumber group: #Goods)
WAStringAttribute)WAListAttribute)WABooleanAttribute)WANumberAttribute)WAPasswordAttribute)There are plenty of examples of each of these in the Seaside
tools...just search for references to each of the attribute
classes. Extending the system to support more value types and
editors is quite simple as well. Suppose we wanted to add a
property to control the background color of the title region of our
store. There are two new players in attribute customization:
WAConfigurationEditor which is a Seaside component
used in the Seaside "config" application and
WAConfigurationAttribute whose subclasses are adapters
used to convert to and from string representations as well as for
double dispatching to display an editor for an attribute.
Let's add color to our company name:
renderContentOn: html
html style: 'background-color:' , self backgroundColor.
html div: [html text: self storeName].
html hr.
html text: 'Welcome to our awesome store!'
backgroundColor
^self session application preferenceAt: #titleBackgroundColor
Since there no pre-defined attribute type for color values we
subclass WAConfigurationAttribute
WAConfigurationAttribute subclass: #SCColorAttribute instanceVariableNames: '' classVariableNames: 'WebSafeColors' poolDictionaries: '' category: 'SCSeasideTutorial'and are now obliged to implement three methods in that class:
accept: aVisitor with: anObject ^ aVisitor visitColorAttribute: self with: anObject stringForValue: aColor ^ aColor valueFromString: aString ^ aStringOn the class side we add method to access the standard "web safe" colors:
"put these on the CLASS side" webSafeColors ^WebSafeColors ifNil: [self initWebSafeColors] initWebSafeColors "self initWebSafeColors" WebSafeColors := OrderedCollection new. 0 to: 16rFF by: 16r33 do: [:r | 0 to: 16rFF by: 16r33 do: [:g | 0 to: 16rFF by: 16r33 do: [:b | WebSafeColors add: '#' , (self twoDigitHex: r) , (self twoDigitHex: g) , (self twoDigitHex: b)]]]. ^WebSafeColors twoDigitHex: value | result | result := value radix: 16. (result beginsWith: '16r') ifTrue: [result := result allButFirst: 3]. ^result size < 2 ifTrue: ['0' , result] ifFalse: [result]Note the use of the visitor pattern in
#accept:with:.
When a configuration attribute's value is being edited,
#accept:with: is invoked with a
WAConfigurationEditor, and a
WAHtmlRenderer as arguments. (While I haven't tried
it it seems that this makes the configuration framework quite
reusable -- could be used to support user application preferences
etc.) The method #visitColorAttribute:with: doesn't
exist in the class WAConfigurationEditor so we must
add it (to WAConfigurationEditor, not our
SCColorAttribute!):
visitColorAttribute: anAttribute with: html
| group current |
group _ html radioGroup.
current _ configuration valueForAttribute: anAttribute.
html
table: [1
to: SCColorAttribute webSafeColors size // 20
do: [:major | html
tableRow: [1
to: 20
do: [:minor |
| theColor |
theColor _ SCColorAttribute webSafeColors at: major - 1 * 20 + minor.
html attributeAt: 'bgcolor' put: theColor.
html
tableData: [
html space.
html radioButtonInGroup: group
selected: theColor = current
callback: [:v | configuration takeValue: theColor
forAttribute: anAttribute].
html space]]]]]
(Note: adding methods to classes in
Smalltalk is OK). I appologize for this mess on two accounts:
the messy code obfuscates the simplicity of the job of this method
and when rendered it looks ugly. Basically we need to render a component
on the argument html which, when edited, sends
#takeValue:forAttribute: to the editor's current
configuration (in the i-var configuration). Look at
the other editors for simpler examples. Anyway, there it is.
Now we need to modify our configuration subclass to include a
color attribute, modify SCExampleConfiguration>>attributes:
attributes ^ Array with: (WAStringAttribute key: #storeName group: #store) with: (SCColorAttribute key: #titleBackgroundColor group: #store)and add a default value:
titleBackgroundColor ^ '#ff0000'Now, we configure one of our store fronts and we are presented with a section that looks like this:


Seaside provides the ability to configure your own instances of
WAUserConfiguration, independent of an application, and then add
them as ancestors to an application's configuration. I'm shooting
from the hip here but I think this is to make it simpler to create
multiple applications which have the same configuration layout
and/or start with similar "default" values which are
different from the defaults supplied by the configuration classes
themselves. Suppose, for example, that you have five unrelated
(not ancestors of each other) subclasses of
WASystemConfiguration which you use in all of your
applications. In addition the default values provided by your
classes are not the ones you typically use when building a new
application. This might be a motivation to create a "free
standing" user configuration. In the Seaside
"config" app, click the "Edit Configurations"
link and then enter a new configuration name, maybe "standard
store" in the New Configuration text box and click
"Add". You are presented with a configuration editor
but you are not configuring any application in particular, just
building a template. So, for our applications you would add
SCExampleConfiguration to the ancestors of this user
configuration and possibly cusomize the color to default to some
other color, instead of the existing programmatic default of red.
Then, when building your store applications, instead of adding
SCExampleConfiguration to your applications you'd add
standard store. Note: as of this writing, due to a
bug in Seaside your pre-built configurations will not show up as
choices in the ancestor's menu. To fix this bug, replace
WASystemConfigurationPool>>configurations with:
configurations ^ configurations copy addAll: (WASystemConfiguration allSubclasses collect: [:ea | ea localConfiguration]); yourselfHere is what your configuration page would look like for an application configured in this way:

standard store instead of from
SCExampleConfiguration.
SCSeasideTutorial so I put the method
(WAConfigurationEditor>>visitColorAttribute:with:) in a
method category named *SCSeasideTutorial. Just be sure to
choose method names which won't clash with future extensions to this
class. Also, keep in mind that you don't have to add a method to
WAConfigurationEditor if you don't like the idea. You
can create your own class to house the editor code (or even put the
method in the attribute class) with some modification. In this
tutorial I was simply following the pattern of the existing
attributes.