Requirements
For a truly engaging experience website visitors need to be able to interact with your website. Interactions can reach from basic ways to search content, register and give feedback to enabling user-to-user communication and facilitating business processes such as product registration and customer self care.
End user interactions should be configurable in the editorial interface by non-technical users in the editorial interface of the system. It should, for example, be possible to place interaction components such as Login and Search buttons on pages just like any other content, configure layout and business rules etc.
Solution
For the Blueprint website, the term "action" denotes a functionality that enables users to interact with the website.
Examples:
Search: The "search" action lets user to enter a query into a form field. After processing the search, a search result is displayed to the user.
Login: This action can be used by users to login to the website by adding user name and password credentials. A successful login changes the state web application's state for the user and offers him additional actions such as editing his user profile.
From an editor's perspective, all actions are represented by content objects of type
CMAction
. This enables an editor to add an action content to a page, for
example by inserting it to the navigation linklist
property. When rendering the page, this action
object is rendered by a certain template that (for example) renders a search form. The submitted
form data (the query, for instance) is received by a handler that does some processing (passing
the query to the search engine, for instance) and that provides a model containing the search
action result.
This section demonstrates the steps necessary to add new actions to CoreMedia Blueprint. It also helps to understand the currently available actions.
Standard Actions
As stated above, all actions are represented as CMAction
contents in the
repository. These contents can be used as placeholders in terms of the "substitution"
mechanism described in the [
CAE Developer Manual]. An example for adding a new action: Consider an action where users can submit their email
addresses in order to receive a newsletter.
Create a bean that represents the subscription form and add an adequate template.
public class SubscriptionForm { public String email; public void setEmail(String email) { this.email = email; } public String getEmail() { return email; } }
SubscriptionForm.asTeaser.jsp
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <%--@elvariable id="self" type="com.mycompany.SubscriptionForm "--%> <%--@elvariable id="subscriptionForm" type="com.mycompany.SubscriptionForm "--%> <%--@elvariable id="cmpage" type="com.coremedia.blueprint.common.contentbeans.Page"--%> <cm:link target="${cmpage.linkable}" var="redirectUri"/> <cm:link target="${self}" var="subscriptionUri"> <cm:param name="return" value="${redirectUri}"/> </cm:link> <form:form id="subscriptionForm" modelAttribute="subscriptionForm" action="${subscriptionUri}" method="post"> <form:input path="email"/> <input type="submit"/> </form:form>
Add a handler that is able to process the subscription as well as a link scheme that builds links pointing to the handler.
@Link @RequestMapping public class SubscriptionHandler { @RequestMapping(value="/subscribe", method=RequestMethod.POST) public ModelAndView handleSubscription(@RequestParam(value="return", required=true) String redirectUri, @ModelAttribute("subscriptionForm") SubscriptionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException { doSubscribe(request.getSession(), form.getEmail()); response.sendRedirect(redirectUri); return null; } @Link(type=SubscriptionForm.class, parameter="return", uri="/subscribe") public UriComponents createSubscriptionLink(UriComponentsBuilder uri, Map<String,Object> parameters) { return uri.queryParam("return", (String) parameters.get("return")).build(); } ... }
Don't forget to register this class as a bean in the Spring application context.
Define an action substitution.
public class SubscriptionHandler { ... @Substitution("com.coremedia.subscription", modelAttribute="subscriptionForm") public SubscriptionForm createSubscriptionSubstitution(CMAction original, HttpServletRequest request) { return new SubscriptionForm(); } ... }
Notes
The parameters
original
as well asrequest
are optional and might be omitted here. But in a more proper implementation it might be useful to have access to the original bean and the current request.The optional
modelAttribute
causes the substitution to be become available as a request attributesubscriptionForm
. This is useful when using dealing with the Spring form tag library (see above).
Create a newsletter action content
Create a content of type
CMAction
Set the
id
property to valuecom.coremedia.subscription
Insert this content to a page's teaser link list.
Here is what happens when opening the page by sending an HTTP request:
The request will be accepted by the
PageHandler
that builds aModelAndView
containing thePage
model. This model's tree of content beans contains the newCMAction
instance.The model will be rendered by initially invoking
Page.jsp
for thePage
bean.When the
CMAction
is going to be rendered in the teaser list, the templateCMAction.asTeaser.jsp
is invoked. This template substitutes theCMAction
bean by invoking thecm:substitute
function while using the IDcom.coremedia.subscription
.The substitution framework invokes the method
#createSubscriptionSubstitution
after checking whetherSubstitutionRegistry#register
has been invoked by any handler for his ID (which hasn't happened here). As the result, the substitutions result is a bean of typeSubscriptionForm
.The above mentioned template
CMAction.asTeaser.jsp
therefore delegates toSubscriptionForm.asTeaser.jsp
then.While rendering
SubscriptionForm.asTeaser.jsp
, a link pointing to this form bean is going to be built. The method#createSubscriptionLink
is chosen as a link scheme so that the link points to the handler method#handleSubscription
.After the user has received the rendered page, he might enter his email address and press the submit button.
This new (POST) request is accepted by the mentioned handler method
#handleSubscription
that performs the subscription and redirects the original page then so that the first step of this flow is repeated.
Of course, a more proper implementation could mark the subscription state (subscribed or not) in a
session/cookie and would return an UnsubscribeForm
from
#createSubscriptionSubstitution
depending on this state.
Webflow Actions
Spring Webflow (http://www.springsource.org/spring-web-flow) is a framework for building complex form based applications consisting of multiple steps. Webflow based actions can be integrated into Blueprint as well. This section describes the steps of how to integrate this kind of actions.
In CoreMedia Blueprint the PageActionHandler
takes care of generally handling Webflow actions. The flow's out coming model is automatically
wrapped into a bean WebflowActionState
. A special aspect of this bean is
that it implements HasCustomType
and therefore is able to control the
lookup of the of the matching template.
Place your flow definition file somewhere below a package named
webflow
somewhere in the classpath. The name of the flow definition file should be<action_id>.xml
. Example: For an actioncom.mycompany.MyFlowAction
you might create a filecom.mycompany.MyFlowAction.xml
that can be placed below a packagecom.coremedia.blueprint.mycompany.webflow
.For every flow view (such as "success" or "failure") create a JSP template. The template name needs to match the action id. Example: The action
com.mycompany.MyFlowAction
requires templates to be named.../templates/com.mycompany/MyFlowAction.<flowView>.jsp
. These templates will be invoked for the mentioned beans of typeWebflowActionState
.Create (and integrate) a new document of type
CMAction
and set the propertyid
to the action id (such ascom.mycompany.MyFlowAction
) and the propertytype
towebflow
.