BaláZs Ree Introduction To Kss, Kinetic Style Sheets

27
Simple KSS Note: This is the print view with all the tutorial pages on one page. The paginated version is available here, if you prefer that. The target audience is everyone who wants to start using KSS. Zero knowledge of javascript is required for this. Both designers and programmers should complete these lessons. Begin with KSS Teaches how to setup and use KSS through a simple example. Installation KSS is included with Plone 3.0. (We also plan provide backports for earlier versions, at a later point.) This document is valid for this version: kss.core 1.2, plone.app.kss 1.2 (Plone 3.0) It is also important for all the further work that you run zope in debug mode and that you disable caching from your browser. In FireFox, if you install the Web Developer assistant in FF, this can be achieved from the leftmost menu by ticking in the Disable cache checkbox. Note that otherwise you need to force the browser to reload the page completely after each change you have made in your kss resource files. In case you have made a change and yet nothing happens on reload, it is caching to be blamed. Also you would need to familiarize yourself with reading javascript exceptions that occur on your page. KSS will raise a javascript error in case something is wrong and you would be able to see this from your browser, some browsers (IE?) may require some debug option to set up. In case you have no browser preference we suggest to use FireFox as it comes with the most friendly debugging facilities. You are also suggested to install the FireBug extension. Note that apart from reading the error message, you do not need to be able to understand or debug javascript at all. Also you will not touch any javascript code during this tutorial. Production and Development mode There is an additional logging facility that provides you with useful debug and informational messages about what happens. Debug information is only shown in Development mode of KSS. Development mode can be activated if you enable Debug mode in portal_javascript tool. After doing so, you must reload your page in the browser. If you want to switch back to Production mode, you will need to reload the page again. Production mode offers a slightly smaller javascript to be loaded into your page, and slightly faster execution, for example, by not giving you logs and detailed error messages. If you ever see the javascript error message Unknown message (kss optimized for production mode), this means you are running in Production Mode and you need to switch to Development Mode in order to see what happens. If you are in Development Mode, and are using FireFox with the FireBug extension, you need not do

description

KSS, Kinetic Style Sheets, is a framework that enables developers to create rich (AJAX) user interfaces without knowing javascript at all. KSS itself is by default included with Plone3 and is also usable with Zope3. In the future we plan to make it available for other pythonic and non-pythonic platforms as well. During the demonstration we give a step by step introduction to adding dynamic behaviour to your browser page by the KSS stylesheet and server side only python code. We also introduce the setup and debugging skills needed to add dynamicity to your application. The targeted audience for the demo are Plone developers and integrators, familiar with server side Plone scripting. A basic knowledge of HTML and CSS is also needed. As a result you will get an introduction to the usage of KSS. Knowledge of javascript is not needed for attending the demonstration.

Transcript of BaláZs Ree Introduction To Kss, Kinetic Style Sheets

Page 1: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

Simple KSSNote: This is the print view with all the tutorial pages on one page. The paginated version is availablehere, if you prefer that.

The target audience is everyone who wants to start using KSS. Zero knowledge of javascript is required for this. Both designers and programmers should complete these lessons.

Begin with KSSTeaches how to setup and use KSS through a simple example.

Installation

KSS is included with Plone 3.0. (We also plan provide backports for earlier versions, at a later point.)This document is valid for this version: kss.core 1.2, plone.app.kss 1.2 (Plone 3.0)

It is also important for all the further work that you run zope in debug mode and that you disablecaching from your browser. In FireFox, if you install the Web Developer assistant in FF, this can beachieved from the leftmost menu by ticking in the Disable cache checkbox. Note that otherwise youneed to force the browser to reload the page completely after each change you have made in your kssresource files. In case you have made a change and yet nothing happens on reload, it is caching to beblamed.

Also you would need to familiarize yourself with reading javascript exceptions that occur on your page.KSS will raise a javascript error in case something is wrong and you would be able to see this fromyour browser, some browsers (IE?) may require some debug option to set up. In case you have nobrowser preference we suggest to use FireFox as it comes with the most friendly debugging facilities.You are also suggested to install the FireBug extension.

Note that apart from reading the error message, you do not need to be able to understand or debugjavascript at all. Also you will not touch any javascript code during this tutorial.

Production and Development mode

There is an additional logging facility that provides you with useful debug and informational messagesabout what happens. Debug information is only shown in Development mode of KSS. Developmentmode can be activated if you enable Debug mode in portal_javascript tool. After doing so, you must reload your page in the browser. If you want to switch back to Production mode, you willneed to reload the page again. Production mode offers a slightly smaller javascript to be loaded intoyour page, and slightly faster execution, for example, by not giving you logs and detailed errormessages.

If you ever see the javascript error message Unknown message (kss optimized forproduction mode), this means you are running in Production Mode and you need to switch toDevelopment Mode in order to see what happens.

If you are in Development Mode, and are using FireFox with the FireBug extension, you need not do

Page 2: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

anything, logs will appear directly in the FireBug console. If you use Safari, logging also appears in theSafari console. If these possibilities are not available for you, you have two other choices: either useFirebug Lite (on IE). or MochiKit (all browsers).

Activating logging with Firebug Lite (IE)

