SlideShare une entreprise Scribd logo
1  sur  27
Télécharger pour lire hors ligne
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
anything, logs will appear directly in the FireBug console. If you use Safari, logging also appears in the
Safari console. If these possibilities are not available for you, you have two other choices: either use
Firebug 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 into
the 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, you
need 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 original
page.

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

javascript:kukit.showLog();



Remark:

      This needs to be repeated each time the page gets reloaded, because each time a page is
      loaded, 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 on
reloading a page. They will begin this way:

Loading KSS engine.
...



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

The text following the first line may indicate that KSS installed more eventrules on its own. This would
not 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 to
disable this. Go to the ZMI in the portal root, enter the tool plone_kss and unselect the checkbox
left 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:
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: 0
Setup of events for document starts.
0 special rules bound in grand total.
0 nodes bound in grand total.



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

If the Count of KSS links is not zero, you need to return to the previous steps and make the
remaining 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 - even
the syntax is almost valid CSS - but it has different semantics and it controls how KSS events are
bound to your page elements.

To do it simplest, create a file named tutorial1.kss in your custom skins directory. To make the file
bound to each page in the portal, we need to make it known by ResourceRegistries. Go to the ZMI in
the 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 not
        forget to enable Debugging Mode on the top of the same page. This will make the resource
        debuggable, even if the production settings are applied for compression, caching and
        merging.
After pressing Add, navigate back to your portal frontpage, and reload it. You should see the following
messages:

Loading KSS engine.
...
Initializing rule sheets.
Count of KSS links: 1
Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type
GET 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 resour
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.
...



This means that your kss file is active on the page, although it is yet not doing anything. At this point
you 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. From
the point of view of KSS this is all the same but this leads to a cleaner development style, better
supported in the future versions of Plone. Do not attempt to do this however if you do not know at
least a little of Five, because in this tutorial we will not teach you Five itself, we only show the way to
use it for KSS. Remember: zope3 development style is easy once you have learned it, but one might
feel 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. In
the product directory on the filesystem, create a browser subdirectory and save the kss file there with
the name tutorial1.kss. Then in the product root create a configure.zcml file with the following
content:

<configure xmlns=quot;http://namespaces.zope.org/zopequot;
   xmlns:browser=quot;http://namespaces.zope.org/browserquot;>

  <!-- the kss resource -->

  <browser:resource
     file=quot;browser/tutorial1.kssquot;
    name=quot;tutorial1.kssquot;
    />

</configure>



After this you need to restart zope: if you have made an error in this file you see it immediately as zope
will refuse to start at all. If zope has started up, then check that the url
http://127.0.0.1:8080/++resource++tutorial1.kss returns the kss file correctly
from 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 the
portal_kss tool. After making this change you should reload your page from the browser, open the
kss debug console and see the log messages like above.

In addition, since now you have a separate tutorial product, you could also make a GenericSetup
profule that automatically registers the kss resource with the kss tool. To have an example of this, you
may 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
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 the
following basic structure, that consists of a folder and three pages in it:

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



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 is
disabled for unauthenticated users. Since you just created the above structure, this should already be
true.

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 the
dynamic behaviour of your page. A KSS rule looks similarly to a CSS rule. There is a selector part that
selects one or more html nodes just like in CSS, then there is an event descriptor that is attached to the
CSS selector with the comma. We will use the click event that will trigger when the selected node or
nodes are clicked with the mouse.

First we decide which nodes we bind to. This can be any selectable element in the page. Let us first
bind 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:

<a title=quot;quot; href=quot;http://127.0.0.1:8080quot;
          class=quot;navTreeCurrentItem visualIconPaddingquot;>
  Home
</a>



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

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



Notice that after the selector we put :click, this tells KSS what event to use and is always obligatory
in the KSS rule. Between the braces we have various properties that control what actions are bound to
the selected nodes, and also the parameters for this actions. This rule says that when the event gets
triggered, we do not want to communicate with the server yet, but simply pop up an alert box. This is
just 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:
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: 1
Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type
GET http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss (52ms)++resource++kukit... (line
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 resour
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.



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 enabled
      Debugging in the portal_kss tool. If this is not done, the resource is most likely got cached
      where it should not be.

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




This tells us that the event has triggered in order, and also gives some additional information, like the
number 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 we
would 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 in
line with how css behaves too. This is what allows us to put a lot of rules into the same kss file. No
need to set up conditions: if the selected nodes are not present in the current page, simple nothing
happens.

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 error
appears and we can see the followings in the console log:
[Exception... quot;'Error parsing KSS at
http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss :
ParsingError: Undefined event : [null:klick].' when calling
method: [nsIDOMEventListener::handleEvent]quot; nsresult:
quot;0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)quot; location:
quot;<unknown>quot; data: no]



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

Note:

        The null in [null:klick] means that the event is in the global namespace. Foe
        events 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 parameters
with the event binding. In the previous example after we closed the alert, the page reloaded because the
link was followed by default. The click event has a parameter preventdefault which can be used to
override this. The modified rule will be:

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



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

        evt says this is an event binding parameter
        click 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 ones
have 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 is
otherwise something that the browser wanted to do on the click, before we started to modify its
behaviour.

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 be
followed. Specify an action parameter

We can also specify parameters to the actions themselves. The main difference between the event
binder 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 we
clicked on the link). As was the case with the event parameters, it is entirely up to the given action to
decide what parameters it accepts and handles. Now, the alert action just happens to accept a
message parameter, so we extend our rule with one line:

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



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 on
the right side: this is only necessary if the value is not a single word. Both single and double quotes can
be 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, with
debug 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 your
changes 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 save
yourself from a lot of headache.

You have learned the basics of setting up kss rules so far, but nothing that would really do anything so
far. 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 2
We will continue and learn how to create server actions and specify their execution from
our event rules.

.

This tutorial is the direct continuation of the first part. We will learn how to specify server side actions
and 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 was
triggered. 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 the
business logic, the task of the server method is to assemble a sequence of commands. These
commands are marshalled back to the client to be executed there. A command is a call to DOM
manipulation client code. A command can (in most cases, should) have a selector, to set the scope of
nodes 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 a
custom one since it requires only python skills. Let's create a python script (response1) in the
custom skin with the following content:
# import Through-The-Web(TTW) API
from kss.core.ttwapi import startKSSCommands
from kss.core.ttwapi import getKSSCommandSet
from kss.core.ttwapi import renderKSSCommands

# start a view for commands
startKSSCommands(context, context.REQUEST)

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

# render the commands
return renderKSSCommands()



After the imports, we initialize the view that will hold the KSS commands that we want to send back to
the 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 html
string.

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

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

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

<!DOCTYPE html PUBLIC quot;-//W3C//DTD XHTML 1.0 Transitional//ENquot;
        quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtdquot;>
<html xmlns=quot;http://www.w3.org/1999/xhtmlquot;
       xmlns:kukit=quot;http://www.kukit.org/commands/1.0quot;><body>
<kukit:commands>
<kukit:command selector=quot;#portal-siteactionsquot;
                 name=quot;replaceInnerHTMLquot; selectorType=quot;quot;>
     <kukit:param name=quot;htmlquot;><h1>We did it!</h1></kukit:param>
</kukit:command>
</kukit:commands>
</body></html>



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 given
parameters.

Calling the server action

Now, we have finished to build our server action; we want to call it from our kss style sheet. We replace
our previous KSS event rule with the following one:
a.navTreeCurrentItem:click {
     evt-click-preventdefault: True;
     action-server: response1;
}



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
quot;Homequot; line in the navtree portlet. It works! We can see the site actions replaced with our text. Also
notice that a few things have been logged to the kss console:

RequestManager notifies server http://127.0.0.1:8080/demo11/folder1/page1/response1, rid=0 (RQ: 1 OUT, 0 WA
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...
...



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:

...
from DateTime import DateTime

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



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

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 server
restarts.

What happens if the server-side script has an error, or the client does not get a correct response for
some 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=quot;ImportError: import of quot;DateTimeXquot; from quot;DateTimequot; is
           unauthorized. The container has no security assertions. Access to 'DateTimeX' of
           (module 'DateTime' from
           '/usr/local/lib/Zope2.10/lib/python/DateTime/__init__.pyc') denied.quot;

The error Request failed indicates that we have to turn to the server to debug the problem. Our
best friend, the zope error log will tell us about the actual problem. However in this case KSS also
logged 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 server
as form variables. Zope publisher can then pass them as usual keyword parameters to our python
script. Let's render a parameter coming from the client. We add parameter mymessage to the python
script. Then modify the script:

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



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

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



The key response1-mymessage is built identically to how we did it with the client action. We use
the 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 bound
node.

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

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



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 on
any 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:
Loading KSS engine.
...
Initializing rule sheets.
Count of KSS links: 1
Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type
GET http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss (22ms)
EventRule #0: a.navTreeCurrentItem EVENT=click
EventRule #1: ul#portal-globalnav li a EVENT=click
Finished loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss resour
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.
...



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

Different command selector

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

...
# add a command
core = 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)
...



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

Commands can also select multiple nodes:

...
# add a command
core = 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)
...



The CSS selector dt.portletHeader a selects all portlet headers in the page, so the replacement
will be executed not on one node but on many nodes (as can also be seen in the logs). Try clicking the
Home 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 the
payload.

One thing is important to note. If a command selects no nodes, it is not an error: the behaviour
designed in this case is that nothing happens. This is in line with the usual logic of CSS selectors in
style 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 KSS
anything can be an event and how an event binds itself is freely implementable.
Let's add the following rule to the end of our kss file (altogether we will have 3 rules then):

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



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. The
event 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 instead
of a CSS selector as we did until now. This is a special KSS selector that is an extension to css and
simply means : bind this event exactly once when the document loads, with a scope of no nodes but
the document itself.

If you reload the page you will notice that clicks work as before, however, every 8 seconds, the timeout
event 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 in
the next tutorials.

Congratulations!

You have completed your first KSS tutorial, learned the basics and now you are able to start some
experimentation 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 is
moving 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 the
product root directory on the filesystem:

from kss.core import KSSView, kssaction
from 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)



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 KSS
      commands 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
way.

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

<browser:page
      for=quot;plone.app.kss.interfaces.IPortalObjectquot;
      class=quot;.demoview.DemoViewquot;
      attribute=quot;response1quot;
     name=quot;response1quot;
      permission=quot;zope2.Viewquot;
     />



The interface that the view is bound to is one setup by kss.core on all portal objects. You could
also use directly the interfaces defined by Plone 2.5 directly if you wanted, however the advantage of
using 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 rename
it. Now it is time to restart Zope. If everything goes well, the page functions as previously but you can
see 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 to
test the changes made in the method code come.


    Accessing DOM information
