Author: atsebro
Date: 2009-04-21 11:39:27 -0400 (Tue, 21 Apr 2009)
New Revision: 13740
Modified:
trunk/docs/realworld_app_guide/en/src/main/docbook/modules/hiw.xml
Log:
RF-5768: Real World Demo Application Tutorial Update for 3.3.1 release
Modified: trunk/docs/realworld_app_guide/en/src/main/docbook/modules/hiw.xml
===================================================================
--- trunk/docs/realworld_app_guide/en/src/main/docbook/modules/hiw.xml 2009-04-21 15:36:50
UTC (rev 13739)
+++ trunk/docs/realworld_app_guide/en/src/main/docbook/modules/hiw.xml 2009-04-21 15:39:27
UTC (rev 13740)
@@ -408,5 +408,394 @@
</section>
+ <section>
+ <title>Slide show</title>
+
+ <para>The slide-show feature in the Photo Album Demo can be enabled by
clicking "Start Slideshow" link from two different places in the
application:
+ 1) from user's album preview
(<code>/web/src/main/webapp/image/albumInfo.xhtml</code>) and 2) from a
particular photo preview (<code>src/main/webapp/image/imageInfo.xhtml</code>).
+ Both of two mentioned XHTML files include slideshow with the help of
Facelets <<emphasis
role="bold"><property>ui:include</property></emphasis> tag
+ (for more information about <<emphasis
role="bold"><property>ui:include</property></emphasis> see
Facelets Reference Guide —
+ <ulink
url="http://www.jsftoolbox.com/documentation/facelets/01-Introductio...>).
+ </para>
+
+ <para>
+ The <code>startSlideshow()</code> method of
<code>SlideshowManager.java</code> has two implementations.
+ The first implementation is designed to activate the slide-show from an
album preview (<code>/image/albumInfo.xhtml</code>) when no photo is selected
in the current image list.
+ The method iterates over all photos of a particular album starting from
the first one in the list. Look at the <code>SlideshowManager.java</code>
listing below:
+ </para>
+ <programlisting role="JAVA"><![CDATA[...
+public void startSlideshow(){
+ active = true;
+ this.slideshowIndex = 0;
+ if(model.getImages() == null || model.getImages().size() < 1){
+ stopSlideshow();
+ Events.instance().raiseEvent(Constants.ADD_ERROR_EVENT, "No images for
slideshow!");
+ return;
+ }
+ this.selectedImage = model.getImages().get(this.slideshowIndex);
+ this.selectedImage.getAlbum().visitImage(selectedImage, true);
+}
+...]]></programlisting>
+
+ <para>
+ The second implementation of the
<code>startSlideshow()</code> method is activated when a link to slide-show is
clicked from a particular photo preview.
+ This method iterates over the rest of photos starting from the current
selected one:
+ </para>
+ <programlisting role="JAVA"><![CDATA[...
+public void startSlideshow(Image selectedImage){
+ active = true;
+ if(model.getImages() == null || model.getImages().size() < 1){
+ stopSlideshow();
+ Events.instance().raiseEvent(Constants.ADD_ERROR_EVENT, "No images for
slideshow!");
+ return;
+ }
+ this.slideshowIndex = model.getImages().indexOf(selectedImage);
+ this.selectedImage = selectedImage;
+ this.selectedImage.getAlbum().visitImage(selectedImage, true);
+}
+...]]></programlisting>
+
+ <para>
+ Both implementations of <code>startSlideshow()</code>
method set active property to true.
+ This <code>true</code> makes two important slide-show parts
(slide-show modal panel and slide-show poller) render.
+ </para>
+ <para>
+ The slide-show modal panel is kept in the
<code>web/src/main/webapp/includes/image/slideshow.xhtml</code> file and
referred from the corresponding pages with the help of <emphasis
role="bold"><property><ui:include></property></emphasis>
Facelets tag:
+ </para>
+
+ <programlisting role="XML"><![CDATA[...
+<ui:include src="/includes/image/slideshow.xhtml"/>
+...]]></programlisting>
+
+ <para>
+ Have a look at
<code>web/src/main/webapp/includes/image/slideshow.xhtml</code> file:
+ </para>
+
+ <programlisting role="XML"><![CDATA[...
+<ui:composition
xmlns="http://www.w3.org/1999/xhtml"...>
+ <rich:modalPanel showWhenRendered="#{slideshow.active}"
+ domElementAttachment="parent"
+ id="slideShowModalPanel"
+ width="650"
+ onshow="showPictureEffect();"
+ height="650">
+ <f:facet name="controls">
+ <h:panelGroup>
+ <h:graphicImage value="/img/modal/close.png"
style="cursor:pointer" id="hidelink">
+ <a4j:support event="onclick"
actionListener="#{slideshow.stopSlideshow}" reRender="slideShowForm,
mainArea, tree" />
+ </h:graphicImage>
+ </h:panelGroup>
+ </f:facet>
+ ...
+ </rich:modalPanel>
+</ui:composition>
+...]]></programlisting>
+
+ <para>
+ The modal panel is always rendered on the page in a hidden state.
+ Activating the slide-show renders the main area, so the slide-show
modal panel appears in the closest to an observer layer.
+ </para>
+
+ <programlisting role="XML"><![CDATA[...
+<a4j:commandLink styleClass="slideshow-link"
+ actionListener="#{slideshow.startSlideshow()}"
+ reRender="slideShowForm, mainArea">
+ ...
+</a4j:commandLink>
+...]]></programlisting>
+ <para>
+ Slide-show poller is enabled if the slide-show is activated:
+ </para>
+ <programlisting role="XML"><![CDATA[...
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
+
xmlns:s="http://jboss.com/products/seam/taglib"
+
xmlns:ui="http://java.sun.com/jsf/facelets"
+
xmlns:f="http://java.sun.com/jsf/core"
+
xmlns:h="http://java.sun.com/jsf/html"
+
xmlns:rich="http://richfaces.org/rich"
+
xmlns:a4j="http://richfaces.org/a4j"
+
xmlns:richx="http://richfaces.org/richx">
+ <a4j:form id="slideShowForm">
+ <a4j:poll reRender="slideshowImage"
+ actionListener="#{slideshow.showNextImage()}"
+ interval="#{slideshow.interval}"
+ enabled="#{slideshow.active}"
+ onsubmit="hidePictureEffect()"
+ oncomplete="showPictureEffect();"/>
+ </a4j:form>
+</ui:composition>
+...]]></programlisting>
+ <para>
+ The slide-show poller sends the request for the next image
(<code>showNextImage()</code> method) each four seconds.
+ The interval is defined in the interval property of the
<code>SlideshowManager.java</code> and refers to a
<code>INITIAL.DELAY</code> constant (<code>constants.java</code>).
+ When the <emphasis
role="bold"><property><a4j:poll></property></emphasis>
component receives a new image it renders the slideShowImage area
(<code>/web/src/main/webapp/image/slideshow.xhtml</code>):
+ </para>
+ <programlisting role="XML"><![CDATA[...
+<h:panelGroup id="slideshowImage">
+ <a4j:mediaOutput id="imgSlideShow"
+ element="img"
+ styleClass="main-image"
+ createContent="#{imageLoader.paintImage}"
+ style="opacity: 0.1"
+ value="#{slideshow.selectedImage != null?
fileManager.transformPath(slideshow.selectedImage.path,
'_medium'):null}">
+ <rich:toolTip followMouse="true"
+ direction="top-right"
+ showDelay="500"
+ styleClass="tooltip">
+ <span style="white-space: nowrap">
#{slideshow.selectedImage.description} </span>
+ </rich:toolTip>
+ </a4j:mediaOutput>
+</h:panelGroup>
+...]]></programlisting>
+
+ <para>
+ The <emphasis
role="bold"><property>a4j:mediaOtuput</property></emphasis>
component has a value attribute that takes a path to photos as a value and renders photos
with the help of a
<emphasis><property>createContent</property></emphasis> attribute.
The showNextImage works cyclically.
+ </para>
+
+ <para>
+ There is <emphasis
role="bold"><property>rich:effect</property></emphasis>
that makes rendering process look more effective
(<code>web/src/main/webapp/includes/image/slideshow.xhtml</code>):
+ </para>
+ <programlisting role="XML"><![CDATA[...
+<rich:effect name="hidePictureEffect" type="Opacity"
params="duration:0.4, from:1.0, to:0.0" for="imgSlideShow" />
+<rich:effect name="showPictureEffect" type="Opacity"
params="duration:0.4, from:0.1, to:1.0" for="imgSlideShow" />
+...]]></programlisting>
+
+ <para>
+ The described above implements a modal panel with photos that change
each other in order they store in an album.
+ </para>
+ <figure>
+ <title>Slide show</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata
fileref="images/slide_show.png"/>
+ </imageobject>
+ </mediaobject>
+ </figure>
+
+ <para>
+ To quit the slide-show user clicks "Close window"
button on the slide-show panel and <code>stopSlideshow()</code> method is
activated.
+ </para>
+ <programlisting role="JAVA"><![CDATA[...
+@Observer("stopSlideshow")
+ public void stopSlideshow(){
+ active = false;
+ this.selectedImage = null;
+ this.slideshowIndex = 0;
+ }
+...]]></programlisting>
+
+ </section>
+ <section>
+ <title>Image Size control</title>
+
+ <section>
+ <title>Building the control with
<rich:inputNumberSlider></title>
+ <para>
+ The <emphasis
role="bold"><property>rich:inputNumberSlider</property></emphasis>
component in the Photo Album Demo is used as a control that helps a user to change photos
size while previewing an album.
+ A handler position on the slider track corresponds to a particular
value of image size.
+ The component is included into the page with the help of <emphasis
role="bold"><property>ui:include</property></emphasis>:
+ </para>
+ <programlisting role="XML"><![CDATA[...
+<ui:include src="/includes/misc/inputNumberSlider.xhtml"/>
+...]]></programlisting>
+
+ <para>
+ Now let's have a look at
<code>src/main/webapp/includes/misc/inputNumberSlider.xhtml</code> file:
+ </para>
+ <programlisting role="XML"><![CDATA[...
+<ui:composition ...>
+ <div>
+ <rich:inputNumberSlider value="#{imageSizeHelper.value}"
+ minValue="80"
+ maxValue="200"
+ step="40"
+ enableManualInput="false"
+ showArrows="false"
+ showBoundaryValues="true"
+ showInput="false">
+ <a4j:support event="onchange"
reRender="userAlbumImages"/>
+ </rich:inputNumberSlider>
+ </div>
+</ui:composition>
+...]]></programlisting>
+ <para>
+ On each slider position change the <emphasis
role="bold"><property>a4j:support</property></emphasis>
component invokes an Ajax request that passes a new set value into the
<code>ImageSizeHelper.java</code> class.
+ This class saves new photo dimensions, processes and assigns new
values to photo related attributes (a CSS class for new photo size, postfix for a new file
name, image background):
+ </para>
+ <programlisting role="JAVA"><![CDATA[...
+ public static enum ImageDimension {
+
+ SIZE_80(80), SIZE_120(120), SIZE_160(160), SIZE_200(200);
+
+ final static String CSS_CLASS = "preview_box_photo_";
+ final static String FILE_POSTFIX = "_small";
+ final static String IMAGE_BG = "/img/shell/frame_photo_%1$d.png";
+ final static String IMAGE_BG_STYLE = "width: %1$dpx; height: %1$dpx";
+
+ int x;
+ String bgStyle;
+ String cssClass;
+ String imageBgSrc;
+ String filePostfix;
+
+ private ImageDimension(int x) {
+ this.x = x;
+ this.bgStyle = String.format(IMAGE_BG_STYLE, x + 20);
+ cssClass = CSS_CLASS + x;
+ imageBgSrc = String.format(IMAGE_BG, (x == 160) ? 200 : x);
+ filePostfix = FILE_POSTFIX + x;
+ }
+ ...]]></programlisting>
+
+ <para>
+ After the <code>ImageSizeHelper.java</code> is worked
out the <emphasis
role="bold"><property>a4j:support</property></emphasis>
component renders user photos
+ (more exactly, the <emphasis
role="bold"><property>h:panelGroup</property></emphasis>
with <code>userAlbumImages</code> id that contains user photos)
+ correspondingly to a new set value. Here is
<code>web/src/main/webapp/includes/image/imageList.xhtml</code>:
+ </para>
+ <programlisting role="XML"><![CDATA[...
+<h:panelGroup id="userAlbumImages">
+ <a4j:repeat id="imageList" value="#{model.images}"
var="image" rows="20">
+ <h:panelGroup layout="block"
styleClass="#{imageSizeHelper.currentDimension.cssClass}">
+ <h:graphicImage styleClass="pr_photo_bg"
style="#{imageSizeHelper.currentDimension.imageBgStyle}"
value="#{imageSizeHelper.currentDimension.imageBg}" />
+ <h:panelGrid cellpadding="0">
+ <h:panelGroup>
+ <a4j:commandLink
actionListener="#{controller.showImage(image)}" reRender="mainArea,
tree">
+ <a4j:mediaOutput id="img"
element="img"
+ createContent="#{imageLoader.paintImage}"
+ style="border : 1px solid #FFFFFF;"
+ value="#{fileManager.transformPath(image.fullPath,
imageSizeHelper.currentDimension.filePostfix)}">
+ <f:param
value="#{imageSizeHelper.currentDimension.x}" name="x" />
+ <rich:dragSupport
rendered="#{controller.isUserImage(image)}" reRender="mainArea, tree"
id="dragSource" dragIndicator="dragIndicator"
+ dragType="image"
dragValue="#{image}">
+ <rich:dndParam
id="dragParam" name="label" value="#{image.name}" />
+ </rich:dragSupport>
+ <ui:include
src="/includes/contextMenu/CMForImage.xhtml" >
+ <ui:param name="image"
value="#{image}" />
+ </ui:include>
+ </a4j:mediaOutput>
+ </a4j:commandLink>
+ <br/>
+ </h:panelGroup>
+ </h:panelGrid>
+ <h:panelGroup layout="block"
styleClass="photo_name">#{image.name} </h:panelGroup>
+ <h:panelGroup layout="block"
styleClass="photo_data">
+ <h:outputText value="#{image.created}">
+ <f:convertDateTime />
+ </h:outputText>
+ </h:panelGroup>
+ </h:panelGroup>
+ </a4j:repeat>
+</h:panelGroup>
+...]]></programlisting>
+
+ <para>
+ When the <emphasis
role="bold"><property><rich:inputNumberSlider<</property></emphasis>
is rendered at first its default value for image size is 120 px.
+ </para>
+
+ <figure>
+ <title>Image size control</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata
fileref="images/image_size.png"/>
+ </imageobject>
+ </mediaobject>
+ </figure>
+
+ </section>
+
+ <section>
+ <title>Links in Photo Album Demo. Using the
<a4j:commandLink></title>
+ <para>
+ The main difference between <emphasis
role="bold"><property><h:commandLink></property></emphasis>
component is a) Ajax request that is generated on a click and b) dynamic rerendering of
the page after a response comes back.
+ It's not necessary to plug any support into the component, as
Ajax support is already built-in.
+ </para>
+ <para>
+ The <emphasis
role="bold"><property><a4j:commandLink></property></emphasis>
component is used widely in the application.
+ In the following example clicking on the <Edit>
link will rerender the main ares (watching area) of the application and open an album edit
form.
+ Each album has its owner and only owner can edit the album
contents.
+ The link <Edit> will be rendered only if the
current logged-in user is the owner of this album: the
<emphasis><property>"rendered"</property></emphasis>
attribute refers to the owner id and compares it with the current user id:
+ </para>
+ <programlisting role="XML"><![CDATA[...
+<a4j:commandLink rendered="#{model.selectedAlbum.owner.id == user.id}"
+ value="#{messages['album.edit']} "
+ actionListener="#{controller.startEditAlbum(model.selectedAlbum)}"
+ reRender="mainArea">
+</a4j:commandLink>
+...]]></programlisting>
+ <para>
+ The
<emphasis><property><value></property></emphasis>
attribute (text that will appear on the link) is picked up from the
<code>messages_en.properties</code> file.
+ Such property files are very useful e.g. for multi language
applications.
+ Each property is saved in the format
"name—value".
+ Let's have a look at this file in the JBDS:
+ </para>
+ <figure>
+ <title>Image size control</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata
fileref="images/messages_en_property.png"/>
+ </imageobject>
+ </mediaobject>
+ </figure>
+ </section>
+
+ <section>
+ <title>Error Reports in the Real World Demo</title>
+ <para>The main page of the application
<code>web/src/main/webapp/index.xhtml</code> includes
<code>web/src/main/webapp/includes/misc/errorPanel.xhtml</code>.
+ The error panel itself is always rendered but hidden if no error
appears. The listing below shows the part of <code>errorPanel.xhtml</code>
page:
+ </para>
+ <programlisting role="XML"><![CDATA[...
+<a4j:outputPanel id="errors" ajaxRendered="true">
+ <h:panelGroup rendered="#{errorHandlerBean.errorExist}">
+ <rich:modalPanel id="errorPanel"
+ showWhenRendered="true"
+ minWidth="300"
+ minHeight="200"
+ autosized="true">
+ ...
+ </rich:modalPanel>
+ </h:panelGroup>
+</a4j:outputPanel>
+...]]></programlisting>
+ <para>
+ Error checking happens each time the Ajax request is invoked by user
activity.
+ The <emphasis
role="bold"><property>rich:modalPanel</property></emphasis>
which wrapped with <emphasis
role="bold"><property><h:panelGroup></property></emphasis>
is rendered in case an error occurs,
+ its
<emphasis><property>"rendered"</property></emphasis>
attribute is binded with <code>isErrorExist</code> boolean method of
<code>errorHandlerBean</code> class.
+ </para>
+ <programlisting role="XML"><![CDATA[...
+package org.richfaces.realworld.ui;
+...
+@Name("errorHandlerBean")
+(a)Scope(ScopeType.EVENT)
+@AutoCreate
+public class ErrorHandlerBean {
+ private List<String> errors = new ArrayList<String>();
+
+ public List<String> getErrors() {
+ return errors;
+ }
+
+ public boolean isErrorExist(){
+ return errors.size() > 0 ;
+ }
+
+ @Observer(Constants.ADD_ERROR_EVENT)
+ public void addToErrors(String e){
+ errors.add(e);
+ }
+}
+...]]></programlisting>
+
+ <para>
+ The <code>addToErrors</code> method is annotated with
<code>@Observer</code> annotation which observes all methods in the
application with <code>ADD_ERROR_EVENT</code> annotation.
+ </para>
+
+ </section>
+
+
+
+ </section>
+
+
</chapter>