| <?xml version="1.0" encoding="UTF-8"?> |
| <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.0//EN" "../../dtd/document-v10.dtd"> |
| |
| <document> |
| <header> |
| <title>Portal: Using forms</title> |
| <subtitle>Overview</subtitle> |
| <authors> |
| <person name="Carsten Ziegeler" email="cziegeler@apache.org"/> |
| </authors> |
| </header> |
| |
| <body> |
| <s1 title="Overview"> |
| <p>This document gives an overview over how to use forms within the portal engine. |
| </p> |
| <p> |
| The sample portal that comes with the Cocoon distribution contains a working sample |
| for form handling. |
| </p> |
| </s1> |
| <s1 title="Including Applications"> |
| <p> |
| The portal allows to include a complete web application (with forms, links etc.) |
| that is build with Cocoon. Therefore it's possible to develop the forms |
| like you usually do with Cocoon without thinking about the portal. When you |
| are finished just include this application into the portal as a coplet. |
| The following shows you how to configure such an application as a coplet. |
| </p> |
| <p> |
| For including complete applications, the portal offers a specific coplet adapter, |
| the caching URI adapter. This is configured as the coplet base type: <em>CachingURICoplet</em>. |
| So each coplet you configure has to use this base type. |
| </p> |
| <p> |
| For each application you have to configure a coplet data object in the profile: |
| </p> |
| <source> |
| <![CDATA[... |
| <coplet-data id="app-test-two" name="standard"> |
| <title>Application Test</title> |
| <coplet-base-data>CachingURICoplet</coplet-base-data> |
| <attribute> |
| <name>buffer</name> |
| <value xsi:type="java:java.lang.Boolean" |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
| true |
| </value> |
| </attribute> |
| <attribute> |
| <name>handleParameters</name> |
| <value xsi:type="java:java.lang.Boolean" |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
| true |
| </value> |
| </attribute> |
| <attribute> |
| <name>uri</name> |
| <value xsi:type="java:java.lang.String" |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
| cocoon:/coplets/html/application |
| </value> |
| </attribute> |
| <attribute> |
| <name>temporary:application-uri</name> |
| <value xsi:type="java:java.lang.String" |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
| cocoon://samples/flow/jxcalc/ |
| </value> |
| </attribute> |
| </coplet-data> |
| ...]]> |
| </source> |
| <p> |
| As usual, the coplet data gets a unique id, a title and in this case the reference |
| to the <em>CachingURICoplet</em>. In addition the <em>buffer</em> attribute is used |
| to buffer the output of the coplet which avoids broken responses in the case of a |
| malformed stream comming from the included application. |
| </p> |
| <p> |
| The <em>handleParameters</em> attribute has to be set to <em>true</em> as well, as |
| the application has to handle it's own request parameters (for links or forms). |
| </p> |
| <p> |
| The <em>uri</em> attribute points to a Cocoon pipeline that will include your |
| application. This is not the pipeline of your application itself. The starting |
| URI for your application has to be configured using the attribute |
| <em>temporary:application-uri</em>. |
| </p> |
| <p> |
| With this configuration you can configure instances of the coplet for each |
| user. In addition a user can have several instances of the same application. |
| If you look at the provided samples, you see two instances of a flow example |
| and two instances of a forms sample at the same time for each user. |
| </p> |
| <p> |
| However, if you allow several instances per user of the same application, |
| this application has to be developed with this aspect in mind. More about |
| this topic later on. |
| </p> |
| <p> |
| So, basically this is all you have to do. Develop your application standalone |
| without the example and include it as outlined above. You can for example |
| invoke the sample above (cocoon://samples/flow/jxcalc/) directly without the |
| portal. |
| </p> |
| </s1> |
| <s1 title="Building forms"> |
| <p> |
| In this chapter we demonstrate using a sample how to build forms that can |
| be used within the portal. We will use Cocoon flow to define the logic for |
| the form, but for your own form you can of course use a different approach |
| as well. If you want to have complex forms, you can also use Cocoon forms |
| (Woody), but you have to be careful with correctly using JavaScript on |
| the client, which means you have to add the JavaScript to the response in |
| the main portal pipeline and not by the form itself. Or you avoid using |
| JavaScript on the client :) |
| </p> |
| <p> |
| As outlined in the previous chapter, you can define your form handling application |
| without taking care about the portal, so let's start developing it. |
| </p> |
| <s2 title="A Sample"> |
| <p> |
| We will use flow for the logic. Each time the application is invoked, the same |
| URI is used; this URI calls a flow function. Inside this function we check |
| whether we have to display the form or a different (result) page. |
| So our sitemap looks like this: |
| </p> |
| <source> |
| <![CDATA[... |
| <map:match pattern="form"> |
| <map:call function="form"/> |
| </map:match> |
| ...]]> |
| </source> |
| <p> |
| We have one single function in the flow, that checks if a session |
| attribute already contains a value or not. If no value is stored |
| in the session, this means that either the form has to be displayed |
| or the user just submitted some values. So we have to distinguish these |
| two cases as well: |
| </p> |
| <source> |
| <![CDATA[... |
| function form() { |
| // is the value stored in the session? |
| if ( cocoon.session.getAttribute("form") == null ) { |
| // No: is this a submit? |
| var name = cocoon.request.getParameter("name"); |
| if ( name == null ) { |
| // No: display the form |
| cocoon.sendPage("page/form", {}); |
| } else { |
| // It's a submit, so process the value |
| cocoon.session.setAttribute("form", name); |
| cocoon.sendPage("page/received", {"name" : name}); |
| } |
| } else { |
| // just display the value |
| var name = cocoon.session.getAttribute("form"); |
| cocoon.sendPage("page/content", {"name" : name}); |
| } |
| }...]]> |
| </source> |
| <p> |
| This schema allows to use the same pipeline for all purposes. However, |
| if you want a different design, you could e.g. use a different pipeline |
| for processing the form data etc. |
| </p> |
| <p> |
| In each case, the view is called which is a Cocoon pipeline. For |
| example the pipeline for the form reads an XML document that looks like |
| this: |
| </p> |
| <source> |
| <![CDATA[... |
| <page> |
| <title>Form</title> |
| <content> |
| <form method="post" action="form"> |
| <para>Please enter your <strong>name</strong>: <input type="text" name="name"/></para> |
| <input type="submit" name="submit" value="Enter"/> |
| </form> |
| </content> |
| </page> |
| }...]]> |
| </source> |
| <p> |
| As already pointed out, you develop your application like you would do without |
| the portal. Note in the sample above that the target of the form is the |
| <em>form</em> pipeline, containing the call to the flow. |
| </p> |
| <p> |
| So, how does this work? Each link (or target of a form action) is rewritten |
| by the portal engine. The link is transformed into an event. When now the |
| user activates such a link (or form) the event is send to the portal and |
| the portal updates the URI to call for the portlet. The <em>portal-html-eventlink</em> |
| transformer does the rewriting of all links and forms in combination |
| with the <em>portal-coplet</em> transformer that is one of the last |
| transformers in the main portal pipeline. |
| </p> |
| <p> |
| Each application portlet that isn't changed during this single request/response |
| cycle, isn't "activated" which means the corresponding application pipeline |
| is not called. |
| </p> |
| <p> |
| The portal caches the response of an application and serves the coplet out |
| of the cache until an action for this coplet is triggered. |
| </p> |
| </s2> |
| <s2 title="Several instances of an application"> |
| <p> |
| If you allow the user to have several instances of the same application, |
| the application has to be aware of this fact. Imagine that each instance |
| should have its own data set, but as the user is the same, the session |
| is shared by the instances. So, a unique identifier for each instance |
| is required. |
| </p> |
| <p> |
| The portal framework already supplies this identifier and passes it |
| as a request parameter to the pipeline of your application. The name |
| of the parameter is <em>copletid</em> and the value is the unique id. |
| In our sample, we pass this identifier to the flow script using an |
| input module: |
| </p> |
| <source> |
| <![CDATA[... |
| <map:match pattern="form"> |
| <map:call function="form"> |
| <map:parameter name="copletId" value="{request-param:copletid}"/> |
| </map:call> |
| </map:match> |
| ...]]> |
| </source> |
| <p> |
| In the flow script, we can now use this identifier to calculate a unique |
| session key to store the information: |
| </p> |
| <source> |
| // get the coplet id |
| var cid = cocoon.parameters["copletId"]; |
| var key = cid + "/myform"; |
| </source> |
| <p> |
| From now on you can use this key to get/store session attributes etc. In |
| addition you can use all features available to flow, like looking up |
| components and using them. |
| </p> |
| </s2> |
| <s2 title="Extending the Sample"> |
| <p> |
| Although the sample is very simple (or more precisly the form is very simple) |
| it demonstrates a possible way to build coplets that handle forms. You can |
| use this as a guideline for your own forms. |
| </p> |
| <p> |
| If you need, e.g. validation of the form, you can simply add the validation |
| in the flow script and return a corresponding view in the case of a |
| validation error. If you need a more complex processing, e.g. sending an |
| email, you can simply add this in the flow script as well. Or you can |
| code the logic into a Java class and call this class from the flow script. |
| </p> |
| <p> |
| In addition, you can use Cocoon forms with all the nice features (widgets, |
| validation, binding etc.) as well. You only have to provide a working set |
| of stylesheets that work in the portal. |
| </p> |
| </s2> |
| </s1> |
| </body> |
| </document> |