To activate Firebug Lite, you need to manually add the javascript file ++resource++firebuglite.js intothe set of resources. You need to set this script to Non-Merging, and set its compression level to None.You must also make sure that you move this file above the ++resource++kukit.js and++resource++kukit-devel.js. (Don't miss to press Save after this!) When this is done, youneed to reload the page. After this Firebug Lite can be activated by pressing F12.

Activating logging with MochiKit

You need to do this only if you are unable to use both FireBug or FireBug Lite for some reason,although this should not happen.

First you need to enable MochiKit's javascript in the portal_javascript tool. Find the line with the id++resource++MochiKit.js, and enable it, then press Save. Following this, reload your originalpage.

Then enter the following url into your browser after your page has been loaded (you can bookmarkthis as a link to avoid typing it in each time):

Remark:

This needs to be repeated each time the page gets reloaded, because each time a page isloaded, the browser starts in a clear state, as far as javascript is concerned.

See the result of logging

In your kss debug console (depending on the debug method you use) some log messages will start onreloading a page. They will begin this way:

If this does not happen, check if you have succeeded with the steps described above, most notably, areyou in Development Mode? You may also need to log in to the portal, as by default KSS is disabled inPlone for non-authenticated users.

The text following the first line may indicate that KSS installed more eventrules on its own. This wouldnot be a problem, but to allow us starting from a clean situation, we will disable all existing rule files.

Disable KSS's default event rule (KSS) files

Plone has some KSS rules in effect by default, which produces a lot of log messages. So first we want todisable this. Go to the ZMI in the portal root, enter the tool plone_kss and unselect the checkboxleft from the names of plone.kss and at.kss. Then save this page.

Reload your original portal page. At this point the logging console should display some log messages:

javascript:kukit.showLog();

Loading KSS engine....

Page 3: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

This means that KSS is active in the page, but there were no rule sheets to set up - so nothinghappened.

If the Count of KSS links is not zero, you need to return to the previous steps and make theremaining kss resources inactive.

Some more explanation about the messages. The lines before the Count of KSS links are messages from plugin components that initialize themselves. There is no need to worry about that now.

Having arrived to this point, we can now proceed to start our first usage example. Attach an event rule(kss) file

First you need to attach a kss resource file to your page. This is similar to a CSS file in purpose - eventhe syntax is almost valid CSS - but it has different semantics and it controls how KSS events arebound to your page elements.

To do it simplest, create a file named tutorial1.kss in your custom skins directory. To make the filebound to each page in the portal, we need to make it known by ResourceRegistries. Go to the ZMI inthe portal root, enter plone_kss, press Add and customize your new entry in the following way:

Notice that merging should be disabled. We also set Compression type to None, and disable caching,to make it work more debugging friendly during this tutorial.o

Note:

In a production environment, you would want to set Compression type to safe, and allow merging and caching of the resource. You can also do this right now, just then do notforget to enable Debugging Mode on the top of the same page. This will make the resourcedebuggable, even if the production settings are applied for compression, caching andmerging.

Loading KSS engine.Using original cssQuery.KSS bootstrap set up in Plone DOMLoad event.Plone legacy [initializeMenus] action registered.Plone legacy [bindExternalLinks] action registered.Plone legacy [initializeCollapsible] action registered.Plone [createTableOfContents] action registered.Initializing 3 menus.KSS started by Plone DOMLoad event.Engine instantiated in [DOMLoad].Initializing rule sheets.Count of KSS links: 0Setup of events for document starts.0 special rules bound in grand total.0 nodes bound in grand total.

Page 4: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

After pressing Add, navigate back to your portal frontpage, and reload it. You should see the followingmessages:

This means that your kss file is active on the page, although it is yet not doing anything. At this pointyou can proceed to the next chapter.

(optional:) Doing it the zope3 way

In the zope3 style of development instead of putting files into skins, we prefer to use resources. Fromthe point of view of KSS this is all the same but this leads to a cleaner development style, bettersupported in the future versions of Plone. Do not attempt to do this however if you do not know atleast a little of Five, because in this tutorial we will not teach you Five itself, we only show the way touse it for KSS. Remember: zope3 development style is easy once you have learned it, but one mightfeel that it has a steep learning curve.

To do this, you have to create a new product for the tutorlal or use one of your existing products. Inthe product directory on the filesystem, create a browser subdirectory and save the kss file there withthe name tutorial1.kss. Then in the product root create a configure.zcml file with the followingcontent:

After this you need to restart zope: if you have made an error in this file you see it immediately as zopewill refuse to start at all. If zope has started up, then check that the urlhttp://127.0.0.1:8080/++resource++tutorial1.kss returns the kss file correctlyfrom the browser (replace the beginning with your portal root url).

Also to use this file you must use ++resource++tutorial1.kss as the name of the file in theportal_kss tool. After making this change you should reload your page from the browser, open thekss debug console and see the log messages like above.

In addition, since now you have a separate tutorial product, you could also make a GenericSetupprofule that automatically registers the kss resource with the kss tool. To have an example of this, youmay want to peek in to Products/CMFPlone, into the profiles directory, ald learn the usage pattern from there. - However this step is totally optional and you might want to skip it at this point.

Set up some sample content

Loading KSS engine....Initializing rule sheets.Count of KSS links: 1Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type kssGET http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss (39ms)Finished loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss resource type kss in 64 + 0 ms.Setup of events for document starts.0 special rules bound in grand total.0 nodes bound in grand total.Setup of events for document finished in 4 ms....

<configure xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser">

<!-- the kss resource --> <browser:resource file="browser/tutorial1.kss" name="tutorial1.kss" />

</configure>

Page 5: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

We will need some sample content in our example. For example we will need a navtree portlet appear on our page. Since this is not appearing on the front page of a newly created portal, we advise to create thefollowing basic structure, that consists of a folder and three pages in it:

In this case, if we visit the url http://127.0.0.1:8080/folder1/page1, we will get to the view of our simple page, and we can see the navigation portlet appearing in the left portal column.

You also need to log in as an authenticated user, because in the standard setup of Plone, KSS isdisabled for unauthenticated users. Since you just created the above structure, this should already betrue.

Write a simple event rule

Now we will add a KSS event rule to the kss file we created. This is the base unit that controls thedynamic behaviour of your page. A KSS rule looks similarly to a CSS rule. There is a selector part thatselects one or more html nodes just like in CSS, then there is an event descriptor that is attached to theCSS selector with the comma. We will use the click event that will trigger when the selected node ornodes are clicked with the mouse.

First we decide which nodes we bind to. This can be any selectable element in the page. Let us firstbind the event to the active element of the navtree - that is, the element where we are at the moment.Inspecting the page with Firebug, we will see the html of the required line:

We can see that the selector of this node can be, for example, a.navTreeCurrentItem so this will be theselector we use. The full event rule that we enter into the kss file will be this:

Notice that after the selector we put :click, this tells KSS what event to use and is always obligatoryin the KSS rule. Between the braces we have various properties that control what actions are bound tothe selected nodes, and also the parameters for this actions. This rule says that when the event getstriggered, we do not want to communicate with the server yet, but simply pop up an alert box. This isjust a debugging aid that helps us see if we are on the right track. The line action-client:alert; says we have a client action with the name alert. Later we will learn to use server actions.

Now reload the page, and you should see the following in the KSS console log:

portal root ----- folder1 ----- page1 | ---------- page2 | -----------page3

<a title="" href="http://127.0.0.1:8080" class="navTreeCurrentItem visualIconPadding"> Home</a>

a.navTreeCurrentItem:click { action-client: alert;}

Page 6: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

Important is to see that our newly created eventrule is in effect (EventRule [#0-@@0@@click]),and that it is bound to 1 node in the page. The rest of the log can be ignored right now.

REMARK:

If the new contents of the kss file does not come into effect, make sure that you enabledDebugging in the portal_kss tool. If this is not done, the resource is most likely got cachedwhere it should not be.

If we click on the Home line in the navtree portlet (or to the item line which is current andhighlighted), the following popup dialog appears:

This tells us that the event has triggered in order, and also gives some additional information, like thenumber of the event rule (#0), the merge id of the event which is an internal identifier of KSS(@@0@@click), and the html node that triggered the event (A).

After pressing the OK button, the page will be reloaded because the link will be followed as if wewould have clicked it normally. We will deal with this soon.

We should however note an important principle right away. If the rule does not select to any nodes,simply nothing happens: no error message will be raised. This is the designated behaviour and it is inline with how css behaves too. This is what allows us to put a lot of rules into the same kss file. Noneed to set up conditions: if the selected nodes are not present in the current page, simple nothinghappens.

This is also the time to see what happens if we have an error in the kss file. Let us change click to klick and reload the page. We will get a javascript error. In FireFox for example a javascript errorappears and we can see the followings in the console log:

Loading KSS engine.KSS bootstrap set up in Plone DOMLoad event.Plone legacy [initializeMenus] action registered.Plone legacy [bindExternalLinks] action registered.Plone legacy [initializeCollapsible] action registered.Plone [createTableOfContents] action registered.Initializing 2 menus.KSS started by Plone DOMLoad event.Engine instantiated in [DOMLoad].Initializing rule sheets.Count of KSS links: 1Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type kssGET http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss (52ms)++resource++kukit... (line 3342)EventRule #0: a.navTreeCurrentItem EVENT=click++resource++kukit... (line 2094)Finished loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss resource type kss in 58 + 7 ms.Setup of events for document starts.Using original cssQuery.EventRule [#0-@@0@@click] selected 1 nodes.++resource++kukit... (line 2133)0 special rules bound in grand total.++resource++kukit... (line 2667)Instantiating event id [@@0], className [0], namespace [null].++resource++kukit... (line 3868)1 nodes bound in grand total.++resource++kukit... (line 2582)Setup of events for document finished in 14 ms.

Page 7: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

The important part of the message says that the klick global event does not exist, the rest is justFireFox cruft. If we just make a syntax error, we get a ParsingError that also give the position of theerror in the file. As a consequence nothing will work and we are forced to fix the error immediately andreload the page afterwards.

Note:

The null in [null:klick] means that the event is in the global namespace. Foeevents in a custom namespace, the namespace would be displayed instead of null.

Specify an event binding parameter

Sometimes it is necessary to customize the way the event is bound. This can be done with parameterswith the event binding. In the previous example after we closed the alert, the page reloaded because thelink was followed by default. The click event has a parameter preventdefault which can be used tooverride this. The modified rule will be:

The full property name is evt-click-preventdefault where three segments are separatedwith dashes:

evt says this is an event binding parameterclick is just the name of the event. It is obligatory to show it here.the last segment is the name of the parameter itself that we are setting.

Also note:

The order of action-client and evt-click-preventdefault is not important.Both capitals and small capitals are allowed in the boolean values, e.g. True or true, False or false are equivalent.

It is entirely up to each event which parameters they take and which ones are obligatory, or which oneshave a default value. At the moment the preventdefault event is only usable for the click event and it has a default value of False. Obviously it may only be necessary to set this parameter if there isotherwise something that the browser wanted to do on the click, before we started to modify itsbehaviour.

The order of the lines within the rule block otherwise does not matter.

If we reload the page, after clicking on the link the alert will pop up but afterwards the link will not befollowed. Specify an action parameter

We can also specify parameters to the actions themselves. The main difference between the eventbinder and the action parameters is, that the previous ones are effective when the event is bound (i.e.when we load the page) while the latter ones come to effect when the event is triggered (i.e. when weclicked on the link). As was the case with the event parameters, it is entirely up to the given action to

[Exception... "'Error parsing KSS athttp://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss :ParsingError: Undefined event : [null:klick].' when callingmethod: [nsIDOMEventListener::handleEvent]" nsresult:"0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)" location:"<unknown>" data: no]

a.navTreeCurrentItem:click { evt-click-preventdefault: True; action-client: alert; }

Page 8: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

decide what parameters it accepts and handles. Now, the alert action just happens to accept amessage parameter, so we extend our rule with one line:

The property name was alert-message, the semantics is that we must always use the name of the action(alert) followed by a dash and the name of the parameter. Notice also that we stringified the value onthe right side: this is only necessary if the value is not a single word. Both single and double quotes canbe used and the quotes can also be used backslashed inside the string.

Reload a page, click on the link and see the alert popup appearing with the text we specified, withdebug information added to the end of it:

Alert action dialog 2

This may also be the right moment to experiment if caching is not keeping you away from seeing yourchanges immediately. Principally, during development, when you have modified and saved the kss file,after reloading the page you should have the new values in effect - if you assure this, you will saveyourself from a lot of headache.

You have learned the basics of setting up kss rules so far, but nothing that would really do anything sofar. It is now time to sit back and relax a bit before we get to the definition and usage of server actions.

Continue...

Begin with KSS, part 2We will continue and learn how to create server actions and specify their execution fromour event rules.

.

This tutorial is the direct continuation of the first part. We will learn how to specify server side actionsand finally complete our first useful AJAX example. Creating a server action

Until now, we called a client action (alert) from our event. This was good to test if our event wastriggered. However, in a typical AJAX pattern, we want to call a server action.

Server actions are implemented as methods on the server. They could be any kind of callable methods,including python scripts, and this is what we will do first. Besides doing whatever is necessary for thebusiness logic, the task of the server method is to assemble a sequence of commands. Thesecommands are marshalled back to the client to be executed there. A command is a call to DOMmanipulation client code. A command can (in most cases, should) have a selector, to set the scope ofnodes on which the command is executed, and a set of parameters for execution.

Although existing components come with implemented server action methods, it is easy to create acustom one since it requires only python skills. Let's create a python script (response1) in thecustom skin with the following content:

a.navTreeCurrentItem:click { evt-click-preventdefault: True; action-client: alert; alert-message: "Hey, we are here";}

Page 9: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

After the imports, we initialize the view that will hold the KSS commands that we want to send back tothe client.

Then we add a command. The name of the command is replaceInnerHTML. This is one of our most useful commands : it simply replaces the contents of the selected html nodes with some htmlstring.

To specify which nodes will be selected, the command also needs a selector: in this example, astandard CSS selector. We choose to replace the portal actions of a Plone portal that are on the top ofthe page - but we could choose any other element as well.

The replaceInnerHTML method is accessed through a command set. Since we have a pluggablesystem, we need to refer to the component that defines the methods, in this case, the 'core' commandset.

In the last line, the renderKSSCommands call is mandatory : it will generate the response payloadfrom the accumulated commands. To look at this payload, let's access this method directly from thebrowser: http://localhost:8080/plone/front-page/response1. We will see Wedid it! on the screen, but let's have a more careful look at the source of the response:

Note:

In KSS version 1.4, the payload looks slightly different, but structurally it is the same.

This is an XML response, where we can see how commands and parameters are actually marshalled.When the response is interpreted by the KSS engine, it will execute the commands with the givenparameters.

Calling the server action

Now, we have finished to build our server action; we want to call it from our kss style sheet. We replaceour previous KSS event rule with the following one:

# import Through-The-Web(TTW) APIfrom kss.core.ttwapi import startKSSCommandsfrom kss.core.ttwapi import getKSSCommandSetfrom kss.core.ttwapi import renderKSSCommands

# start a view for commandsstartKSSCommands(context, context.REQUEST)

# add a commandcore = getKSSCommandSet('core')core.replaceInnerHTML('#portal-siteactions', '<h1>We did it!</h1>')

# render the commandsreturn renderKSSCommands()

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xmlns:kukit="http://www.kukit.org/commands/1.0"><body><kukit:commands><kukit:command selector="#portal-siteactions" name="replaceInnerHTML" selectorType=""> <kukit:param name="html"><h1>We did it!</h1></kukit:param></kukit:command></kukit:commands></body></html>

Page 10: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

The action-server line specifies the name of the remote method to call: response1 (since this is how we named our python script). The script will be called on the context url of the page we are at.

Let's reload the page so that the new kss comes into effect. Open the loggingpane. Then press the"Home" line in the navtree portlet. It works! We can see the site actions replaced with our text. Alsonotice that a few things have been logged to the kss console:

This gives a lot of information about what happened in the client:

the server is notified,the response is received,it is parsed successfully,it contains one command,the command selects 1 node to act on.

Now let's change our command response in the following way:

This way, the current time is sent back by the server on each click and we can see that somethinghappens.

It is interesting to note that we did not need to reload the page in order to see the effect of this change.Because we only made changes on the server, we did not need to load anything new on the client side.So we can continue to debug from the already loaded page and this will work even through serverrestarts.

What happens if the server-side script has an error, or the client does not get a correct response forsome reason? For example, changing DateTime to DateTimeX in our code, results in the following:

Request failed at url http://127.0.0.1:8080/demo11/folder1/page1/response1, rid=1, server_reason="ImportError: import of "DateTimeX" from "DateTime" isunauthorized. The container has no security assertions. Access to 'DateTimeX' of(module 'DateTime' from'/usr/local/lib/Zope2.10/lib/python/DateTime/__init__.pyc') denied."

The error Request failed indicates that we have to turn to the server to debug the problem. Our

a.navTreeCurrentItem:click { evt-click-preventdefault: True; action-server: response1;}

RequestManager notifies server http://127.0.0.1:8080/demo11/folder1/page1/response1, rid=0 (RQ: 1 OUT, 0 WAI).POST http://127.0.0.1:8080/demo11/folder1/page1/response1?kukitTimeStamp=1191846467877 (109ms)...RequestManager received result with rid [0] (RQ: 0 OUT, 0 WAI).Parsing commands.Number of commands : 1.Selector type [default (css)], selector [#portal-siteactions], selected nodes [1].[replaceInnerHTML] execution.++resource++kukit...1 nodes inserted.++resource++kukit......

...from DateTime import DateTime

# add a commandcore = getKSSCommandSet('core')content = '<h1>We did it!</h1><span>%s</span>' % DateTime()core.replaceInnerHTML('#portal-siteactions', content)...

Page 11: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

best friend, the zope error log will tell us about the actual problem. However in this case KSS alsologged us the server side error message.

Server action parameters

Like client actions, server actions can also accept parameters. The parameters will be sent to the serveras form variables. Zope publisher can then pass them as usual keyword parameters to our pythonscript. Let's render a parameter coming from the client. We add parameter mymessage to the python script. Then modify the script:

We modify our kss rule to actually send the parameter from the client:

The key response1-mymessage is built identically to how we did it with the client action. We usethe name of the action first and then, following the dash, the name of the parameter. This time,because we change the stylesheet, we need to reload the page before testing by clicking on the boundnode.

To understand better how all this is working, let's enter a second rule in the kss:

This shows some new things. First, you can see that you can use any css selector in a rule. In this case,the selector will select all globalnav tab links. If you reload the page, you will notice that if you click onany of those links, different content is replaced because different parameter are passed to the server.

If you take a look at the log after the page reload, you can see something like this:

... # add a commandcore = getKSSCommandSet('core')content = '<h1>We did it!</h1><span><b>%s</b> at %s</span>' % (mymessage, DateTime())core.replaceInnerHTML('#portal-siteactions', content)...

a.navTreeCurrentItem:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: "Hello Plone!";}

ul#portal-globalnav li a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: "clicked on global nav";}

Page 12: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

This shows that the second rule is also in effect now. Moreover, it has selected 5 nodes (or howevermany globalnav tabs you have). A lot of other information is also logged, it should not worry you at themoment.

Different command selector

Until now, in our command, we used the default css selector. It is possible to use other types ofselectors, like a html id selector. Let's modify our command in the following way:

What an HTML id selector selects should be obvious. Reload the page and exercise!

Commands can also select multiple nodes:

The CSS selector dt.portletHeader a selects all portlet headers in the page, so the replacementwill be executed not on one node but on many nodes (as can also be seen in the logs). Try clicking theHome link in the navtree, or any of the globalnav tabs to see the effect.

You can also add multiple commands: each of them will be executed, in the order they have within thepayload.

One thing is important to note. If a command selects no nodes, it is not an error: the behaviourdesigned in this case is that nothing happens. This is in line with the usual logic of CSS selectors instyle sheets. Using a different event

So far we have only used the click event: let's try with another one, timeout. The timeout event does not directly map to a browser event but it is a (conceptual) KSS event. This shows that in KSSanything can be an event and how an event binds itself is freely implementable.

Loading KSS engine....Initializing rule sheets.Count of KSS links: 1Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type kssGET http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss (22ms)EventRule #0: a.navTreeCurrentItem EVENT=clickEventRule #1: ul#portal-globalnav li a EVENT=clickFinished loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss resource type kss in 33 + 36 ms.Setup of events for document starts.Using original cssQuery.EventRule [#0-@@0@@click] selected 1 nodes.EventRule [#1-@@0@@click] selected 5 nodes.0 special rules bound in grand total.Instantiating event id [@@0], className [0], namespace [null].6 nodes bound in grand total....

Setup of events for document finished in 52 ms....

... # add a commandcore = getKSSCommandSet('core')content = '<h1>We did it!</h1><span><b>%s</b> at %s</span>' % (mymessage, DateTime())selector = core.getHtmlIdSelector('portal-personaltools')core.replaceInnerHTML(selector, content)...

... # add a commandcore = getKSSCommandSet('core')content = '<h1>We did it!</h1><span><b>%s</b> at %s</span>' % (mymessage, DateTime())selector = core.getCssSelector('dt.portletHeader a')core.replaceInnerHTML(selector, content)...

Page 13: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

Let's add the following rule to the end of our kss file (altogether we will have 3 rules then):

The timeout event implements a recurring timeout tick. It has a delay parameter that specifies the time in milliseconds. In this case, the event will be triggered each 8 seconds over and over again. Theevent calls the server action that we already have but with a different parameter.

The timeout event does not really need a node as binding scope. This is why we use document insteadof a CSS selector as we did until now. This is a special KSS selector that is an extension to css andsimply means : bind this event exactly once when the document loads, with a scope of no nodes butthe document itself.

If you reload the page you will notice that clicks work as before, however, every 8 seconds, the timeoutevent will trigger and do a replacement on the required nodes.

There are some more advanced issues that this example opens and we will show more about them inthe next tutorials.

Congratulations!

You have completed your first KSS tutorial, learned the basics and now you are able to start someexperimentation on your own. Or, just sit back and relax.

Server-side commands - the zope3 way

A python script may not be the most proper implementation of a server method. Plone community ismoving towards zope3 style development: the suggested way is to use a browser view (multiadapter).Previously, you have created a demo product, now create a python module demoview.py in theproduct root directory on the filesystem:

We inherit our view from KSSView. It further inherits from Five's BrowserView.

It is maybe time to explain how the ttwapi uses those views.

startKSSCommands does the instantiation of a KSSView.getKSSCommandSet is the call equivalent to self.getCommandSet.renderKSSCommands calls self.render(), which actually returns the KSScommands payload to the client. The self.render() is implicitly arranged on return of methods decorated with @kssaction. This is why we do not need to finish our method that

document:timeout { evt-timeout-delay: 8000; action-server: response1; response1-mymessage: "from timeout";}

from kss.core import KSSView, kssactionfrom datetime import datetime

class DemoView(KSSView):

@kssaction def response1(self, mymessage): # build HTML content = '<h1>We did it!</h1><p><b>%s</b> at %s</p>' date = str(datetime.now()) content = content % (mymessage, date) # KSS specific calls core = self.getCommandSet('core') core.replaceInnerHTML('#portal-siteactions', content)

Page 14: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

way.

To be able to use the server side method, you need to add the following to your configure.zcmlfile:

The interface that the view is bound to is one setup by kss.core on all portal objects. You couldalso use directly the interfaces defined by Plone 2.5 directly if you wanted, however the advantage ofusing the marker interface defined by plone.app.kss.interfaces, like in the above example,is that the same code will work in older Plone versions as well.

If you still have the response1 python script from the begin of this tutorial, do not forget to renameit. Now it is time to restart Zope. If everything goes well, the page functions as previously but you cansee from the replacement message that the new method is operating on your page.

Remember, when you are working with browser views, you must restart Zope each time you want totest the changes made in the method code come.

Accessing DOM informationIn this lesson we will learn how we can access information from our page and pass it tothe server actions. We will also familiarize ourselves with the most common errors weencounter with parameters passing, and learn some useful debugging tricks.

In our previous lessons we learned how to use KSS to invoke a server action and how to pass differentparameters to them. To start with, we will further improve the code that we used in the previousexamples. You can import this code from the ZMI: go to your portal_skins, delete or remove the custom folder, and import the code. If you have just finished the previous lesson, you do not need todo this. Also remember to disable the kss files already registered from the installed products and addyour new kss file into portal_css tool, as described earlier. Preface

Or, why is this important at all?

We have the following rule in our kss resource:

This rule, combined with the response1 method, writes a message in the upper right corner of thepage, if in the navigation portlet we click to the line representing our current folder location. (For theportal home page, this will be the "Home" line.)

This works fine. We also learned how we can invoke the same server action with different parameters,resulting in different display texts in the result. Now we would like to bind the rule to all the navtreeitems. After having looked in the FireBug inspector we can see that the corresponding css selector willbe li.navTreeItem a. Change the rule accordingly. (Do not make a duplicate for reasons to be

<browser:page for="plone.app.kss.interfaces.IPortalObject" class=".demoview.DemoView" attribute="response1" name="response1" permission="zope2.View" />

a.navTreeCurrentItem:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: "Hello Plone!";}

Page 15: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

described later.):

Save the rules and reload your page. If done right, you can see in the log that the rule is now correctlybinding to the required number of nodes (for example, I have 2 items appearing in the navtree):

Also you can check that clicking of any of the navtree items will activate the replacement.

Now, I would like to see in the result, which line I clicked on. However I would then need to passdifferent parameters depending on the node. If I programmed in javascript, I would look up thecorrect information from the DOM. How can I do this with KSS? Obviously making 4 differenteventrule is not an option, as the point is exactly to have one matching rule.

The solution is using value provider functions that can be specified instead of the strings. The functionsmay take a number of parameters, and based on this they look up some information from the page.This results in dynamic parameter values passed to the remote request. As an example. consider wewant to pass the href attribute of the clicked node. Then we modify the rule as follows:

Reload the page to see the effect!

Overview of the builtin value providers

The KSS core provides all the value providers that are useful to cover most of the use cases. Inaddition to the parameters you must supply to them, they operate on the node selected by the event.The most important ones are:

Fetching content

Our first objective is to be able to access the html content of the node, most of all the html attributes.

nodeAttr(attrname)Produces the value of a given HTML attribute of the selected node.

nodeContent()

This produces the textual content of the node. We do not encourage using this, as weshould use these functions for identifying the node on which the event happened,however it may come handy for debugging or for some cases - like in our followingexample.

kssAttr(attrname)

This is for reading (namespace) attributes holding information for kss only, and otherwise

li.navTreeItem a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: "Hello Plone!";}

EventRule [#0-@@0@@click] selected 2 nodes.

li.navTreeItem a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: nodeAttr("href");}

Page 16: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

irrelevant for the HTML content. We will talk about this later.

Fetching form data

The second important task is to fetch form values from the page. The following functions fetch a singleform variable:

formVar(formname, varname)

Produces the value of a given variable within a given form.

currentFormVar(varname)

Produces the value of a given variable within the current form, which is the one in whichthe selected node is. The parameter varname is optional, and if it is ommitted, the currentnode will be used (in this case the node must be a form variable itself).

Note:

varname has to be a form variable exactly as it appears on the form. There is nosemantic interpretation of the name. Therefore, multiform zope variables with the prefix:list, :record and :records cannot be fetched this way: they are not assembledas they semantica would require it.

To handle zope multiform variables, currently the only way is to acquire all formvariables in an entire form, as described below.

For a complete and updated list, you can check the cheatsheet. All these commands are implementedin the kukit core and the rule is to keep them simple: if you need something more complicated, then aplugin extension will be needed. However the current use cases are all covered by the existing set.

Acquiring an entire form

The kssSubmitForm special action parameter can be used to pass an entire form to a server action.The prefix "kss" in the name signifies that this is a special parameter ( - consequently ordinaryparameters are prohibited to start with the prefix "kss"). kssSubmitForm cannot be used with client actions.

The following value provider functions can be used after kssSubmitForm:

form(formname)

Provides the values of all the variables in a given form.

currentForm()

Provides the values of all the variables in the form that contains the current node.

In both cases, all the variables in the given form will be marshalled to the server. The server action canreceive the parameters as it would receive them in the case of a normal server side method (theZPublisher marshalls parameters that appear in the method signature, and in addition the variablesarealso accessible on request.form as usual.

Since there is no mangling of the variable names, Zope multiform variables of the type :list, :record, :records will work as expected.

Remark:

Page 17: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

In the earlier versions of KSS, these value providers were also usable in normalparameters. This usage is now deprecated.

Combining more parameters, and encoding

Let us have some more useful information displayed as a result. First let us pass 3 parameters: a string(like earlier), the current nodeAttr function, combined with nodeValue that yields the textual value ofthe node, in this case the visible title of the navtree item:

We also need to change the response1 python scrript to accomodate the new parameters:

We also need to change the parameter list of the string to: "mymessage, href, value".

Let's click on the different lines in the navtree and enjoy the results. However I am using aninternationalized site, and when I click on any line where there are non-ascii characters in the foldername, I receive an error in the logs:

We can also see the full traceback in the Zope event log:

li.navTreeItem a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: 'clicked on navtree item'; response1-href: nodeAttr("href"); response1-value: nodeContent();}

# import Through-The-Web(TTW) APIfrom kss.core.ttwapi import startKSSCommandsfrom kss.core.ttwapi import getKSSCommandSetfrom kss.core.ttwapi import renderKSSCommands

# start a view for commandsstartKSSCommands(context, context.REQUEST)

# use the parameterstxt = '<h1>We did it!</h1><span><b>%s</b> at %s</span>' % (mymessage, DateTime())txt += '<br><span>href=<b>"%s"</b>' % (href, )txt += '<br><span>value=<b>"%s"</b>' % (value, )

# add a commandcore = getKSSCommandSet('core')core.replaceInnerHTML('#portal-siteactions', txt)

# render the commandsreturn renderKSSCommands()

Request failed at url http://127.0.0.1:8080/demo11/folder1/page2/response1, rid=0, server_reason="KSSUnicodeError: Content must be unicode or ascii string, original exception: 'ascii' codec can't decode byte 0xc3 in position 201: ordinal not in range(128)"

2007-10-08 15:24:56 ERROR Zope.SiteErrorLog http://127.0.0.1:8080/demo11/folder1/page2/response1Traceback (innermost last): Module ZPublisher.Publish, line 119, in publish Module ZPublisher.mapply, line 88, in mapply Module ZPublisher.Publish, line 42, in call_object Module Shared.DC.Scripts.Bindings, line 313, in __call__ Module Shared.DC.Scripts.Bindings, line 350, in _bindAndExec Module Products.PythonScripts.PythonScript, line 327, in _exec Module None, line 16, in response1 - <PythonScript at /demo11/response1 used for /demo11/folder1/page2> - Line 16 Module kss.core.plugins.core.commands, line 32, in replaceInnerHTML Module kss.core.parsers, line 74, in __init__ Module kss.core.unicode_quirks, line 27, in force_unicodeKSSUnicodeError: Content must be unicode or ascii string, original exception: 'ascii' codec can't decode byte 0xc3 in position 201: ordinal not in range(128)

Page 18: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

The explanation is this. When we build up the commands, the basic rule is that the supplied commandparameters must be either unicode or plain ascii. We do not care autoconverting anything else: KSS has noinformation of what encoding is used in a string, and we have learned by now that magic can do moreharm than help. So we must do explicit conversion of the parameters:

The force_unicode method converts the input to unicode, from the encoding given as a secondparameter. Why did we use utf? Actually KSS is handling charsets properly on the client side, andpasses the parameters with utf encoding. The ZPublisher of Zope2 is dumb enough to marshall this toparameters as utf-8 encoded (non-unicode) strings. Hence the need for the conversion. On the otherhand the twisted publisher of Zope3 is passing proper unicode values. So, if the force_unicode methodmeets an unicode, it just lets it pass. This is a way of thinking about future compatibility: we target towrite code usable on Zope3 as is.

Notice that to have the server side change in effect there was no need to reload the page in the browser!This is one convenience: the browser needs a reload only if we made changes in the client side code,in this case, the KSS.

Missing parameters

This works fine now. But we used to have a second KSS rule too (inherited from the previous lesson):

This comes into effect when we click on the global navigation tabs.

But now, this gives an error in the loggingpane. Turning to the Zope log we see a traceback:

What happened here? One of the mandatory parameters were missing for the response1 method.solution: we need to change the list of parameters to mymessage, href=None, value=None. In addition we need to make the call to force_unicode conditional, and then for niceity, also make thetext output better:

from kss.core import force_unicodevalue = force_unicode(value, 'utf')

ul#portal-globalnav li a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: "clicked on global nav";}

2007-10-08 15:36:09 ERROR Zope.SiteErrorLog http://127.0.0.1:8080/demo11/folder1/page2/response1Traceback (innermost last): Module ZPublisher.Publish, line 119, in publish Module ZPublisher.mapply, line 83, in mapply Module ZPublisher.Publish, line 47, in missing_name Module ZPublisher.HTTPResponse, line 694, in badRequestErrorBadRequest: <h2>Site Error</h2> <p>An error was encountered while publishing this resource. </p> <p><strong>Invalid request</strong></p>

The parameter, <em>href</em>, was omitted from the request.<p>Make sure to specify all required parameters, and try the request again.</p> <hr noshade="noshade"/>

Page 19: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

When we are at debugging, let's look at some other important cases as well.

Nonexistent node attributes

What would happen if nodeAttr wants to acquire an attribute that does not exist on the node? To testthat, change corresponding line in kss to:

If we test it (this time don't miss to reload the page in the browser), it works well and we see the hrefinformational line omitted from the output. This means that if there is a nonexistent value to bepassed, the parameter will be missing from the request.

If we had not put the None default value to the href parameter in the script, we would have receivedagain the "parameter missing" error in Zope. Lucky we were smart in advance... In fact this is animportant issue. The reason for the implementation is, that we do not have a representation of thevalue "None" that is passable to the ZPublisher as a method. So we have chosen to omit the value inthis case, but this makes the default value for the parameter inevitable.

As a second annoyance, currently we have no cross-browser compatible implementation that coulddistinguish between a nonexistent attribute and an attribute that equals to an empty string. So we takethe latter case to be the same as a nonexistent attribute.

We might polish this implementation in the future.

Before continuing, don't forget to change back the kss line as used to be originally.

No error in zope event log

Sometimes it may happen that we see a request failed error in the loggingpane, but we see nothing inthe Zope event log. One way to simulate this easily is to call a server action that does not have acorresponding server method. This would give NotFound and would not be logged by default. Oneway to try this is to temporarily remove the response1 method and see what happens. We could ofcourse enable logging for this exceptions too, but we have an alternate way that gives us more control.

This is the time to look at the actual communication between client and server. There are two ways forthis: use the firebug extension of FireFox with the show XMLHttpRequests option switched on, or usea proxy application like tcpwatch of zope3 that can be used to tap the full client-server communication.It is advisable to get acquainted with both methods but for its simplicity we use FireBug now.

If we look into the response of the failing request we can see that instead of the kukit commandresponse we received a normal Plone error page. Inside the long long page we will see the actual errormessage.

Congratulations! You can move to the next part at this point, or optinally, have a look at the nextchapter.

...if value is not None: value = force_unicode(value, 'utf')

txt = '<h1>We did it!</h1><span><b>%s</b> at %s</span>' % (mymessage, DateTime())if href is not None: txt += '<br><span>href=<b>"%s"</b>' % (href, )if value is not None: txt += '<br><span>value=<b>"%s"</b>' % (value, )...

response1-href: nodeAttr("hrefXXX");

Page 20: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

Doing it the Zope3 way (optional)

If you are familiar with Zope3 style development, you can easily transform the code into a method in abrowserview yourself. Enter the file demoview.py into the product root in the filesystem:

As usual, you also need to add the following to your configure.zcml:

In the next tutorial lesson we look at some more usage of parameter producer functions.

Accessing DOM information, part 2This is the continuation of the previous lesson, with more complexity involved.

In the previous lesson we learned how to use parameter producer functions with KSS, and faced withthe most common issues in dealing with parameters on the server side. We continue where we finished.Merging (or cascading) rules again

Originally we had a single kss rule matching the current node in the navtree only. Now we have a rulethat matches all navtree nodes. Would there be a way to have both ? Consider entering a second kssrules after the current one:

After reloading the page we see this section in the kss console logs:

from kss.core import KSSViewfrom kss.core import force_unicodefrom datetime import datetime

class DemoView(KSSView):

def response1(self, mymessage, href=None, value=None):

if value is not None: value = force_unicode(value, 'utf')

txt = '<h1>We did it!</h1><span><b>%s</b> at %s</span>' % (mymessage, DateTime()) if href is not None: txt += '<br><span>href=<b>"%s"</b>' % (href, ) if value is not None: txt += '<br><span>value=<b>"%s"</b>' % (value, )

# KSS specific calls core = self.getCommandSet('core') core.replaceInnerHTML('#portal-siteactions', txt) return self.render()

<browser:page for="plone.app.kss.interfaces.IPortalObject" class=".demoview.DemoView" attribute="response1" name="response1" permission="zope2.View" />

li.navTreeItem a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: 'clicked on navtree item'; response1-href: nodeAttr("href"); response1-value: nodeContent();}

a.navTreeCurrentItem:click { response1-mymessage: 'clicked on the CURRENT navtree item';}

Page 21: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

What happens here? Eventrule #0 matches up with 2 nodes as previously. Eventrule #1 matches upwith 1 node - however this node is one of the four nodes rule #0 has matched to. What happens? Rule#1 would not make much sense in itself, but in this case the two rules will merge on the commonnode. We can also see from the log that this happens.

The result will be as expected (for those that are familiar with CSS cascading): if we click on the currentnavtree node, the text from rule #1 will be displayed on the screen.

There are a few important things to notice.

Rule lines are merged similarly to css.Merging is not done based on the selectors but on the nodes that the selectors actually select.So, merging depends on the content on which the rules are matched.Order is important: consequent rules override the earlier rules.Different eventnames do not match, for example a :click event and a :timeout event will not bemerged.Actions with different names will be added after each other (as an example, place an alert inrule #1), actions with the same name will merge to one action with all their parameters merged.

KSS attributes (HTML markup)

As the next task, we want to extend our navtree so that we can also access the UID of the folder. Thefirst thing that comes to our mind is that we could use another node attribute to hold this information.However, there is a problem. We could use the id attribute of the node but we may want to keep thatfor different purposes (besided, a node can have only a single id.) We could use a different attribute butusing an attribute unknown to HTML would result in an invalid HTML content, even if the browserwould not shout against it. So we want to be able to attach attributes to the nodes when we generate thetemplate, and we want them to be accessible from KSS, but we want it to be invisible for HTML.

As a solution, namespace attributes could be used. We could make attributes likekukit:attrname="attrvalue". Unfortunately we cannot use this in Plone. Because Ploneuses transitional XHTML at the moment and this would only be valid in real XHTML with mimetypetext/xhtml.

To explore this feature, let us create a customized version of the portlet_navtree_macro. In this case,do not copy the template code from here, but make the necessary changes on your customized versioninstead.

We will path the information "Is this item the current item?" to our server action. That is alreadyavailable at the desired point of the template in a variable called is_current.

The changes need to be done on the filesystem, because plone portlets cannot be customized through

...Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type kssGET http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss (36ms)EventRule #0: li.navTreeItem a EVENT=clickEventRule #1: a.navTreeCurrentItem EVENT=clickEventRule #2: ul#portal-globalnav li a EVENT=clickFinished loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss resource type kss in 667 + 80 ms.Setup of events for document starts.Using original cssQuery.EventRule [#0-@@0@@click] selected 2 nodes.Merged rule [0,1-@@0@@click].EventRule [#1-@@0@@click] selected 1 nodes.EventRule [#2-@@0@@click] selected 5 nodes.0 special rules bound in grand total.Instantiating event id [@@0], className [0], namespace [null].7 nodes bound in grand total.Setup of events for document finished in 69 ms....

Page 22: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

the ZMI in Plone 3. Edit the fileplone/app/portlets/portlets/navigation_recurse.pt in lib/python of yourinstance:

Note:

You may need to restart Zope after you have made changes on the filesytem, to have yourchanges rendered.

Look at the emphasized part. What we do is that instead of using real namespace attributes, we use anemulation that stores attributes into the html class attribute. The advantage is that we can have moreattributes encoded into the same class after each other, and even preserve the existing classes on thenode. The class entry must have the form of kssattr-key-value. The kssattr- prefix assures that we recognize the entry and do not mix with real classes.

We only added the kssattr at the end of the existing classes on the node and did not change anythingelse on the template. After rendering the page we can see that all the navtree nodes have kss attributes,for example consider the following rendered html section:

To access this attributes from KSS, we use the kssAttr value provider. This works very similarly tonodeAttr, only in this case it looks for the special kss parameters, and it is able to understand theencoding of kss attributes into classes, as described above.

To use it, let us extend our kss rules in the following way:

And, expand our response1 script too. Also add iscurrent=None to the end of the parameter list:

...<div tal:define="item_class python:is_current and item_class + ' navTreeCurrentItem' or item_class"> <a tal:attributes="href python:link_remote and remote_url or item_url; title node/Description; class string:$item_class kssattr-iscurrent-${is_current}">

<img tal:replace="structure item_icon/html_tag" /> <span tal:replace="node/Title">Selected Item Title</span> </a> </div>...

<li class="navTreeItem visualNoMarker"> <div> <a class="state-private kssattr-iscurrent-False" title="" href="http://l12/demo11/folder1/page1"> </a> </div></li>

li.navTreeItem a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: 'clicked on navtree item'; response1-href: nodeAttr("href"); response1-value: nodeContent(); response1-iscurrent: kssAttr(iscurrent);}

a.navTreeCurrentItem:click { response1-mymessage: 'clicked on the CURRENT navtree item';}

Page 23: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

Let us test the results now. All navtree items now print if they are current or not (except for Homewhich comes from a different template with which we did not deal now).

Homework:

Modify the navtree.pt template too, to make the information available on the"Home" folder as well. This will only have a visible result if the navtree portlet becomesenabled on the portal root, too.

Starting on this path, we could arrive to an ajaxified navtree portlet. We do not do that now, howeverwe can conclude this. The ajaxification of existing components may require the customization oftemplates. The customization can be done by attaching simple KSS attributes to the required nodes.Why do we need to do this? For the same reason we need class attributes in order to make css work.

Accessing form variables

Another common task is to access the form variables in the page We need this if we want to implementin-place field validation, for example. When we click out of a field, we want to execute a server action.

We will use the portal front page's edit view for our experiment. This contains a stringfield (the title), atextarea field (the description), and a rich widget (text) controlled by the kupu editor as default. We willstart only with the title now.

Note:

Although I asked you in the first tutorial, I find it important to stress it again at this point:Before going on with this example, make sure again that Plone's builtin plone.kss and at.kss resources are made inactive. This is important before a similar functionality isalready implemented in Plone, and not disabling it would contradict with our example,confusing the results.

Let us use FireBug to look at the html part responsible for rendering the title field:

The CSS rule that would bind to the input node is div#archetypes-fieldname-titleinput and the event that we will use is blur. This event is a native browser event and gets triggeredwhen the control looses focus. So let's add the following rule to our kss:

...txt = '<h1>We did it!</h1><span><b>%s</b> at %s</span>' % (mymessage, DateTime())if href is not None: txt += '<br><span>href=<b>"%s"</b>' % (href, )if value is not None: txt += '<br><span>value=<b>"%s"</b>' % (value, )if iscurrent is not None: txt += '<br><span>path=<b>"%s"</b>' % (iscurrent, )...

<div id="archetypes-fieldname-title" class="field ArchetypesStringWidget kssattr-atfieldname-title"> <span/> <label for="title">Title</label> <span class="fieldRequired" title="Required"> (Required) </span> <div id="title_help" class="formHelp"/> <div class="fieldErrorBox"/> <input id="title" class="blurrable firstToFocus" type="text" maxlength="255" size="30" value="Welcome to Plone" name="title"/></div>

Page 24: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

First we use the alert action to see if the event activates. Indeed, if we enter the field and then leave it,the alert popup appears.

After this we can call a real server action. Let us name it validateField and create it as a python script from the custom skin. At first make it just to display a message to the upper right corner just likeour response1 method did:

And change the kss to call it:

This is good so far, but now we want to pass the value of the input form to the field. We have a fewpossibilities for this:

formVar('edit_form', 'title') fetches the value by form name and field name.currentFormVar('title') fetches the value by field name, and from the current form.The "current" form is selected by the node on which the event has been executed, in this casethe input node.currentFormVar() supposes that the node on which the event has been triggered is itselfa form control element, and uses its value.

You can try all the three possibilities, but for our purposes the third one seems to be the best, since theinput node is the same on which the blur event is triggered. So our kss rule will be:

Then we change our validateField script as follows (and we also add value as a parameter to thescript):

div#archetypes-fieldname-title input:blur { action-client: alert;}

# import Through-The-Web(TTW) APIfrom kss.core.ttwapi import startKSSCommandsfrom kss.core.ttwapi import getKSSCommandSetfrom kss.core.ttwapi import renderKSSCommands

# start a view for commandsstartKSSCommands(context, context.REQUEST)

# use the parameterstxt = '<h1>In validation.</h1><span>at %s</span>' % (DateTime(), )

# add a commandcore = getKSSCommandSet('core')core.replaceInnerHTML('#portal-siteactions', txt)

# render the commandsreturn renderKSSCommands()

div#archetypes-fieldname-title input:blur { action-server: validateField;}

div#archetypes-fieldname-title input:blur { action-server: validateField; validateField-value: currentFormVar();}

Page 25: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

We can see now that the script really passes the value that is in the title field, when we leave it. Howwould we validate it? Well, we use Archetypes to do it. This method will return an error in case thefield does not validate, or None if it is all right:

In addition if there is an error, we want the message to appear as a portal status message (orange).(Normally the message would appear as a field status message, but it's ok for us now.) Fortunately this isalready implemented in KSS, so we don't have to implement it ourselves! This can be invoked in thefollowing way:

This calls something we call a command. It can be thought of a complex command but in fact it canemit a more complex series of commands for sending back to the client. It also does other things, likedecides if there is an error at all (and if there is not, it disables the status message), and also does atranslation of the thing.

The full action script will be this in the end:

# import Through-The-Web(TTW) APIfrom kss.core.ttwapi import startKSSCommandsfrom kss.core.ttwapi import getKSSCommandSetfrom kss.core.ttwapi import renderKSSCommandsfrom kss.core.ttwapi import force_unicode

# start a view for commandsstartKSSCommands(context, context.REQUEST)

# use the parametersvalue = force_unicode(value, 'utf')

txt = '<h1>In validation.</h1><span>at %s</span>' % (DateTime(), )txt +='<br><span>value=<b>"%s"</b>' % (value, )

# add a commandcore = getKSSCommandSet('core')core.replaceInnerHTML('#portal-siteactions', txt)

# render the commandsreturn renderKSSCommands()

instance = selffield = instance.getField('title')error = field.validate(value, instance, {})

plone = getKSSCommandSet("plone")plone.issuePortalMessage(error)

Page 26: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

To test it in working, let us clear the title. Leave the field: we see the orange message appear. Entersomething again, leave the field, and see the message disappear. At the same time, we can see the statusupdated in the upper right corner too, since we left that part in as well. For a complete experience, weare happy to observe that we can also click on the navtree items and even to the global tabs! In otherwords: we have not broken our previous programs.

Naturally to implement in-place validation with all the archetypes fields, more things are needed. Mostimportantly, we need a way to be able to bind not only to a single field, but to all the fields (of the sametype) at once. In addition we don't only have text inputs but checkboxes, dropdowns and morecomplex fields like the richtext editor. They all require tedious work on more Archetypes templates.However do not despair: this is already implemented in Plone. To look how it works, you can take adeeper look into the archetypes.kss product, together with the at.kss resource file that is contained by Products/Archetypes.

In a following tutorial we will also learn how to use the kssAttr parameter producer function and asingle node markup thatr becames effective for entire page regions. For now, however, we conclude thecurrent lesson and enjoy the fruit of our efforts. Because by now we have learned what we need toknow if we want to add simple dynamicity to our custom products if we wish to do so.

The Zope export of the final results of this tutorial can be downloaded from here. Before importing,rename your existing custom skin to avoid conflicts. Also after importing if the modified templatebreaks for you, you might want to customize the template from the exact Plone version you use, insteadof the one supplied here.

Doing it the Zope3 way (homework)

We leave this as homework for the kind Reader. One useful hint: in the script we used instance =context, and now in the view we have to use:

The aq_inner is in some cases necessary to provide the exactly right acquisition context for theArchetypes scripts.

# import Through-The-Web(TTW) APIfrom kss.core.ttwapi import startKSSCommandsfrom kss.core.ttwapi import getKSSCommandSetfrom kss.core.ttwapi import renderKSSCommandsfrom kss.core.ttwapi import force_unicode

# start a view for commandsstartKSSCommands(context, context.REQUEST)

# use the parametersvalue = force_unicode(value, 'utf')

txt = '<h1>In validation.</h1><span>at %s</span>' % (DateTime(), )txt +='<br><span>value=<b>"%s"</b>' % (value, )

# add a commandcore = getKSSCommandSet('core')core.replaceInnerHTML('#portal-siteactions', txt)

# do validation and send back resultinstance = contextfield = instance.getField('title')error = field.validate(value, instance, {})plone = getKSSCommandSet("plone")plone.issuePortalMessage(error)

# render the commandsreturn renderKSSCommands()

instance = self.context.aq_inner

Page 27: BaláZs Ree   Introduction To Kss, Kinetic Style Sheets

Also when we use the commandset adapters, the parameter for the interface that does the adaptationshould be self, not commands (exactly how we did in the previous lessons).