In this lesson we will learn how we can access information from our page and pass it to
the server actions. We will also familiarize ourselves with the most common errors we
encounter 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 different
parameters to them. To start with, we will further improve the code that we used in the previous
examples. 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 to
do this. Also remember to disable the kss files already registered from the installed products and add
your 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:

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



This rule, combined with the response1 method, writes a message in the upper right corner of the
page, if in the navigation portlet we click to the line representing our current folder location. (For the
portal home page, this will be the quot;Homequot; 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 navtree
items. After having looked in the FireBug inspector we can see that the corresponding css selector will
be li.navTreeItem a. Change the rule accordingly. (Do not make a duplicate for reasons to be
described later.):

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



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

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



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 pass
different parameters depending on the node. If I programmed in javascript, I would look up the
correct information from the DOM. How can I do this with KSS? Obviously making 4 different
eventrule 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 functions
may 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 we
want to pass the href attribute of the clicked node. Then we modify the rule as follows:

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



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. In
addition 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 we
       should 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 following
       example.

kssAttr(attrname)

       This is for reading (namespace) attributes holding information for kss only, and otherwise
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 single
form 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 which
        the selected node is. The parameter varname is optional, and if it is ommitted, the current
        node 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 no
        semantic interpretation of the name. Therefore, multiform zope variables with the prefix
        :list, :record and :records cannot be fetched this way: they are not assembled
        as they semantica would require it.

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

For a complete and updated list, you can check the cheatsheet. All these commands are implemented
in the kukit core and the rule is to keep them simple: if you need something more complicated, then a
plugin 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 quot;kssquot; in the name signifies that this is a special parameter ( - consequently ordinary
parameters are prohibited to start with the prefix quot;kssquot;). 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 can
receive the parameters as it would receive them in the case of a normal server side method (the
ZPublisher marshalls parameters that appear in the method signature, and in addition the variablesare
also 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:
In the earlier versions of KSS, these value providers were also usable in normal
      parameters. 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 of
the node, in this case the visible title of the navtree item:

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



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

# import Through-The-Web(TTW) API
from kss.core.ttwapi import startKSSCommands
from kss.core.ttwapi import getKSSCommandSet
from kss.core.ttwapi import renderKSSCommands

# start a view for commands
startKSSCommands(context, context.REQUEST)

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

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

# render the commands
return renderKSSCommands()



We also need to change the parameter list of the string to: quot;mymessage, href, valuequot;.

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

Request failed at url http://127.0.0.1:8080/demo11/folder1/page2/response1,
     rid=0, server_reason=quot;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)quot;



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

2007-10-08 15:24:56 ERROR Zope.SiteErrorLog http://127.0.0.1:8080/demo11/folder1/page2/response1
Traceback (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_unicode
KSSUnicodeError: Content must be unicode or ascii string, original exception: 'ascii' codec can't decode by
The explanation is this. When we build up the commands, the basic rule is that the supplied command
parameters must be either unicode or plain ascii. We do not care autoconverting anything else: KSS has no
information of what encoding is used in a string, and we have learned by now that magic can do more
harm than help. So we must do explicit conversion of the parameters:

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



The force_unicode method converts the input to unicode, from the encoding given as a second
parameter. Why did we use utf? Actually KSS is handling charsets properly on the client side, and
passes the parameters with utf encoding. The ZPublisher of Zope2 is dumb enough to marshall this to
parameters as utf-8 encoded (non-unicode) strings. Hence the need for the conversion. On the other
hand the twisted publisher of Zope3 is passing proper unicode values. So, if the force_unicode method
meets an unicode, it just lets it pass. This is a way of thinking about future compatibility: we target to
write 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):

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



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:

2007-10-08 15:36:09 ERROR Zope.SiteErrorLog http://127.0.0.1:8080/demo11/folder1/page2/response1
Traceback (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 badRequestError
BadRequest:   <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 parameter
  <hr noshade=quot;noshadequot;/>



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 the
text output better:
...
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>quot;%squot;</b>' % (href, )
if value is not None:
    txt += '<br><span>value=<b>quot;%squot;</b>' % (value, )
...



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 test
that, change corresponding line in kss to:

response1-href:          nodeAttr(quot;hrefXXXquot;);



If we test it (this time don't miss to reload the page in the browser), it works well and we see the href
informational line omitted from the output. This means that if there is a nonexistent value to be
passed, 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 received
again the quot;parameter missingquot; error in Zope. Lucky we were smart in advance... In fact this is an
important issue. The reason for the implementation is, that we do not have a representation of the
value quot;Nonequot; that is passable to the ZPublisher as a method. So we have chosen to omit the value in
this case, but this makes the default value for the parameter inevitable.

As a second annoyance, currently we have no cross-browser compatible implementation that could
distinguish between a nonexistent attribute and an attribute that equals to an empty string. So we take
the 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 in
the Zope event log. One way to simulate this easily is to call a server action that does not have a
corresponding server method. This would give NotFound and would not be logged by default. One
way to try this is to temporarily remove the response1 method and see what happens. We could of
course 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 for
this: use the firebug extension of FireFox with the show XMLHttpRequests option switched on, or use
a 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 command
response we received a normal Plone error page. Inside the long long page we will see the actual error
message.

Congratulations! You can move to the next part at this point, or optinally, have a look at the next
chapter.
Doing it the Zope3 way (optional)

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

from kss.core import KSSView
from kss.core import force_unicode
from 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>quot;%squot;</b>' % (href, )
         if value is not None:
              txt += '<br><span>value=<b>quot;%squot;</b>' % (value, )

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



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

<browser:page
     for=quot;plone.app.kss.interfaces.IPortalObjectquot;
     class=quot;.demoview.DemoViewquot;
     attribute=quot;response1quot;
    name=quot;response1quot;
     permission=quot;zope2.Viewquot;
    />



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


    Accessing DOM information, part 2
This 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 with
the 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 rule
that matches all navtree nodes. Would there be a way to have both ? Consider entering a second kss
rules after the current one:

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

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



After reloading the page we see this section in the kss console logs:
...
Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type
GET http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss (36ms)
EventRule #0: li.navTreeItem a EVENT=click
EventRule #1: a.navTreeCurrentItem EVENT=click
EventRule #2: ul#portal-globalnav li a EVENT=click
Finished loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss resour
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.
...



What happens here? Eventrule #0 matches up with 2 nodes as previously. Eventrule #1 matches up
with 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 common
node. 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 current
navtree 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 be
      merged.
      Actions with different names will be added after each other (as an example, place an alert in
      rule #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. The
first 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 that
for different purposes (besided, a node can have only a single id.) We could use a different attribute but
using an attribute unknown to HTML would result in an invalid HTML content, even if the browser
would not shout against it. So we want to be able to attach attributes to the nodes when we generate the
template, 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 like
kukit:attrname=quot;attrvaluequot;. Unfortunately we cannot use this in Plone. Because Plone
uses transitional XHTML at the moment and this would only be valid in real XHTML with mimetype
text/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 version
instead.

We will path the information quot;Is this item the current item?quot; to our server action. That is already
available 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
the ZMI in Plone 3. Edit the file
plone/app/portlets/portlets/navigation_recurse.pt in lib/python of your
instance:

...
<div tal:define=quot;item_class python:is_current and item_class + ' navTreeCurrentItem' or item_classquot;>

     <a tal:attributes=quot;href python:link_remote and remote_url or item_url;
                        title node/Description;

                            class string:$item_class kssattr-iscurrent-${is_current}quot;>

           <img tal:replace=quot;structure item_icon/html_tagquot; />
           <span tal:replace=quot;node/Titlequot;>Selected Item Title</span>
    </a>

</div>
...



Note:

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

Look at the emphasized part. What we do is that instead of using real namespace attributes, we use an
emulation that stores attributes into the html class attribute. The advantage is that we can have more
attributes encoded into the same class after each other, and even preserve the existing classes on the
node. 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 anything
else 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:

<li class=quot;navTreeItem visualNoMarkerquot;>
  <div>
     <a class=quot;state-private kssattr-iscurrent-Falsequot; title=quot;quot; href=quot;http://l12/demo11/folder1/page1quot;>
    </a>
  </div>
</li>



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

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

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

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



And, expand our response1 script too. Also add iscurrent=None to the end of the parameter list:
...
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>quot;%squot;</b>' % (href, )
if value is not None:
    txt += '<br><span>value=<b>quot;%squot;</b>' % (value, )
if iscurrent is not None:
    txt += '<br><span>path=<b>quot;%squot;</b>' % (iscurrent, )
...



Let us test the results now. All navtree items now print if they are current or not (except for Home
which 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
        quot;Homequot; folder as well. This will only have a visible result if the navtree portlet becomes
        enabled on the portal root, too.

Starting on this path, we could arrive to an ajaxified navtree portlet. We do not do that now, however
we can conclude this. The ajaxification of existing components may require the customization of
templates. 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 implement
in-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), a
textarea field (the description), and a rich widget (text) controlled by the kupu editor as default. We will
start 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 is
        already 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:

<div id=quot;archetypes-fieldname-titlequot;
      class=quot;field ArchetypesStringWidget kssattr-atfieldname-titlequot;>
  <span/>
  <label for=quot;titlequot;>Title</label>
  <span class=quot;fieldRequiredquot; title=quot;Requiredquot;> (Required) </span>
  <div id=quot;title_helpquot; class=quot;formHelpquot;/>
  <div class=quot;fieldErrorBoxquot;/>
  <input id=quot;titlequot;
          class=quot;blurrable firstToFocusquot;
          type=quot;textquot; maxlength=quot;255quot; size=quot;30quot;
          value=quot;Welcome to Plonequot; name=quot;titlequot;/>
</div>



The CSS rule that would bind to the input node is div#archetypes-fieldname-title
input and the event that we will use is blur. This event is a native browser event and gets triggered
when the control looses focus. So let's add the following rule to our kss:
div#archetypes-fieldname-title input:blur {
    action-client: alert;
}



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 like
our response1 method did:

# import Through-The-Web(TTW) API
from kss.core.ttwapi import startKSSCommands
from kss.core.ttwapi import getKSSCommandSet
from kss.core.ttwapi import renderKSSCommands

# start a view for commands
startKSSCommands(context, context.REQUEST)

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

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

# render the commands
return renderKSSCommands()



And change the kss to call it:

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



This is good so far, but now we want to pass the value of the input form to the field. We have a few
possibilities 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 quot;currentquot; form is selected by the node on which the event has been executed, in this case
       the input node.
       currentFormVar() supposes that the node on which the event has been triggered is itself
       a 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 the
input node is the same on which the blur event is triggered. So our kss rule will be:

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



Then we change our validateField script as follows (and we also add value as a parameter to the
script):
# import Through-The-Web(TTW) API
from kss.core.ttwapi import startKSSCommands
from kss.core.ttwapi import getKSSCommandSet
from kss.core.ttwapi import renderKSSCommands
from kss.core.ttwapi import force_unicode

# start a view for commands
startKSSCommands(context, context.REQUEST)

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

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

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

# render the commands
return renderKSSCommands()



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

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



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 is
already implemented in KSS, so we don't have to implement it ourselves! This can be invoked in the
following way:

plone = getKSSCommandSet(quot;plonequot;)
plone.issuePortalMessage(error)



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

The full action script will be this in the end:
# import Through-The-Web(TTW) API
from kss.core.ttwapi import startKSSCommands
from kss.core.ttwapi import getKSSCommandSet
from kss.core.ttwapi import renderKSSCommands
from kss.core.ttwapi import force_unicode

# start a view for commands
startKSSCommands(context, context.REQUEST)

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

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

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

# do validation and send back result
instance = context
field = instance.getField('title')
error = field.validate(value, instance, {})
plone = getKSSCommandSet(quot;plonequot;)
plone.issuePortalMessage(error)

# render the commands
return renderKSSCommands()



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

Naturally to implement in-place validation with all the archetypes fields, more things are needed. Most
importantly, we need a way to be able to bind not only to a single field, but to all the fields (of the same
type) at once. In addition we don't only have text inputs but checkboxes, dropdowns and more
complex 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 a
deeper 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 a
single node markup thatr becames effective for entire page regions. For now, however, we conclude the
current lesson and enjoy the fruit of our efforts. Because by now we have learned what we need to
know 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 template
breaks for you, you might want to customize the template from the exact Plone version you use, instead
of 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:

instance = self.context.aq_inner



The aq_inner is in some cases necessary to provide the exactly right acquisition context for the
Archetypes scripts.
Also when we use the commandset adapters, the parameter for the interface that does the adaptation
should be self, not commands (exactly how we did in the previous lessons).

Contenu connexe

Tendances

How to install ReactJS software
How to install ReactJS software How to install ReactJS software
How to install ReactJS software VigneshVijay21
 
Hidden Secrets For A Hack-Proof Joomla! Site
Hidden Secrets For A Hack-Proof Joomla! SiteHidden Secrets For A Hack-Proof Joomla! Site
Hidden Secrets For A Hack-Proof Joomla! SiteDaniel Kanchev
 
How to investigate and recover from a security breach in WordPress
How to investigate and recover from a security breach in WordPressHow to investigate and recover from a security breach in WordPress
How to investigate and recover from a security breach in WordPressOtto Kekäläinen
 
How to Speed Up Your Joomla! Site
How to Speed Up Your Joomla! SiteHow to Speed Up Your Joomla! Site
How to Speed Up Your Joomla! SiteDaniel Kanchev
 
Webpack Tutorial, Uppsala JS
Webpack Tutorial, Uppsala JSWebpack Tutorial, Uppsala JS
Webpack Tutorial, Uppsala JSEmil Öberg
 
webpack 101 slides
webpack 101 slideswebpack 101 slides
webpack 101 slidesmattysmith
 
DrupalCon Barcelona 2015
DrupalCon Barcelona 2015DrupalCon Barcelona 2015
DrupalCon Barcelona 2015Daniel Kanchev
 
Web Development with NodeJS
Web Development with NodeJSWeb Development with NodeJS
Web Development with NodeJSRiza Fahmi
 
Fast Paced Drupal 8: Accelerating Development with Composer, Drupal Console a...
Fast Paced Drupal 8: Accelerating Development with Composer, Drupal Console a...Fast Paced Drupal 8: Accelerating Development with Composer, Drupal Console a...
Fast Paced Drupal 8: Accelerating Development with Composer, Drupal Console a...Acquia
 
WordCamp Finland 2015 - WordPress Security
WordCamp Finland 2015 - WordPress SecurityWordCamp Finland 2015 - WordPress Security
WordCamp Finland 2015 - WordPress SecurityTiia Rantanen
 
D installation manual
D installation manualD installation manual
D installation manualFaheem Akbar
 
Orchestration? You Don't Need Orchestration. What You Want is Choreography.
Orchestration? You Don't Need Orchestration. What You Want is Choreography.Orchestration? You Don't Need Orchestration. What You Want is Choreography.
Orchestration? You Don't Need Orchestration. What You Want is Choreography.Julian Dunn
 
Odoo development workflow with pip and virtualenv
Odoo development workflow with pip and virtualenvOdoo development workflow with pip and virtualenv
Odoo development workflow with pip and virtualenvacsone
 
implement lighthouse-ci with your web development workflow
implement lighthouse-ci with your web development workflowimplement lighthouse-ci with your web development workflow
implement lighthouse-ci with your web development workflowWordPress
 
Red Hat and Oracle: Delivering on the Promise of Interoperability in Java EE 7
Red Hat and Oracle: Delivering on the Promise of Interoperability in Java EE 7Red Hat and Oracle: Delivering on the Promise of Interoperability in Java EE 7
Red Hat and Oracle: Delivering on the Promise of Interoperability in Java EE 7Max Andersen
 
Anthony Somerset - Site Speed = Success!
Anthony Somerset - Site Speed = Success!Anthony Somerset - Site Speed = Success!
Anthony Somerset - Site Speed = Success!WordCamp Cape Town
 

Tendances (20)

AspNetWhitePaper
AspNetWhitePaperAspNetWhitePaper
AspNetWhitePaper
 
How to install ReactJS software
How to install ReactJS software How to install ReactJS software
How to install ReactJS software
 
Hidden Secrets For A Hack-Proof Joomla! Site
Hidden Secrets For A Hack-Proof Joomla! SiteHidden Secrets For A Hack-Proof Joomla! Site
Hidden Secrets For A Hack-Proof Joomla! Site
 
Webpack: from 0 to 2
Webpack: from 0 to 2Webpack: from 0 to 2
Webpack: from 0 to 2
 
Webpack DevTalk
Webpack DevTalkWebpack DevTalk
Webpack DevTalk
 
How to investigate and recover from a security breach in WordPress
How to investigate and recover from a security breach in WordPressHow to investigate and recover from a security breach in WordPress
How to investigate and recover from a security breach in WordPress
 
How to Speed Up Your Joomla! Site
How to Speed Up Your Joomla! SiteHow to Speed Up Your Joomla! Site
How to Speed Up Your Joomla! Site
 
Webpack Tutorial, Uppsala JS
Webpack Tutorial, Uppsala JSWebpack Tutorial, Uppsala JS
Webpack Tutorial, Uppsala JS
 
webpack 101 slides
webpack 101 slideswebpack 101 slides
webpack 101 slides
 
DrupalCon Barcelona 2015
DrupalCon Barcelona 2015DrupalCon Barcelona 2015
DrupalCon Barcelona 2015
 
Web Development with NodeJS
Web Development with NodeJSWeb Development with NodeJS
Web Development with NodeJS
 
Fast Paced Drupal 8: Accelerating Development with Composer, Drupal Console a...
Fast Paced Drupal 8: Accelerating Development with Composer, Drupal Console a...Fast Paced Drupal 8: Accelerating Development with Composer, Drupal Console a...
Fast Paced Drupal 8: Accelerating Development with Composer, Drupal Console a...
 
WordCamp Finland 2015 - WordPress Security
WordCamp Finland 2015 - WordPress SecurityWordCamp Finland 2015 - WordPress Security
WordCamp Finland 2015 - WordPress Security
 
D installation manual
D installation manualD installation manual
D installation manual
 
YouDrup_in_Drupal
YouDrup_in_DrupalYouDrup_in_Drupal
YouDrup_in_Drupal
 
Orchestration? You Don't Need Orchestration. What You Want is Choreography.
Orchestration? You Don't Need Orchestration. What You Want is Choreography.Orchestration? You Don't Need Orchestration. What You Want is Choreography.
Orchestration? You Don't Need Orchestration. What You Want is Choreography.
 
Odoo development workflow with pip and virtualenv
Odoo development workflow with pip and virtualenvOdoo development workflow with pip and virtualenv
Odoo development workflow with pip and virtualenv
 
implement lighthouse-ci with your web development workflow
implement lighthouse-ci with your web development workflowimplement lighthouse-ci with your web development workflow
implement lighthouse-ci with your web development workflow
 
Red Hat and Oracle: Delivering on the Promise of Interoperability in Java EE 7
Red Hat and Oracle: Delivering on the Promise of Interoperability in Java EE 7Red Hat and Oracle: Delivering on the Promise of Interoperability in Java EE 7
Red Hat and Oracle: Delivering on the Promise of Interoperability in Java EE 7
 
Anthony Somerset - Site Speed = Success!
Anthony Somerset - Site Speed = Success!Anthony Somerset - Site Speed = Success!
Anthony Somerset - Site Speed = Success!
 

Similaire à BaláZs Ree Introduction To Kss, Kinetic Style Sheets

Front end performance optimization
Front end performance optimizationFront end performance optimization
Front end performance optimizationStevie T
 
Magento Performance Optimization 101
Magento Performance Optimization 101Magento Performance Optimization 101
Magento Performance Optimization 101Angus Li
 
SugarCE For Small And Medium Enterprises
SugarCE For Small And Medium EnterprisesSugarCE For Small And Medium Enterprises
SugarCE For Small And Medium EnterprisesDashamir Hoxha
 
Drupal Frontend Performance and Scalability
Drupal Frontend Performance and ScalabilityDrupal Frontend Performance and Scalability
Drupal Frontend Performance and ScalabilityAshok Modi
 
Improving Drupal Performances
Improving Drupal PerformancesImproving Drupal Performances
Improving Drupal PerformancesVladimir Ilic
 
Building high performance web apps.
Building high performance web apps.Building high performance web apps.
Building high performance web apps.Arshak Movsisyan
 
Web development today
Web development todayWeb development today
Web development todayJaydev Gajera
 
15 ways to improve your word press website performance in 30 minutes
15 ways to improve your word press website performance in 30 minutes 15 ways to improve your word press website performance in 30 minutes
15 ways to improve your word press website performance in 30 minutes World Web Technology Pvt Ltd
 
Creating an nuget package for EPiServer
Creating an nuget package for EPiServerCreating an nuget package for EPiServer
Creating an nuget package for EPiServerPaul Graham
 
PVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOps
PVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOpsPVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOps
PVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOpsAndrey Karpov
 
Node JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web AppNode JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web AppEdureka!
 
How to optimize your Magento store
How to optimize your Magento store How to optimize your Magento store
How to optimize your Magento store Rasbor.com
 
Cloud and Ubiquitous Computing manual
Cloud and Ubiquitous Computing manual Cloud and Ubiquitous Computing manual
Cloud and Ubiquitous Computing manual Sonali Parab
 
Dspace for windows
Dspace for windowsDspace for windows
Dspace for windowsRavi Bankar
 
WordPress on AE Beanstalk:The One-Stop Guide
WordPress on AE Beanstalk:The One-Stop GuideWordPress on AE Beanstalk:The One-Stop Guide
WordPress on AE Beanstalk:The One-Stop GuidePixlogix Infotech
 
Trigger maxl from fdmee
Trigger maxl from fdmeeTrigger maxl from fdmee
Trigger maxl from fdmeeBernard Ash
 
Care and feeding notes
Care and feeding notesCare and feeding notes
Care and feeding notesPerrin Harkins
 
Compile open cpn on windows
Compile open cpn on windowsCompile open cpn on windows
Compile open cpn on windowsrandikaucsc
 
How To Install WordPress On VPS Hosting.pdf
How To Install WordPress On VPS Hosting.pdfHow To Install WordPress On VPS Hosting.pdf
How To Install WordPress On VPS Hosting.pdfHost It Smart
 
2019 se installation_guide&amp;knownissues
2019 se installation_guide&amp;knownissues2019 se installation_guide&amp;knownissues
2019 se installation_guide&amp;knownissuestanveer578
 

Similaire à BaláZs Ree Introduction To Kss, Kinetic Style Sheets (20)

Front end performance optimization
Front end performance optimizationFront end performance optimization
Front end performance optimization
 
Magento Performance Optimization 101
Magento Performance Optimization 101Magento Performance Optimization 101
Magento Performance Optimization 101
 
SugarCE For Small And Medium Enterprises
SugarCE For Small And Medium EnterprisesSugarCE For Small And Medium Enterprises
SugarCE For Small And Medium Enterprises
 
Drupal Frontend Performance and Scalability
Drupal Frontend Performance and ScalabilityDrupal Frontend Performance and Scalability
Drupal Frontend Performance and Scalability
 
Improving Drupal Performances
Improving Drupal PerformancesImproving Drupal Performances
Improving Drupal Performances
 
Building high performance web apps.
Building high performance web apps.Building high performance web apps.
Building high performance web apps.
 
Web development today
Web development todayWeb development today
Web development today
 
15 ways to improve your word press website performance in 30 minutes
15 ways to improve your word press website performance in 30 minutes 15 ways to improve your word press website performance in 30 minutes
15 ways to improve your word press website performance in 30 minutes
 
Creating an nuget package for EPiServer
Creating an nuget package for EPiServerCreating an nuget package for EPiServer
Creating an nuget package for EPiServer
 
PVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOps
PVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOpsPVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOps
PVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOps
 
Node JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web AppNode JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web App
 
How to optimize your Magento store
How to optimize your Magento store How to optimize your Magento store
How to optimize your Magento store
 
Cloud and Ubiquitous Computing manual
Cloud and Ubiquitous Computing manual Cloud and Ubiquitous Computing manual
Cloud and Ubiquitous Computing manual
 
Dspace for windows
Dspace for windowsDspace for windows
Dspace for windows
 
WordPress on AE Beanstalk:The One-Stop Guide
WordPress on AE Beanstalk:The One-Stop GuideWordPress on AE Beanstalk:The One-Stop Guide
WordPress on AE Beanstalk:The One-Stop Guide
 
Trigger maxl from fdmee
Trigger maxl from fdmeeTrigger maxl from fdmee
Trigger maxl from fdmee
 
Care and feeding notes
Care and feeding notesCare and feeding notes
Care and feeding notes
 
Compile open cpn on windows
Compile open cpn on windowsCompile open cpn on windows
Compile open cpn on windows
 
How To Install WordPress On VPS Hosting.pdf
How To Install WordPress On VPS Hosting.pdfHow To Install WordPress On VPS Hosting.pdf
How To Install WordPress On VPS Hosting.pdf
 
2019 se installation_guide&amp;knownissues
2019 se installation_guide&amp;knownissues2019 se installation_guide&amp;knownissues
2019 se installation_guide&amp;knownissues
 

Plus de Vincenzo Barone

Sally Kleinfeldt - Plone Application Development Patterns
Sally Kleinfeldt - Plone Application Development PatternsSally Kleinfeldt - Plone Application Development Patterns
Sally Kleinfeldt - Plone Application Development PatternsVincenzo Barone
 
Where's the source, Luke? : How to find and debug the code behind Plone
Where's the source, Luke? : How to find and debug the code behind PloneWhere's the source, Luke? : How to find and debug the code behind Plone
Where's the source, Luke? : How to find and debug the code behind PloneVincenzo Barone
 
ItalianSkin: an improvement in the accessibility of the Plone interface in or...
ItalianSkin: an improvement in the accessibility of the Plone interface in or...ItalianSkin: an improvement in the accessibility of the Plone interface in or...
ItalianSkin: an improvement in the accessibility of the Plone interface in or...Vincenzo Barone
 
How to market Plone the Web2.0 way
How to market Plone the Web2.0 wayHow to market Plone the Web2.0 way
How to market Plone the Web2.0 wayVincenzo Barone
 
Lennart Regebro What Zope Did Wrong (And What To Do Instead)
Lennart Regebro   What Zope Did Wrong (And What To Do Instead)Lennart Regebro   What Zope Did Wrong (And What To Do Instead)
Lennart Regebro What Zope Did Wrong (And What To Do Instead)Vincenzo Barone
 
Wichert Akkerman Plone Deployment Practices The Plone.Org Setup
Wichert Akkerman   Plone Deployment Practices   The Plone.Org SetupWichert Akkerman   Plone Deployment Practices   The Plone.Org Setup
Wichert Akkerman Plone Deployment Practices The Plone.Org SetupVincenzo Barone
 
Philipp Von Weitershausen Untested Code Is Broken Code
Philipp Von Weitershausen   Untested Code Is Broken CodePhilipp Von Weitershausen   Untested Code Is Broken Code
Philipp Von Weitershausen Untested Code Is Broken CodeVincenzo Barone
 
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...Duco Dokter - Plone for the enterprise market: technical musing on caching, C...
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...Vincenzo Barone
 
Rocky Burt Subtyping Unleashed
Rocky Burt   Subtyping UnleashedRocky Burt   Subtyping Unleashed
Rocky Burt Subtyping UnleashedVincenzo Barone
 
Alec Mitchell Relationship Building Defining And Querying Complex Relatio...
Alec Mitchell   Relationship Building   Defining And Querying Complex Relatio...Alec Mitchell   Relationship Building   Defining And Querying Complex Relatio...
Alec Mitchell Relationship Building Defining And Querying Complex Relatio...Vincenzo Barone
 
Wageindicator Foundation: a Case Study
Wageindicator Foundation: a Case StudyWageindicator Foundation: a Case Study
Wageindicator Foundation: a Case StudyVincenzo Barone
 
Tom Lazar Using Zope3 Views And Viewlets For Plone 3.0 Product Development
Tom Lazar   Using Zope3 Views And Viewlets For Plone 3.0 Product DevelopmentTom Lazar   Using Zope3 Views And Viewlets For Plone 3.0 Product Development
Tom Lazar Using Zope3 Views And Viewlets For Plone 3.0 Product DevelopmentVincenzo Barone
 
Xavier Heymans Plone Gov Plone In The Public Sector. Panel Presenting The...
Xavier Heymans   Plone Gov   Plone In The Public Sector. Panel Presenting The...Xavier Heymans   Plone Gov   Plone In The Public Sector. Panel Presenting The...
Xavier Heymans Plone Gov Plone In The Public Sector. Panel Presenting The...Vincenzo Barone
 
Brent Lambert Plone In Education A Case Study Of The Use Of Plone And Educa...
Brent Lambert   Plone In Education A Case Study Of The Use Of Plone And Educa...Brent Lambert   Plone In Education A Case Study Of The Use Of Plone And Educa...
Brent Lambert Plone In Education A Case Study Of The Use Of Plone And Educa...Vincenzo Barone
 
Wichert Akkerman - Plone.Org Infrastructure
Wichert Akkerman - Plone.Org InfrastructureWichert Akkerman - Plone.Org Infrastructure
Wichert Akkerman - Plone.Org InfrastructureVincenzo Barone
 
Philipp Von Weitershausen Plone Age Mammoths, Sabers And Caveen Cant The...
Philipp Von Weitershausen   Plone Age  Mammoths, Sabers And Caveen   Cant The...Philipp Von Weitershausen   Plone Age  Mammoths, Sabers And Caveen   Cant The...
Philipp Von Weitershausen Plone Age Mammoths, Sabers And Caveen Cant The...Vincenzo Barone
 
Denis Mishunov Making Plone Theme 10 Most Wanted Tips
Denis Mishunov   Making Plone Theme   10 Most Wanted Tips Denis Mishunov   Making Plone Theme   10 Most Wanted Tips
Denis Mishunov Making Plone Theme 10 Most Wanted Tips Vincenzo Barone
 
Duncan Booth Kupu, Past Present And Future
Duncan Booth   Kupu, Past Present And FutureDuncan Booth   Kupu, Past Present And Future
Duncan Booth Kupu, Past Present And FutureVincenzo Barone
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your WillVincenzo Barone
 
Jared Whitlock Open Source In The Enterprise Plone @ Novell
Jared Whitlock   Open Source In The Enterprise    Plone @ NovellJared Whitlock   Open Source In The Enterprise    Plone @ Novell
Jared Whitlock Open Source In The Enterprise Plone @ NovellVincenzo Barone
 

Plus de Vincenzo Barone (20)

Sally Kleinfeldt - Plone Application Development Patterns
Sally Kleinfeldt - Plone Application Development PatternsSally Kleinfeldt - Plone Application Development Patterns
Sally Kleinfeldt - Plone Application Development Patterns
 
Where's the source, Luke? : How to find and debug the code behind Plone
Where's the source, Luke? : How to find and debug the code behind PloneWhere's the source, Luke? : How to find and debug the code behind Plone
Where's the source, Luke? : How to find and debug the code behind Plone
 
ItalianSkin: an improvement in the accessibility of the Plone interface in or...
ItalianSkin: an improvement in the accessibility of the Plone interface in or...ItalianSkin: an improvement in the accessibility of the Plone interface in or...
ItalianSkin: an improvement in the accessibility of the Plone interface in or...
 
How to market Plone the Web2.0 way
How to market Plone the Web2.0 wayHow to market Plone the Web2.0 way
How to market Plone the Web2.0 way
 
Lennart Regebro What Zope Did Wrong (And What To Do Instead)
Lennart Regebro   What Zope Did Wrong (And What To Do Instead)Lennart Regebro   What Zope Did Wrong (And What To Do Instead)
Lennart Regebro What Zope Did Wrong (And What To Do Instead)
 
Wichert Akkerman Plone Deployment Practices The Plone.Org Setup
Wichert Akkerman   Plone Deployment Practices   The Plone.Org SetupWichert Akkerman   Plone Deployment Practices   The Plone.Org Setup
Wichert Akkerman Plone Deployment Practices The Plone.Org Setup
 
Philipp Von Weitershausen Untested Code Is Broken Code
Philipp Von Weitershausen   Untested Code Is Broken CodePhilipp Von Weitershausen   Untested Code Is Broken Code
Philipp Von Weitershausen Untested Code Is Broken Code
 
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...Duco Dokter - Plone for the enterprise market: technical musing on caching, C...
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...
 
Rocky Burt Subtyping Unleashed
Rocky Burt   Subtyping UnleashedRocky Burt   Subtyping Unleashed
Rocky Burt Subtyping Unleashed
 
Alec Mitchell Relationship Building Defining And Querying Complex Relatio...
Alec Mitchell   Relationship Building   Defining And Querying Complex Relatio...Alec Mitchell   Relationship Building   Defining And Querying Complex Relatio...
Alec Mitchell Relationship Building Defining And Querying Complex Relatio...
 
Wageindicator Foundation: a Case Study
Wageindicator Foundation: a Case StudyWageindicator Foundation: a Case Study
Wageindicator Foundation: a Case Study
 
Tom Lazar Using Zope3 Views And Viewlets For Plone 3.0 Product Development
Tom Lazar   Using Zope3 Views And Viewlets For Plone 3.0 Product DevelopmentTom Lazar   Using Zope3 Views And Viewlets For Plone 3.0 Product Development
Tom Lazar Using Zope3 Views And Viewlets For Plone 3.0 Product Development
 
Xavier Heymans Plone Gov Plone In The Public Sector. Panel Presenting The...
Xavier Heymans   Plone Gov   Plone In The Public Sector. Panel Presenting The...Xavier Heymans   Plone Gov   Plone In The Public Sector. Panel Presenting The...
Xavier Heymans Plone Gov Plone In The Public Sector. Panel Presenting The...
 
Brent Lambert Plone In Education A Case Study Of The Use Of Plone And Educa...
Brent Lambert   Plone In Education A Case Study Of The Use Of Plone And Educa...Brent Lambert   Plone In Education A Case Study Of The Use Of Plone And Educa...
Brent Lambert Plone In Education A Case Study Of The Use Of Plone And Educa...
 
Wichert Akkerman - Plone.Org Infrastructure
Wichert Akkerman - Plone.Org InfrastructureWichert Akkerman - Plone.Org Infrastructure
Wichert Akkerman - Plone.Org Infrastructure
 
Philipp Von Weitershausen Plone Age Mammoths, Sabers And Caveen Cant The...
Philipp Von Weitershausen   Plone Age  Mammoths, Sabers And Caveen   Cant The...Philipp Von Weitershausen   Plone Age  Mammoths, Sabers And Caveen   Cant The...
Philipp Von Weitershausen Plone Age Mammoths, Sabers And Caveen Cant The...
 
Denis Mishunov Making Plone Theme 10 Most Wanted Tips
Denis Mishunov   Making Plone Theme   10 Most Wanted Tips Denis Mishunov   Making Plone Theme   10 Most Wanted Tips
Denis Mishunov Making Plone Theme 10 Most Wanted Tips
 
Duncan Booth Kupu, Past Present And Future
Duncan Booth   Kupu, Past Present And FutureDuncan Booth   Kupu, Past Present And Future
Duncan Booth Kupu, Past Present And Future
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your Will
 
Jared Whitlock Open Source In The Enterprise Plone @ Novell
Jared Whitlock   Open Source In The Enterprise    Plone @ NovellJared Whitlock   Open Source In The Enterprise    Plone @ Novell
Jared Whitlock Open Source In The Enterprise Plone @ Novell
 

Dernier

MAHA Global and IPR: Do Actions Speak Louder Than Words?
MAHA Global and IPR: Do Actions Speak Louder Than Words?MAHA Global and IPR: Do Actions Speak Louder Than Words?
MAHA Global and IPR: Do Actions Speak Louder Than Words?Olivia Kresic
 
(Best) ENJOY Call Girls in Faridabad Ex | 8377087607
(Best) ENJOY Call Girls in Faridabad Ex | 8377087607(Best) ENJOY Call Girls in Faridabad Ex | 8377087607
(Best) ENJOY Call Girls in Faridabad Ex | 8377087607dollysharma2066
 
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...lizamodels9
 
Innovation Conference 5th March 2024.pdf
Innovation Conference 5th March 2024.pdfInnovation Conference 5th March 2024.pdf
Innovation Conference 5th March 2024.pdfrichard876048
 
Digital Transformation in the PLM domain - distrib.pdf
Digital Transformation in the PLM domain - distrib.pdfDigital Transformation in the PLM domain - distrib.pdf
Digital Transformation in the PLM domain - distrib.pdfJos Voskuil
 
APRIL2024_UKRAINE_xml_0000000000000 .pdf
APRIL2024_UKRAINE_xml_0000000000000 .pdfAPRIL2024_UKRAINE_xml_0000000000000 .pdf
APRIL2024_UKRAINE_xml_0000000000000 .pdfRbc Rbcua
 
Market Sizes Sample Report - 2024 Edition
Market Sizes Sample Report - 2024 EditionMarket Sizes Sample Report - 2024 Edition
Market Sizes Sample Report - 2024 EditionMintel Group
 
/:Call Girls In Indirapuram Ghaziabad ➥9990211544 Independent Best Escorts In...
/:Call Girls In Indirapuram Ghaziabad ➥9990211544 Independent Best Escorts In.../:Call Girls In Indirapuram Ghaziabad ➥9990211544 Independent Best Escorts In...
/:Call Girls In Indirapuram Ghaziabad ➥9990211544 Independent Best Escorts In...lizamodels9
 
Ten Organizational Design Models to align structure and operations to busines...
Ten Organizational Design Models to align structure and operations to busines...Ten Organizational Design Models to align structure and operations to busines...
Ten Organizational Design Models to align structure and operations to busines...Seta Wicaksana
 
Organizational Structure Running A Successful Business
Organizational Structure Running A Successful BusinessOrganizational Structure Running A Successful Business
Organizational Structure Running A Successful BusinessSeta Wicaksana
 
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,noida100girls
 
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...lizamodels9
 
8447779800, Low rate Call girls in New Ashok Nagar Delhi NCR
8447779800, Low rate Call girls in New Ashok Nagar Delhi NCR8447779800, Low rate Call girls in New Ashok Nagar Delhi NCR
8447779800, Low rate Call girls in New Ashok Nagar Delhi NCRashishs7044
 
BEST Call Girls In Old Faridabad ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Old Faridabad ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,BEST Call Girls In Old Faridabad ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Old Faridabad ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,noida100girls
 
The CMO Survey - Highlights and Insights Report - Spring 2024
The CMO Survey - Highlights and Insights Report - Spring 2024The CMO Survey - Highlights and Insights Report - Spring 2024
The CMO Survey - Highlights and Insights Report - Spring 2024christinemoorman
 
Case study on tata clothing brand zudio in detail
Case study on tata clothing brand zudio in detailCase study on tata clothing brand zudio in detail
Case study on tata clothing brand zudio in detailAriel592675
 
Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deck
Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deckPitch Deck Teardown: Geodesic.Life's $500k Pre-seed deck
Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deckHajeJanKamps
 
Call Us 📲8800102216📞 Call Girls In DLF City Gurgaon
Call Us 📲8800102216📞 Call Girls In DLF City GurgaonCall Us 📲8800102216📞 Call Girls In DLF City Gurgaon
Call Us 📲8800102216📞 Call Girls In DLF City Gurgaoncallgirls2057
 

Dernier (20)

MAHA Global and IPR: Do Actions Speak Louder Than Words?
MAHA Global and IPR: Do Actions Speak Louder Than Words?MAHA Global and IPR: Do Actions Speak Louder Than Words?
MAHA Global and IPR: Do Actions Speak Louder Than Words?
 
(Best) ENJOY Call Girls in Faridabad Ex | 8377087607
(Best) ENJOY Call Girls in Faridabad Ex | 8377087607(Best) ENJOY Call Girls in Faridabad Ex | 8377087607
(Best) ENJOY Call Girls in Faridabad Ex | 8377087607
 
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
 
Innovation Conference 5th March 2024.pdf
Innovation Conference 5th March 2024.pdfInnovation Conference 5th March 2024.pdf
Innovation Conference 5th March 2024.pdf
 
Digital Transformation in the PLM domain - distrib.pdf
Digital Transformation in the PLM domain - distrib.pdfDigital Transformation in the PLM domain - distrib.pdf
Digital Transformation in the PLM domain - distrib.pdf
 
APRIL2024_UKRAINE_xml_0000000000000 .pdf
APRIL2024_UKRAINE_xml_0000000000000 .pdfAPRIL2024_UKRAINE_xml_0000000000000 .pdf
APRIL2024_UKRAINE_xml_0000000000000 .pdf
 
Market Sizes Sample Report - 2024 Edition
Market Sizes Sample Report - 2024 EditionMarket Sizes Sample Report - 2024 Edition
Market Sizes Sample Report - 2024 Edition
 
Japan IT Week 2024 Brochure by 47Billion (English)
Japan IT Week 2024 Brochure by 47Billion (English)Japan IT Week 2024 Brochure by 47Billion (English)
Japan IT Week 2024 Brochure by 47Billion (English)
 
/:Call Girls In Indirapuram Ghaziabad ➥9990211544 Independent Best Escorts In...
/:Call Girls In Indirapuram Ghaziabad ➥9990211544 Independent Best Escorts In.../:Call Girls In Indirapuram Ghaziabad ➥9990211544 Independent Best Escorts In...
/:Call Girls In Indirapuram Ghaziabad ➥9990211544 Independent Best Escorts In...
 
Ten Organizational Design Models to align structure and operations to busines...
Ten Organizational Design Models to align structure and operations to busines...Ten Organizational Design Models to align structure and operations to busines...
Ten Organizational Design Models to align structure and operations to busines...
 
Organizational Structure Running A Successful Business
Organizational Structure Running A Successful BusinessOrganizational Structure Running A Successful Business
Organizational Structure Running A Successful Business
 
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
 
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
 
Corporate Profile 47Billion Information Technology
Corporate Profile 47Billion Information TechnologyCorporate Profile 47Billion Information Technology
Corporate Profile 47Billion Information Technology
 
8447779800, Low rate Call girls in New Ashok Nagar Delhi NCR
8447779800, Low rate Call girls in New Ashok Nagar Delhi NCR8447779800, Low rate Call girls in New Ashok Nagar Delhi NCR
8447779800, Low rate Call girls in New Ashok Nagar Delhi NCR
 
BEST Call Girls In Old Faridabad ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Old Faridabad ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,BEST Call Girls In Old Faridabad ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Old Faridabad ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
 
The CMO Survey - Highlights and Insights Report - Spring 2024
The CMO Survey - Highlights and Insights Report - Spring 2024The CMO Survey - Highlights and Insights Report - Spring 2024
The CMO Survey - Highlights and Insights Report - Spring 2024
 
Case study on tata clothing brand zudio in detail
Case study on tata clothing brand zudio in detailCase study on tata clothing brand zudio in detail
Case study on tata clothing brand zudio in detail
 
Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deck
Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deckPitch Deck Teardown: Geodesic.Life's $500k Pre-seed deck
Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deck
 
Call Us 📲8800102216📞 Call Girls In DLF City Gurgaon
Call Us 📲8800102216📞 Call Girls In DLF City GurgaonCall Us 📲8800102216📞 Call Girls In DLF City Gurgaon
Call Us 📲8800102216📞 Call Girls In DLF City Gurgaon
 

BaláZs Ree Introduction To Kss, Kinetic Style Sheets

  • 1. 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
  • 2. anything, logs will appear directly in the FireBug console. If you use Safari, logging also appears in the Safari console. If these possibilities are not available for you, you have two other choices: either use Firebug 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 into the 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, you need 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 original page. Then enter the following url into your browser after your page has been loaded (you can bookmark this as a link to avoid typing it in each time): javascript:kukit.showLog(); Remark: This needs to be repeated each time the page gets reloaded, because each time a page is loaded, 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 on reloading a page. They will begin this way: Loading KSS engine. ... If this does not happen, check if you have succeeded with the steps described above, most notably, are you in Development Mode? You may also need to log in to the portal, as by default KSS is disabled in Plone for non-authenticated users. The text following the first line may indicate that KSS installed more eventrules on its own. This would not 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 to disable this. Go to the ZMI in the portal root, enter the tool plone_kss and unselect the checkbox left 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:
  • 3. 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: 0 Setup of events for document starts. 0 special rules bound in grand total. 0 nodes bound in grand total. This means that KSS is active in the page, but there were no rule sheets to set up - so nothing happened. If the Count of KSS links is not zero, you need to return to the previous steps and make the remaining 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 - even the syntax is almost valid CSS - but it has different semantics and it controls how KSS events are bound to your page elements. To do it simplest, create a file named tutorial1.kss in your custom skins directory. To make the file bound to each page in the portal, we need to make it known by ResourceRegistries. Go to the ZMI in the 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 not forget to enable Debugging Mode on the top of the same page. This will make the resource debuggable, even if the production settings are applied for compression, caching and merging.
  • 4. After pressing Add, navigate back to your portal frontpage, and reload it. You should see the following messages: Loading KSS engine. ... Initializing rule sheets. Count of KSS links: 1 Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type GET 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 resour 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. ... This means that your kss file is active on the page, although it is yet not doing anything. At this point you 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. From the point of view of KSS this is all the same but this leads to a cleaner development style, better supported in the future versions of Plone. Do not attempt to do this however if you do not know at least a little of Five, because in this tutorial we will not teach you Five itself, we only show the way to use it for KSS. Remember: zope3 development style is easy once you have learned it, but one might feel 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. In the product directory on the filesystem, create a browser subdirectory and save the kss file there with the name tutorial1.kss. Then in the product root create a configure.zcml file with the following content: <configure xmlns=quot;http://namespaces.zope.org/zopequot; xmlns:browser=quot;http://namespaces.zope.org/browserquot;> <!-- the kss resource --> <browser:resource file=quot;browser/tutorial1.kssquot; name=quot;tutorial1.kssquot; /> </configure> After this you need to restart zope: if you have made an error in this file you see it immediately as zope will refuse to start at all. If zope has started up, then check that the url http://127.0.0.1:8080/++resource++tutorial1.kss returns the kss file correctly from 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 the portal_kss tool. After making this change you should reload your page from the browser, open the kss debug console and see the log messages like above. In addition, since now you have a separate tutorial product, you could also make a GenericSetup profule that automatically registers the kss resource with the kss tool. To have an example of this, you may 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
  • 5. 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 the following basic structure, that consists of a folder and three pages in it: portal root ----- folder1 ----- page1 | ---------- page2 | -----------page3 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 is disabled for unauthenticated users. Since you just created the above structure, this should already be true. 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 the dynamic behaviour of your page. A KSS rule looks similarly to a CSS rule. There is a selector part that selects one or more html nodes just like in CSS, then there is an event descriptor that is attached to the CSS selector with the comma. We will use the click event that will trigger when the selected node or nodes are clicked with the mouse. First we decide which nodes we bind to. This can be any selectable element in the page. Let us first bind 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: <a title=quot;quot; href=quot;http://127.0.0.1:8080quot; class=quot;navTreeCurrentItem visualIconPaddingquot;> Home </a> We can see that the selector of this node can be, for example, a.navTreeCurrentItem so this will be the selector we use. The full event rule that we enter into the kss file will be this: a.navTreeCurrentItem:click { action-client: alert; } Notice that after the selector we put :click, this tells KSS what event to use and is always obligatory in the KSS rule. Between the braces we have various properties that control what actions are bound to the selected nodes, and also the parameters for this actions. This rule says that when the event gets triggered, we do not want to communicate with the server yet, but simply pop up an alert box. This is just 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:
  • 6. 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: 1 Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type GET http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss (52ms)++resource++kukit... (line 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 resour 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. 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 enabled Debugging in the portal_kss tool. If this is not done, the resource is most likely got cached where it should not be. If we click on the Home line in the navtree portlet (or to the item line which is current and highlighted), the following popup dialog appears: This tells us that the event has triggered in order, and also gives some additional information, like the number 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 we would 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 in line with how css behaves too. This is what allows us to put a lot of rules into the same kss file. No need to set up conditions: if the selected nodes are not present in the current page, simple nothing happens. 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 error appears and we can see the followings in the console log:
  • 7. [Exception... quot;'Error parsing KSS at http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss : ParsingError: Undefined event : [null:klick].' when calling method: [nsIDOMEventListener::handleEvent]quot; nsresult: quot;0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)quot; location: quot;<unknown>quot; data: no] The important part of the message says that the klick global event does not exist, the rest is just FireFox cruft. If we just make a syntax error, we get a ParsingError that also give the position of the error in the file. As a consequence nothing will work and we are forced to fix the error immediately and reload the page afterwards. Note: The null in [null:klick] means that the event is in the global namespace. Foe events 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 parameters with the event binding. In the previous example after we closed the alert, the page reloaded because the link was followed by default. The click event has a parameter preventdefault which can be used to override this. The modified rule will be: a.navTreeCurrentItem:click { evt-click-preventdefault: True; action-client: alert; } The full property name is evt-click-preventdefault where three segments are separated with dashes: evt says this is an event binding parameter click 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 ones have 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 is otherwise something that the browser wanted to do on the click, before we started to modify its behaviour. 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 be followed. Specify an action parameter We can also specify parameters to the actions themselves. The main difference between the event binder 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 we clicked on the link). As was the case with the event parameters, it is entirely up to the given action to
  • 8. decide what parameters it accepts and handles. Now, the alert action just happens to accept a message parameter, so we extend our rule with one line: a.navTreeCurrentItem:click { evt-click-preventdefault: True; action-client: alert; alert-message: quot;Hey, we are herequot;; } 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 on the right side: this is only necessary if the value is not a single word. Both single and double quotes can be 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, with debug 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 your changes 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 save yourself from a lot of headache. You have learned the basics of setting up kss rules so far, but nothing that would really do anything so far. 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 2 We will continue and learn how to create server actions and specify their execution from our event rules. . This tutorial is the direct continuation of the first part. We will learn how to specify server side actions and 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 was triggered. 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 the business logic, the task of the server method is to assemble a sequence of commands. These commands are marshalled back to the client to be executed there. A command is a call to DOM manipulation client code. A command can (in most cases, should) have a selector, to set the scope of nodes 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 a custom one since it requires only python skills. Let's create a python script (response1) in the custom skin with the following content:
  • 9. # import Through-The-Web(TTW) API from kss.core.ttwapi import startKSSCommands from kss.core.ttwapi import getKSSCommandSet from kss.core.ttwapi import renderKSSCommands # start a view for commands startKSSCommands(context, context.REQUEST) # add a command core = getKSSCommandSet('core') core.replaceInnerHTML('#portal-siteactions', '<h1>We did it!</h1>') # render the commands return renderKSSCommands() After the imports, we initialize the view that will hold the KSS commands that we want to send back to the 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 html string. To specify which nodes will be selected, the command also needs a selector: in this example, a standard CSS selector. We choose to replace the portal actions of a Plone portal that are on the top of the page - but we could choose any other element as well. The replaceInnerHTML method is accessed through a command set. Since we have a pluggable system, we need to refer to the component that defines the methods, in this case, the 'core' command set. In the last line, the renderKSSCommands call is mandatory : it will generate the response payload from the accumulated commands. To look at this payload, let's access this method directly from the browser: http://localhost:8080/plone/front-page/response1. We will see We did it! on the screen, but let's have a more careful look at the source of the response: <!DOCTYPE html PUBLIC quot;-//W3C//DTD XHTML 1.0 Transitional//ENquot; quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtdquot;> <html xmlns=quot;http://www.w3.org/1999/xhtmlquot; xmlns:kukit=quot;http://www.kukit.org/commands/1.0quot;><body> <kukit:commands> <kukit:command selector=quot;#portal-siteactionsquot; name=quot;replaceInnerHTMLquot; selectorType=quot;quot;> <kukit:param name=quot;htmlquot;><h1>We did it!</h1></kukit:param> </kukit:command> </kukit:commands> </body></html> 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 given parameters. Calling the server action Now, we have finished to build our server action; we want to call it from our kss style sheet. We replace our previous KSS event rule with the following one:
  • 10. a.navTreeCurrentItem:click { evt-click-preventdefault: True; action-server: response1; } 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 quot;Homequot; line in the navtree portlet. It works! We can see the site actions replaced with our text. Also notice that a few things have been logged to the kss console: RequestManager notifies server http://127.0.0.1:8080/demo11/folder1/page1/response1, rid=0 (RQ: 1 OUT, 0 WA 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... ... 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: ... from DateTime import DateTime # add a command core = getKSSCommandSet('core') content = '<h1>We did it!</h1><span>%s</span>' % DateTime() core.replaceInnerHTML('#portal-siteactions', content) ... This way, the current time is sent back by the server on each click and we can see that something happens. 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 server restarts. What happens if the server-side script has an error, or the client does not get a correct response for some 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=quot;ImportError: import of quot;DateTimeXquot; from quot;DateTimequot; is unauthorized. The container has no security assertions. Access to 'DateTimeX' of (module 'DateTime' from '/usr/local/lib/Zope2.10/lib/python/DateTime/__init__.pyc') denied.quot; The error Request failed indicates that we have to turn to the server to debug the problem. Our
  • 11. best friend, the zope error log will tell us about the actual problem. However in this case KSS also logged 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 server as form variables. Zope publisher can then pass them as usual keyword parameters to our python script. Let's render a parameter coming from the client. We add parameter mymessage to the python script. Then modify the script: ... # add a command core = getKSSCommandSet('core') content = '<h1>We did it!</h1><span><b>%s</b> at %s</span>' % (mymessage, DateTime()) core.replaceInnerHTML('#portal-siteactions', content) ... We modify our kss rule to actually send the parameter from the client: a.navTreeCurrentItem:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: quot;Hello Plone!quot;; } The key response1-mymessage is built identically to how we did it with the client action. We use the 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 bound node. To understand better how all this is working, let's enter a second rule in the kss: ul#portal-globalnav li a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: quot;clicked on global navquot;; } 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 on any 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:
  • 12. Loading KSS engine. ... Initializing rule sheets. Count of KSS links: 1 Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type GET http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss (22ms) EventRule #0: a.navTreeCurrentItem EVENT=click EventRule #1: ul#portal-globalnav li a EVENT=click Finished loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss resour 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. ... This shows that the second rule is also in effect now. Moreover, it has selected 5 nodes (or however many globalnav tabs you have). A lot of other information is also logged, it should not worry you at the moment. Different command selector Until now, in our command, we used the default css selector. It is possible to use other types of selectors, like a html id selector. Let's modify our command in the following way: ... # add a command core = 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) ... What an HTML id selector selects should be obvious. Reload the page and exercise! Commands can also select multiple nodes: ... # add a command core = 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) ... The CSS selector dt.portletHeader a selects all portlet headers in the page, so the replacement will be executed not on one node but on many nodes (as can also be seen in the logs). Try clicking the Home 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 the payload. One thing is important to note. If a command selects no nodes, it is not an error: the behaviour designed in this case is that nothing happens. This is in line with the usual logic of CSS selectors in style 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 KSS anything can be an event and how an event binds itself is freely implementable.
  • 13. Let's add the following rule to the end of our kss file (altogether we will have 3 rules then): document:timeout { evt-timeout-delay: 8000; action-server: response1; response1-mymessage: quot;from timeoutquot;; } 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. The event 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 instead of a CSS selector as we did until now. This is a special KSS selector that is an extension to css and simply means : bind this event exactly once when the document loads, with a scope of no nodes but the document itself. If you reload the page you will notice that clicks work as before, however, every 8 seconds, the timeout event 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 in the next tutorials. Congratulations! You have completed your first KSS tutorial, learned the basics and now you are able to start some experimentation 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 is moving 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 the product root directory on the filesystem: from kss.core import KSSView, kssaction from 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) 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 KSS commands 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
  • 14. way. To be able to use the server side method, you need to add the following to your configure.zcml file: <browser:page for=quot;plone.app.kss.interfaces.IPortalObjectquot; class=quot;.demoview.DemoViewquot; attribute=quot;response1quot; name=quot;response1quot; permission=quot;zope2.Viewquot; /> The interface that the view is bound to is one setup by kss.core on all portal objects. You could also use directly the interfaces defined by Plone 2.5 directly if you wanted, however the advantage of using 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 rename it. Now it is time to restart Zope. If everything goes well, the page functions as previously but you can see 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 to test the changes made in the method code come. Accessing DOM information In this lesson we will learn how we can access information from our page and pass it to the server actions. We will also familiarize ourselves with the most common errors we encounter 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 different parameters to them. To start with, we will further improve the code that we used in the previous examples. 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 to do this. Also remember to disable the kss files already registered from the installed products and add your 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: a.navTreeCurrentItem:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: quot;Hello Plone!quot;; } This rule, combined with the response1 method, writes a message in the upper right corner of the page, if in the navigation portlet we click to the line representing our current folder location. (For the portal home page, this will be the quot;Homequot; 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 navtree items. After having looked in the FireBug inspector we can see that the corresponding css selector will be li.navTreeItem a. Change the rule accordingly. (Do not make a duplicate for reasons to be
  • 15. described later.): li.navTreeItem a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: quot;Hello Plone!quot;; } Save the rules and reload your page. If done right, you can see in the log that the rule is now correctly binding to the required number of nodes (for example, I have 2 items appearing in the navtree): EventRule [#0-@@0@@click] selected 2 nodes. 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 pass different parameters depending on the node. If I programmed in javascript, I would look up the correct information from the DOM. How can I do this with KSS? Obviously making 4 different eventrule 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 functions may 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 we want to pass the href attribute of the clicked node. Then we modify the rule as follows: li.navTreeItem a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: nodeAttr(quot;hrefquot;); } 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. In addition 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 we should 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 following example. kssAttr(attrname) This is for reading (namespace) attributes holding information for kss only, and otherwise
  • 16. 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 single form 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 which the selected node is. The parameter varname is optional, and if it is ommitted, the current node 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 no semantic interpretation of the name. Therefore, multiform zope variables with the prefix :list, :record and :records cannot be fetched this way: they are not assembled as they semantica would require it. To handle zope multiform variables, currently the only way is to acquire all form variables in an entire form, as described below. For a complete and updated list, you can check the cheatsheet. All these commands are implemented in the kukit core and the rule is to keep them simple: if you need something more complicated, then a plugin 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 quot;kssquot; in the name signifies that this is a special parameter ( - consequently ordinary parameters are prohibited to start with the prefix quot;kssquot;). 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 can receive the parameters as it would receive them in the case of a normal server side method (the ZPublisher marshalls parameters that appear in the method signature, and in addition the variablesare also 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:
  • 17. In the earlier versions of KSS, these value providers were also usable in normal parameters. 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 of the node, in this case the visible title of the navtree item: li.navTreeItem a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: 'clicked on navtree item'; response1-href: nodeAttr(quot;hrefquot;); response1-value: nodeContent(); } We also need to change the response1 python scrript to accomodate the new parameters: # import Through-The-Web(TTW) API from kss.core.ttwapi import startKSSCommands from kss.core.ttwapi import getKSSCommandSet from kss.core.ttwapi import renderKSSCommands # start a view for commands startKSSCommands(context, context.REQUEST) # use the parameters txt = '<h1>We did it!</h1><span><b>%s</b> at %s</span>' % (mymessage, DateTime()) txt += '<br><span>href=<b>quot;%squot;</b>' % (href, ) txt += '<br><span>value=<b>quot;%squot;</b>' % (value, ) # add a command core = getKSSCommandSet('core') core.replaceInnerHTML('#portal-siteactions', txt) # render the commands return renderKSSCommands() We also need to change the parameter list of the string to: quot;mymessage, href, valuequot;. Let's click on the different lines in the navtree and enjoy the results. However I am using an internationalized site, and when I click on any line where there are non-ascii characters in the folder name, I receive an error in the logs: Request failed at url http://127.0.0.1:8080/demo11/folder1/page2/response1, rid=0, server_reason=quot;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)quot; We can also see the full traceback in the Zope event log: 2007-10-08 15:24:56 ERROR Zope.SiteErrorLog http://127.0.0.1:8080/demo11/folder1/page2/response1 Traceback (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_unicode KSSUnicodeError: Content must be unicode or ascii string, original exception: 'ascii' codec can't decode by
  • 18. The explanation is this. When we build up the commands, the basic rule is that the supplied command parameters must be either unicode or plain ascii. We do not care autoconverting anything else: KSS has no information of what encoding is used in a string, and we have learned by now that magic can do more harm than help. So we must do explicit conversion of the parameters: from kss.core import force_unicode value = force_unicode(value, 'utf') The force_unicode method converts the input to unicode, from the encoding given as a second parameter. Why did we use utf? Actually KSS is handling charsets properly on the client side, and passes the parameters with utf encoding. The ZPublisher of Zope2 is dumb enough to marshall this to parameters as utf-8 encoded (non-unicode) strings. Hence the need for the conversion. On the other hand the twisted publisher of Zope3 is passing proper unicode values. So, if the force_unicode method meets an unicode, it just lets it pass. This is a way of thinking about future compatibility: we target to write 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): ul#portal-globalnav li a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: quot;clicked on global navquot;; } 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: 2007-10-08 15:36:09 ERROR Zope.SiteErrorLog http://127.0.0.1:8080/demo11/folder1/page2/response1 Traceback (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 badRequestError BadRequest: <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 parameter <hr noshade=quot;noshadequot;/> 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 the text output better:
  • 19. ... 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>quot;%squot;</b>' % (href, ) if value is not None: txt += '<br><span>value=<b>quot;%squot;</b>' % (value, ) ... 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 test that, change corresponding line in kss to: response1-href: nodeAttr(quot;hrefXXXquot;); If we test it (this time don't miss to reload the page in the browser), it works well and we see the href informational line omitted from the output. This means that if there is a nonexistent value to be passed, 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 received again the quot;parameter missingquot; error in Zope. Lucky we were smart in advance... In fact this is an important issue. The reason for the implementation is, that we do not have a representation of the value quot;Nonequot; that is passable to the ZPublisher as a method. So we have chosen to omit the value in this case, but this makes the default value for the parameter inevitable. As a second annoyance, currently we have no cross-browser compatible implementation that could distinguish between a nonexistent attribute and an attribute that equals to an empty string. So we take the 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 in the Zope event log. One way to simulate this easily is to call a server action that does not have a corresponding server method. This would give NotFound and would not be logged by default. One way to try this is to temporarily remove the response1 method and see what happens. We could of course 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 for this: use the firebug extension of FireFox with the show XMLHttpRequests option switched on, or use a 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 command response we received a normal Plone error page. Inside the long long page we will see the actual error message. Congratulations! You can move to the next part at this point, or optinally, have a look at the next chapter.
  • 20. Doing it the Zope3 way (optional) If you are familiar with Zope3 style development, you can easily transform the code into a method in a browserview yourself. Enter the file demoview.py into the product root in the filesystem: from kss.core import KSSView from kss.core import force_unicode from 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>quot;%squot;</b>' % (href, ) if value is not None: txt += '<br><span>value=<b>quot;%squot;</b>' % (value, ) # KSS specific calls core = self.getCommandSet('core') core.replaceInnerHTML('#portal-siteactions', txt) return self.render() As usual, you also need to add the following to your configure.zcml: <browser:page for=quot;plone.app.kss.interfaces.IPortalObjectquot; class=quot;.demoview.DemoViewquot; attribute=quot;response1quot; name=quot;response1quot; permission=quot;zope2.Viewquot; /> In the next tutorial lesson we look at some more usage of parameter producer functions. Accessing DOM information, part 2 This 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 with the 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 rule that matches all navtree nodes. Would there be a way to have both ? Consider entering a second kss rules after the current one: li.navTreeItem a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: 'clicked on navtree item'; response1-href: nodeAttr(quot;hrefquot;); response1-value: nodeContent(); } a.navTreeCurrentItem:click { response1-mymessage: 'clicked on the CURRENT navtree item'; } After reloading the page we see this section in the kss console logs:
  • 21. ... Start loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss of type GET http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss (36ms) EventRule #0: li.navTreeItem a EVENT=click EventRule #1: a.navTreeCurrentItem EVENT=click EventRule #2: ul#portal-globalnav li a EVENT=click Finished loading and processing http://127.0.0.1:8080/demo11/portal_kss/Plone%20Default/tutorial1.kss resour 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. ... What happens here? Eventrule #0 matches up with 2 nodes as previously. Eventrule #1 matches up with 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 common node. 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 current navtree 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 be merged. Actions with different names will be added after each other (as an example, place an alert in rule #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. The first 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 that for different purposes (besided, a node can have only a single id.) We could use a different attribute but using an attribute unknown to HTML would result in an invalid HTML content, even if the browser would not shout against it. So we want to be able to attach attributes to the nodes when we generate the template, 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 like kukit:attrname=quot;attrvaluequot;. Unfortunately we cannot use this in Plone. Because Plone uses transitional XHTML at the moment and this would only be valid in real XHTML with mimetype text/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 version instead. We will path the information quot;Is this item the current item?quot; to our server action. That is already available 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
  • 22. the ZMI in Plone 3. Edit the file plone/app/portlets/portlets/navigation_recurse.pt in lib/python of your instance: ... <div tal:define=quot;item_class python:is_current and item_class + ' navTreeCurrentItem' or item_classquot;> <a tal:attributes=quot;href python:link_remote and remote_url or item_url; title node/Description; class string:$item_class kssattr-iscurrent-${is_current}quot;> <img tal:replace=quot;structure item_icon/html_tagquot; /> <span tal:replace=quot;node/Titlequot;>Selected Item Title</span> </a> </div> ... Note: You may need to restart Zope after you have made changes on the filesytem, to have your changes rendered. Look at the emphasized part. What we do is that instead of using real namespace attributes, we use an emulation that stores attributes into the html class attribute. The advantage is that we can have more attributes encoded into the same class after each other, and even preserve the existing classes on the node. 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 anything else 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: <li class=quot;navTreeItem visualNoMarkerquot;> <div> <a class=quot;state-private kssattr-iscurrent-Falsequot; title=quot;quot; href=quot;http://l12/demo11/folder1/page1quot;> </a> </div> </li> To access this attributes from KSS, we use the kssAttr value provider. This works very similarly to nodeAttr, only in this case it looks for the special kss parameters, and it is able to understand the encoding of kss attributes into classes, as described above. To use it, let us extend our kss rules in the following way: li.navTreeItem a:click { evt-click-preventdefault: True; action-server: response1; response1-mymessage: 'clicked on navtree item'; response1-href: nodeAttr(quot;hrefquot;); response1-value: nodeContent(); response1-iscurrent: kssAttr(iscurrent); } a.navTreeCurrentItem:click { response1-mymessage: 'clicked on the CURRENT navtree item'; } And, expand our response1 script too. Also add iscurrent=None to the end of the parameter list:
  • 23. ... 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>quot;%squot;</b>' % (href, ) if value is not None: txt += '<br><span>value=<b>quot;%squot;</b>' % (value, ) if iscurrent is not None: txt += '<br><span>path=<b>quot;%squot;</b>' % (iscurrent, ) ... Let us test the results now. All navtree items now print if they are current or not (except for Home which 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 quot;Homequot; folder as well. This will only have a visible result if the navtree portlet becomes enabled on the portal root, too. Starting on this path, we could arrive to an ajaxified navtree portlet. We do not do that now, however we can conclude this. The ajaxification of existing components may require the customization of templates. 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 implement in-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), a textarea field (the description), and a rich widget (text) controlled by the kupu editor as default. We will start 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 is already 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: <div id=quot;archetypes-fieldname-titlequot; class=quot;field ArchetypesStringWidget kssattr-atfieldname-titlequot;> <span/> <label for=quot;titlequot;>Title</label> <span class=quot;fieldRequiredquot; title=quot;Requiredquot;> (Required) </span> <div id=quot;title_helpquot; class=quot;formHelpquot;/> <div class=quot;fieldErrorBoxquot;/> <input id=quot;titlequot; class=quot;blurrable firstToFocusquot; type=quot;textquot; maxlength=quot;255quot; size=quot;30quot; value=quot;Welcome to Plonequot; name=quot;titlequot;/> </div> The CSS rule that would bind to the input node is div#archetypes-fieldname-title input and the event that we will use is blur. This event is a native browser event and gets triggered when the control looses focus. So let's add the following rule to our kss:
  • 24. div#archetypes-fieldname-title input:blur { action-client: alert; } 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 like our response1 method did: # import Through-The-Web(TTW) API from kss.core.ttwapi import startKSSCommands from kss.core.ttwapi import getKSSCommandSet from kss.core.ttwapi import renderKSSCommands # start a view for commands startKSSCommands(context, context.REQUEST) # use the parameters txt = '<h1>In validation.</h1><span>at %s</span>' % (DateTime(), ) # add a command core = getKSSCommandSet('core') core.replaceInnerHTML('#portal-siteactions', txt) # render the commands return renderKSSCommands() And change the kss to call it: div#archetypes-fieldname-title input:blur { action-server: validateField; } This is good so far, but now we want to pass the value of the input form to the field. We have a few possibilities 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 quot;currentquot; form is selected by the node on which the event has been executed, in this case the input node. currentFormVar() supposes that the node on which the event has been triggered is itself a 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 the input node is the same on which the blur event is triggered. So our kss rule will be: div#archetypes-fieldname-title input:blur { action-server: validateField; validateField-value: currentFormVar(); } Then we change our validateField script as follows (and we also add value as a parameter to the script):
  • 25. # import Through-The-Web(TTW) API from kss.core.ttwapi import startKSSCommands from kss.core.ttwapi import getKSSCommandSet from kss.core.ttwapi import renderKSSCommands from kss.core.ttwapi import force_unicode # start a view for commands startKSSCommands(context, context.REQUEST) # use the parameters value = force_unicode(value, 'utf') txt = '<h1>In validation.</h1><span>at %s</span>' % (DateTime(), ) txt +='<br><span>value=<b>quot;%squot;</b>' % (value, ) # add a command core = getKSSCommandSet('core') core.replaceInnerHTML('#portal-siteactions', txt) # render the commands return renderKSSCommands() We can see now that the script really passes the value that is in the title field, when we leave it. How would we validate it? Well, we use Archetypes to do it. This method will return an error in case the field does not validate, or None if it is all right: instance = self field = instance.getField('title') error = field.validate(value, instance, {}) 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 is already implemented in KSS, so we don't have to implement it ourselves! This can be invoked in the following way: plone = getKSSCommandSet(quot;plonequot;) plone.issuePortalMessage(error) This calls something we call a command. It can be thought of a complex command but in fact it can emit a more complex series of commands for sending back to the client. It also does other things, like decides if there is an error at all (and if there is not, it disables the status message), and also does a translation of the thing. The full action script will be this in the end:
  • 26. # import Through-The-Web(TTW) API from kss.core.ttwapi import startKSSCommands from kss.core.ttwapi import getKSSCommandSet from kss.core.ttwapi import renderKSSCommands from kss.core.ttwapi import force_unicode # start a view for commands startKSSCommands(context, context.REQUEST) # use the parameters value = force_unicode(value, 'utf') txt = '<h1>In validation.</h1><span>at %s</span>' % (DateTime(), ) txt +='<br><span>value=<b>quot;%squot;</b>' % (value, ) # add a command core = getKSSCommandSet('core') core.replaceInnerHTML('#portal-siteactions', txt) # do validation and send back result instance = context field = instance.getField('title') error = field.validate(value, instance, {}) plone = getKSSCommandSet(quot;plonequot;) plone.issuePortalMessage(error) # render the commands return renderKSSCommands() To test it in working, let us clear the title. Leave the field: we see the orange message appear. Enter something again, leave the field, and see the message disappear. At the same time, we can see the status updated in the upper right corner too, since we left that part in as well. For a complete experience, we are happy to observe that we can also click on the navtree items and even to the global tabs! In other words: we have not broken our previous programs. Naturally to implement in-place validation with all the archetypes fields, more things are needed. Most importantly, we need a way to be able to bind not only to a single field, but to all the fields (of the same type) at once. In addition we don't only have text inputs but checkboxes, dropdowns and more complex 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 a deeper 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 a single node markup thatr becames effective for entire page regions. For now, however, we conclude the current lesson and enjoy the fruit of our efforts. Because by now we have learned what we need to know 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 template breaks for you, you might want to customize the template from the exact Plone version you use, instead of 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: instance = self.context.aq_inner The aq_inner is in some cases necessary to provide the exactly right acquisition context for the Archetypes scripts.
  • 27. Also when we use the commandset adapters, the parameter for the interface that does the adaptation should be self, not commands (exactly how we did in the previous lessons).