Author: artdaw
Date: 2009-04-25 07:26:25 -0400 (Sat, 25 Apr 2009)
New Revision: 13851
Added:
trunk/docs/realworld_app_guide/en/src/main/docbook/includes/
trunk/docs/realworld_app_guide/en/src/main/docbook/includes/hiw.xml
trunk/docs/realworld_app_guide/en/src/main/docbook/includes/navigationPanel.xml
trunk/docs/realworld_app_guide/en/src/main/docbook/includes/usedComponents.xml
trunk/docs/realworld_app_guide/en/src/main/docbook/modules/overview.xml
Removed:
trunk/docs/realworld_app_guide/en/src/main/docbook/modules/hiw.xml
Modified:
trunk/docs/realworld_app_guide/en/src/main/docbook/master.xml
trunk/docs/realworld_app_guide/en/src/main/docbook/modules/intro.xml
Log:
https://jira.jboss.org/jira/browse/RF-5768 - Photo Album review
Added: trunk/docs/realworld_app_guide/en/src/main/docbook/includes/hiw.xml
===================================================================
--- trunk/docs/realworld_app_guide/en/src/main/docbook/includes/hiw.xml
(rev 0)
+++ trunk/docs/realworld_app_guide/en/src/main/docbook/includes/hiw.xml 2009-04-25
11:26:25 UTC (rev 13851)
@@ -0,0 +1,480 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<section>
+ <title>Upload images</title>
+ <para>The implementation of <emphasis
role="bold"><property><rich:fileUpload></property></emphasis>
in the Photo Album Demo uses the embedded Flash module that adds extra functionality to
the component.
+ Here are the additional features that the Flash module provides:
+ </para>
+ <itemizedlist>
+ <listitem><para>Multiple files
choosing;</para></listitem>
+ <listitem><para>Specification of permitted file types in
the "Open File" dialog window;</para></listitem>
+ <listitem><para>A number of additional entry object
properties.</para></listitem>
+ </itemizedlist>
+
+ <para>
+ The photos uploading functionality is based on the
<code>/includes/fileUpload/fileUploader.xhtml page</code>. Let's have a
look at this page to find out how the uploader is implemented:
+ </para>
+
+ <programlisting role="XML"><![CDATA[<rich:fileUpload
style="margin : 0px 0px 20px 0px; width : 504px; height : 200px; background :
#f1f1f1; border : 1px solid #A1A1A1"
+ id="fileUpload"
+ allowFlash="true"
+ immediateUpload="false"
+ acceptedTypes="jpg,jpeg"
+ maxFilesQuantity="100"
+ autoclear="true"
+ fileUploadListener="#{fileUploadManager.listener}" >
+ <a4j:support event="onuploadcomplete" reRender="filesPanel, tree"
/>
+ <a4j:support event="onfileuploadcomplete" />
+ </rich:fileUpload>]]></programlisting>
+
+ <para>
+ The
"<emphasis><property>FileUploadListener</property></emphasis>"
attribute is binded with <code>fileUploadManager.listener</code> method which
makes the main job on the upload.
+ Below is the <code>fileUploadManager.listener</code>
method:
+ </para>
+ <programlisting role="JAVA"><![CDATA[public void
listener(UploadEvent event) throws Exception {
+ UploadItem item = event.getUploadItem();
+ Image image = constructImage(item);
+ try {
+ extractMetadata(item, image);
+ } catch (Exception e1) {
+ addError(item, image, Constants.FILE_PROCESSING_ERROR);
+ return;
+ }
+ image.setAlbum(model.getSelectedAlbum());
+ if(image.getAlbum() == null){
+ addError(item, image, Constants.NO_ALBUM_TO_DOWNLOAD_ERROR);
+ return;
+ }
+ try{
+ if(imageAction.isImageWithThisPathExist(image)){
+ image.setPath(generateNewPath(image.getPath()));
+ }
+ imageAction.addImage(image);
+ }catch(Exception e){
+ addError(item, image, Constants.IMAGE_SAVING_ERROR);
+ return;
+ }
+ if(!fileManager.addImage(image.getFullPath(), item.getFile().getPath())){
+ addError(item, image, Constants.FILE_SAVE_ERROR);
+ return;
+ }
+ fileWrapper.getFiles().add(image);
+ Events.instance().raiseEvent(Constants.IMAGE_ADDED_EVENT, image);
+ item.getFile().delete();
+}]]></programlisting>
+
+<para>
+ When a photo is added into the uploader, the first thing it does is saving this
photo in the file system.
+ The uploaded files are stored in the temporary folder in the computer file system.
+ For this purpose the value of the <code>createTempFile</code>
parameter in <code>Ajax4jsf Filter</code> section should be set to
<code>true</code>.
+ Below in the <code>Web.xml</code> file Ajax filter section:
+</para>
+
+ <programlisting role="XML"><![CDATA[...
+<init-param>
+ <param-name>createTempFiles</param-name>
+ <param-value>true</param-value>
+</init-param>
+...]]></programlisting>
+
+ <para>
+ Then the uploader creates an <code>Image</code> object and
extracts all image metadata such as Camera name, Image size etc.
+ Then it sets an album and generates a new path to it.
+ The system should save six different sizes of the photo.
+ After the photo was added into the database the system removes a temporary
file created for storage.
+ </para>
+
+
+ </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 example 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>
+ <section id="contextMenu">
+ <title>Context Menu</title>
+ <para>Work in progress...</para>
+ </section>
Property changes on: trunk/docs/realworld_app_guide/en/src/main/docbook/includes/hiw.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/docs/realworld_app_guide/en/src/main/docbook/includes/navigationPanel.xml
===================================================================
--- trunk/docs/realworld_app_guide/en/src/main/docbook/includes/navigationPanel.xml
(rev 0)
+++
trunk/docs/realworld_app_guide/en/src/main/docbook/includes/navigationPanel.xml 2009-04-25
11:26:25 UTC (rev 13851)
@@ -0,0 +1,350 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<section id="navigationPanel" xreflabel="navigationPanel">
+ <?dbhtml filename="navigationPanel.html"?>
+ <title>Navigation panel represented by
<rich:tree></title>
+ <para>
+ The <emphasis
role="bold"><property><rich:tree></property></emphasis>
component takes one of the main places
+ in the <property>Photo Album</property> and is
tightly bounded with the application logic.
+ It helps to represent
+ and implement inherently the "Shelves - Albums"
hierarchy.
+ Shelf is the highest possible level in the tree hierarchy, that
+ is used to group thematic albums and may contain as many albums
as needed.
+ </para>
+ <para>
+ There are two types of navigation panel in the application: for a
registered user and for a guest.
+ The difference between them is that the first one has a context
menu and drag-and-drop possibility.
+ </para>
+ <section>
+ <title>Navigation panel for a guest</title>
+
+ <!--para>
+ The <emphasis
role="bold"><property><rich:tree></property></emphasis>
component is designed for
+ hierarchical data presentation and is used to build a tree
structure.
+ The component also supports built-in drag-and-drop
functionality.
+ </para-->
+ <para>
+ Navigation panel for a guest is represented as a simple
+ <emphasis
role="bold"><property><rich:tree></property></emphasis>
component.
+ </para>
+ <para>
+ There are several ways to implement the <emphasis
role="bold"><property><rich:tree></property></emphasis>
on a page.
+ In the current application the <emphasis
role="bold"><property><rich:tree></property></emphasis>
is designed
+ using a model tag <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>.
+ </para>
+ <para>
+ The <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
component
+ has a
<emphasis><property>"nodes"</property></emphasis>
attribute that accepts
+ a collection of elements, so <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
+ iterates over the collection and renders hierarchical tree
structure on a page.
+ </para>
+ <para>
+ According to the "Shelves - Albums" hierarchy we
need two nested
+ <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
components.
+ The first one iterates through the Shelves collection
+ that is returned from the
<code>getPredefinedShelves()</code> method:
+ </para>
+ <programlisting role="JAVA"><![CDATA[...
+public List<Shelf> getPredefinedShelves() {
+ if (shelves == null) {
+ shelves = shelfAction.getPredefinedShelves();
+ }
+ return shelves;
+ }
+}
+...]]></programlisting>
+ <para>
+ The second <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
component
+ iterates through the Albums collection of the current Shelf which is available via
+ <emphasis><property>"var"</property></emphasis>
attribute.
+ The
<emphasis><property>"var"</property></emphasis>
attribute
+ is used to get access to the data object of the current collection element Shelf,
+ so it is possible to output any necessary data:
+ </para>
+ <!--
+ The <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
component accepts
+ a collection of elements, which are allowed to include lists,
arrays, maps, XML NodeList or NamedNodeMap, and iterate over it.
+ The <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
component repeats a hierarchical tree structure on a <code>xhtml</code> or
<code>jsp</code> page in a component tree and can be nested without any
limitations.
+ The code snippet below schematically shows the idea of how a tree
can be built on a page with the help of adaptors:
+
+
+
+ <programlisting
role="XML"><![CDATA[<rich:tree>
+ <rich:treeNodesAdaptor>
+ <rich:treeNode />
+
+ <rich:treeNodesAdaptor>
+ <rich:treeNode />
+
+ <rich:treeNodesAdaptor>
+ <rich:treeNode />
+ ...
+ </rich:treeNodesAdaptor>
+ </rich:treeNodesAdaptor>
+ </rich:treeNodesAdaptor>
+</rich:tree>]]></programlisting>
+
+ <para>
+ When using <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
component
+ there is no need to specify
+ the
<emphasis><property>"value"</property></emphasis>
and
+
<emphasis><property>"var"</property></emphasis>
attributes
+ for the <emphasis
role="bold"><property><rich:tree></property></emphasis>.
+ The values for nodes to render are passed directly into the
corresponding adaptor
+ and the component performs all the necessary iterative work
+ (vizit the <ulink
url="http://livedemo.exadel.com/richfaces-demo/richfaces/treeNodesAd...
page</ulink> on the RichFacesLiveDemo for more details).
+ </para>
+
+ <para>
+ Implementation of the <emphasis
role="bold"><property><rich:tree></property></emphasis>
in the application
+ is very similar to the model shown above.
+ The top <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
+ in the <emphasis
role="bold"><property><rich:tree></property></emphasis>
in the Photo Album
+ application is responsible for shelves rendering.
+ Its
<emphasis><property>"nodes"</property></emphasis>
attribute refers
+ to
<emphasis><property>getShelves()</property></emphasis> method of
the <code>ShelfManager</code> class
+ and gets the collection of shelves associated with the current
user including all shared shelves in the system.
+ Take a look at this method:
+ </para-->
+
+ <!-- para>
+ When the
<emphasis><property>"nodes"</property></emphasis>
attribute of the <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>,
which is responsible for shelves rendering, receives the collection of shelves,
+ the iteration process switches to the nested <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>,
which is responsible for albums.
+ The "albums" <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
<emphasis><property>"nodes"</property></emphasis>
attribute refers in its turn to the <code>albums</code> field of the
<code>Shelf</code> class
+ and takes a collection of all albums associated with the current
shelf.
+ The "albums" <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
renders all albums that belong to the current iterating shelf and then switches back to
the "shelf" <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
to render and iterate the next shelf in the shelves collection received earlier.
+ Here is how it looks in the
<code>webapp/includes/index/tree.xhtml</code> file:
+ </para-->
+
+ <programlisting role="XML"><![CDATA[...
+<rich:tree
+ adviseNodeOpened="#{treeManager.adviseNodeSelected}"
+ adviseNodeSelected="#{treeManager.adviseNodeSelected}"
+ ajaxSubmitSelection="false" id="PreDefinedTree"
+ treeNodeVar="treeNode" switchType="client"
+ iconCollapsed="/img/shell/tree_icon_plus.png"
+ iconExpanded="/img/shell/tree_icon_minus.png"
+ showConnectingLines="false">
+ <rich:treeNodesAdaptor nodes="#{shelfManager.getPredefinedShelves()}"
var="shelf">
+ <rich:treeNode style="cursor:pointer"
reRender="treeform,mainArea"
+ selectedClass="tree-selected-node">
+ <f:facet name="icon">
+ <h:graphicImage style="border: none"
value="/img/shell/tree_icon_shelf.png">
+ <a4j:support reRender="treeform,mainArea" event="onclick"
actionListener="#{controller.showShelf(shelf)}"
similarityGroupingId="sel" />
+ </h:graphicImage>
+ </f:facet>
+ <a4j:outputPanel >
+ <h:outputText style="cursor:pointer" value="#{shelf.name}"
/>
+ <h:outputText value=" :: " />
+ <strong>#{shelf.unvisitedImages.size()}</strong> new
+ <a4j:support reRender="treeform,mainArea" event="onclick"
actionListener="#{controller.showShelf(shelf)}"
similarityGroupingId="sel" />
+ </a4j:outputPanel>
+ </rich:treeNode>
+ <rich:treeNodesAdaptor var="album"
+ nodes="#{shelf.albums}">
+ <rich:treeNode style="cursor:pointer"
reRender="treeform,mainArea"
+ selectedClass="tree-selected-node"
+ icon="img/shell/tree_icon_album.png">
+ <f:facet name="iconLeaf">
+ <h:graphicImage style="border: none"
value="img/shell/tree_icon_album.png">
+ <a4j:support reRender="treeform,mainArea" event="onclick"
actionListener="#{controller.showAlbum(album)}"
similarityGroupingId="sel" />
+ </h:graphicImage>
+ </f:facet>
+ <a4j:outputPanel>
+ <h:outputText style="cursor:pointer" value="#{album.name}"
/>
+ <h:outputText value=" :: " />
+ <strong>#{album.unvisitedImages.size()}</strong> new
+ <a4j:support reRender="treeform,mainArea" event="onclick"
actionListener="#{controller.showAlbum(album)}"
similarityGroupingId="sel" />
+ </a4j:outputPanel>
+ </rich:treeNode>
+ </rich:treeNodesAdaptor>
+ </rich:treeNodesAdaptor>
+ </rich:tree>
+...]]></programlisting>
+
+ <para>
+ The illustration below shows how the navigation panel for a guest
is rendered on the page.
+ </para>
+
+ <figure>
+ <title>Shelves and albums nodes rendered with the help of
the <rich:treeNodesAdaptor></title>
+ <mediaobject>
+ <imageobject>
+ <imagedata
fileref="images/tree.png"/>
+ </imageobject>
+ </mediaobject>
+ </figure>
+
+</section>
+
+ <section>
+ <title>Navigation panel for a registered user</title>
+ <para>
+ As it was mentioned before a navigation panel for a registered user
has
+ two main features: drag-and-drop and context menu. Context menu is
described
+ in the <link linkend="contextMenu">"Context
menu"</link> chapter.
+ </para>
+ <para>
+ Drag-and-drop feature supported in the Photo Album application is
not so complicated
+ as it may seem from the first view.
+ In this application we can mark out two types of drag-and-drop:
+ one type takes place only inside the tree (between tree nodes)
+ and another one - between the watching area and the tree.
+ The difference is not considerable enough to describe two types
separately,
+ but also not at all insignificant to be omitted here.
+ </para>
+
+ <para>
+ The tree related components
+ (<emphasis
role="bold"><property><rich:tree></property></emphasis>
and <emphasis
role="bold"><property><rich:treeNode></property></emphasis>)
+ have their own attributes that provide drag-and-drop
functionality.
+ These attributes can be divided into two groups:
+ those which provide drag (dragValue, dragListener,
dragIndicator, dragType attributes)
+ and those which provide drop operations (dropValue, dropListener,
acceptedTypes, typeMapping).
+ </para>
+ <note>
+ <title>
+ Note:
+ </title>
+ <para>
+ Due to "Shelves - Albums - Photos"
hierarchy we can say that photos could be moved between albums,
+ albums could be moved between shelves.
+ To avoid a mishmash, it's not allowed to place photos
directly in shelves as well as nesting shelves
+ inside shelves or albums inside albums.
+ </para>
+ </note>
+ <para>
+ Let's explore how drag-and-drop works for albums.
+ </para>
+ <para>
+ All albums, that are represented as TreeNodes, must be marked
somehow for dragging.
+ For this purpose we use previously mentioned
+
<emphasis><property>"dragValue"</property></emphasis>,
+
<emphasis><property>"dragType"</property></emphasis>
+ attributes:
+ </para>
+ <programlisting
role="XML"><![CDATA[<rich:treeNodesAdaptor var="album"
nodes="#{shelf.albums}">
+ <rich:treeNode dragType="album" dragValue="#{album}"
reRender="mainArea, treeform">
+ ...
+ </rich:treeNode>
+</rich:treeNodesAdaptor>
+]]></programlisting>
+ <para>
+ To provide drop functionality for the marked albums we should mark
Shelves as drop zones in the application code too.
+ For this purpose we add the
<emphasis><property>"dropValue"</property></emphasis>
and
+
<emphasis><property>"acceptedTypes"</property></emphasis>
attributes to the "Shelf" node:
+ </para>
+ <programlisting
role="XML"><![CDATA[<rich:treeNodesAdaptor
nodes="#{shelfManager.getUserShelves()}" var="shelf">
+ <rich:treeNode acceptedTypes="album" dropValue="#{shelf}"
reRender="mainArea, treeform">
+ ...
+ </rich:treeNode>
+</rich:treeNodeAdaptor>]]></programlisting>
+ <para>
+ The
<emphasis><property>"acceptedTypes"</property></emphasis>
attribute tells the "Shelf" node
+ what types of dragged zones (albums in this case) it can accept.
+ We have specified "Album" node
+
<emphasis><property>"dragType"</property></emphasis>
as "album",
+ so the "Shelf" node can accept it.
+ </para>
+ <para>
+ Finally we need to specify listener in the
<emphasis><property>"dropListener"</property></emphasis>
+ attribute in order to process drop on the server side:
+ </para>
+ <programlisting role="XML"><![CDATA[<rich:tree
dragIndicator="dragIndicator"
dropListener="#{dndManager.processDrop}">
+ ...
+</rich:tree>]]></programlisting>
+ <para>
+ The <code>processDrop()</code> method is shown in the
listing below:
+ </para>
+ <programlisting role="JAVA"><![CDATA[...
+public void processDrop(DropEvent dropEvent) {
+ Dropzone dropzone = (Dropzone) dropEvent.getComponent();
+ Object dragValue = dropEvent.getDragValue();
+ Object dropValue = dropzone.getDropValue();
+ if(dragValue instanceof Image){
+ if(!((Album)dropValue).getOwner().getLogin().equals(user.getLogin())){
+ Events.instance().raiseEvent(Constants.ADD_ERROR_EVENT,
Constants.DND_PHOTO_ERROR);
+ return;
+ }
+ handleImage((Image)dragValue, (Album)dropValue);
+ }else if(dragValue instanceof Album){
+ if(!((Shelf)dropValue).getOwner().getLogin().equals(user.getLogin())){
+ Events.instance().raiseEvent(Constants.ADD_ERROR_EVENT,
Constants.DND_ALBUM_ERROR);
+ return;
+ }
+ handleAlbum((Album)dragValue, (Shelf)dropValue);
+ }
+}
+...]]></programlisting>
+ <para>
+ Here is the whole example of the "Navigation panel for a registered
user":
+ </para>
+<programlisting role="XML"><![CDATA[<rich:tree
+ ajaxSubmitSelection="false" dragIndicator="dragIndicator"
+ treeNodeVar="treeNode" switchType="client"
+ iconCollapsed="/img/shell/tree_icon_plus.png"
+ iconExpanded="/img/shell/tree_icon_minus.png"
+ dropListener="#{dndManager.processDrop}"
+ showConnectingLines="false">
+ <rich:treeNodesAdaptor nodes="#{shelfManager.getUserShelves()}"
+ var="shelf">
+ <rich:treeNode style="cursor:pointer" acceptedTypes="album"
dropValue="#{shelf}"
+ reRender="mainArea, treeform"
+ selectedClass="tree-selected-node">
+ <f:facet name="icon">
+ <h:graphicImage style="border: none"
value="/img/shell/tree_icon_shelf.png">
+ <a4j:support reRender="treeform, mainArea" event="onclick"
actionListener="#{controller.showShelf(shelf)}"
similarityGroupingId="sel" />
+ </h:graphicImage>
+ </f:facet>
+ <ui:include src="/includes/contextMenu/CMForShelf.xhtml" >
+ <ui:param name="shelf" value="#{shelf}" />
+ </ui:include>
+ <a4j:outputPanel>
+ <h:outputText style="cursor:pointer" value="#{shelf.name}"
/>
+ <h:outputText value=" :: " />
+ <strong>#{shelf.unvisitedImages.size()}</strong> new
+ <a4j:support reRender="treeform, mainArea" event="onclick"
actionListener="#{controller.showShelf(shelf)}"
similarityGroupingId="sel" />
+ </a4j:outputPanel>
+ </rich:treeNode>
+
+ <rich:treeNodesAdaptor var="album"
+ nodes="#{shelf.albums}">
+ <rich:treeNode style="cursor:pointer" reRender="mainArea,
treeform" dragType="album"
+ dragValue="#{album}" dropValue="#{album}"
+ acceptedTypes="image"
+ selectedClass="tree-selected-node"
+ icon="img/shell/tree_icon_album.png">
+ <f:facet name="iconLeaf">
+ <h:graphicImage style="border: none"
value="img/shell/tree_icon_album.png">
+ <a4j:support reRender="treeform, mainArea"
event="onclick" actionListener="#{controller.showAlbum(album)}"
similarityGroupingId="sel" />
+ </h:graphicImage>
+ </f:facet>
+ <ui:include src="/includes/contextMenu/CMForAlbum.xhtml" >
+ <ui:param name="album" value="#{album}" />
+ </ui:include>
+ <rich:dndParam name="label" type="drag"
value="#{album.name}" />
+ <a4j:outputPanel >
+ <h:outputText style="cursor:pointer" value="#{album.name}"
/>
+ <h:outputText value=" :: " />
+ <strong>#{album.unvisitedImages.size()}</strong> new
+ <a4j:support reRender="treeform, mainArea" event="onclick"
actionListener="#{controller.showAlbum(album)}"
similarityGroupingId="sel" />
+ </a4j:outputPanel>
+ </rich:treeNode>
+
+ </rich:treeNodesAdaptor>
+ </rich:treeNodesAdaptor>
+
+</rich:tree>]]></programlisting>
+ <para>
+ The illustration below shows how the described above drag-and-drop
features are rendered in the Photo Album.
+ </para>
+ <figure>
+ <title>Dragging the "Flora" album from
"Sport" shelf into the "Nature" (left) and the tree
after drag-and-drop (right).</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata
fileref="images/dnd.png"/>
+ </imageobject>
+ </mediaobject>
+ </figure>
+ </section>
+</section>
+
Property changes on:
trunk/docs/realworld_app_guide/en/src/main/docbook/includes/navigationPanel.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/docs/realworld_app_guide/en/src/main/docbook/includes/usedComponents.xml
===================================================================
--- trunk/docs/realworld_app_guide/en/src/main/docbook/includes/usedComponents.xml
(rev 0)
+++
trunk/docs/realworld_app_guide/en/src/main/docbook/includes/usedComponents.xml 2009-04-25
11:26:25 UTC (rev 13851)
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<section id="usedComponents" xreflabel="usedComponents">
+<?dbhtml filename="usedComponents.html"?>
+ <title>Used Components</title>
+ <para>
+ Below there is a list of components used in <property>Photo
Album</property>.
+ </para>
+ <table>
+ <title>Components used in "Photo Album Demo"</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Value</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+
<entry><a4j:commandLink></entry>
+ <entry>The component is very similar to the
<h:commandLink> component, the only difference is that an Ajax form submit
is generated on a click and it allows dynamic rerendering after a response comes back.
It's not necessary to plug any support into the component, as Ajax support is already
built in.</entry>
+ </row>
+ <row>
+
<entry><a4j:commandButton></entry>
+ <entry>The component is very similar to the
<h:commandButton> component, the only difference is that an Ajax form submit
is generated on a click and it allows dynamic rerendering after a response comes back.
It's not necessary to plug any support into the component, as Ajax support is already
built in.</entry>
+ </row>
+ <!-- row>
+ <entry>a4j:push</entry>
+ <entry>The component periodically perform Ajax
request to the server, to simulate 'push' data.</entry>
+ </row-->
+ <row>
+ <entry><a4j:poll></entry>
+ <entry>The component allows periodical sending
of Ajax requests to the server and is used for a page updating according to a specified
time interval.</entry>
+ </row>
+ <row>
+
<entry><rich:calendar></entry>
+ <entry>The component is used for creating
monthly calendar elements on a page.</entry>
+ </row>
+ <row>
+
<entry><rich:contextMenu></entry>
+ <entry>The component is used for creation of
multileveled context menus that are activated after a user defines an event
("onmouseover", "onclick", etc.) on any element on the
page.</entry>
+ </row>
+ <row>
+
<entry><rich:dataGrid></entry>
+ <entry>The component to render data as a grid
that allows choosing data from a model and obtains built-in support of Ajax
updates.</entry>
+ </row>
+ <row>
+
<entry><rich:datascroller></entry>
+ <entry>The component is designed for providing
the functionality of tables scrolling using Ajax requests.</entry>
+ </row>
+ <row>
+
<entry><rich:fileUpload></entry>
+ <entry>The component is designed to perform
Ajax-ed files upload to the server.</entry>
+ </row>
+ <row>
+
<entry><rich:inplaceInput></entry>
+ <entry>The component is an input component used
for displaying and editing data inputted. </entry>
+ </row>
+ <row>
+
<entry><rich:inplaceSelect></entry>
+ <entry>The component is used to create select
based inputs: it shows the value as text in one state and enables editing the value,
providing a list of options in another state.</entry>
+ </row>
+ <row>
+
<entry><rich:mediaOutput></entry>
+ <entry>The component implements one of the
basic features specified in the framework. The component is a facility for generating
images, video, sounds and other binary resources defined by you on-the-fly.</entry>
+ </row>
+ <row>
+
<entry><rich:modalPanel></entry>
+ <entry>The component implements a modal dialog
window. All operations in the main application window are locked out while this window is
active. Opening and closing the window is done with client JavaScript code.</entry>
+ </row>
+ <row>
+
<entry><rich:progressBar></entry>
+ <entry>The component is designed for displaying
a progress bar which shows the current status of the process. </entry>
+ </row>
+ <row>
+ <entry><rich:tree></entry>
+ <entry>The component is designed for
hierarchical data presentation and is applied for building a tree structure with a
drag-and-drop capability. The component also uses built-in drag and drop.</entry>
+ </row>
+
+
+ </tbody>
+ </tgroup>
+ </table>
+</section>
Property changes on:
trunk/docs/realworld_app_guide/en/src/main/docbook/includes/usedComponents.xml
___________________________________________________________________
Name: svn:executable
+ *
Modified: trunk/docs/realworld_app_guide/en/src/main/docbook/master.xml
===================================================================
--- trunk/docs/realworld_app_guide/en/src/main/docbook/master.xml 2009-04-25 10:41:47 UTC
(rev 13850)
+++ trunk/docs/realworld_app_guide/en/src/main/docbook/master.xml 2009-04-25 11:26:25 UTC
(rev 13851)
@@ -1,12 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
"http://www.docbook.org/xml/4.3CR3/docbookx.dtd" [
- <!ENTITY intro SYSTEM "modules/intro.xml">
- <!ENTITY application_overview SYSTEM
"modules/application_overview.xml">
- <!ENTITY getting_started SYSTEM "modules/getting_started.xml">
- <!ENTITY hiw SYSTEM "modules/hiw.xml">
-
+ <!ENTITY intro SYSTEM "modules/intro.xml">
+ <!ENTITY application_overview SYSTEM
"modules/application_overview.xml">
+ <!ENTITY overview SYSTEM "modules/overview.xml">
+ <!ENTITY getting_started SYSTEM "modules/getting_started.xml">
+
+ <!ENTITY usedComponents SYSTEM "includes/usedComponents.xml">
+ <!ENTITY navigationPanel SYSTEM "includes/navigationPanel.xml">
+ <!ENTITY hiw SYSTEM "includes/hiw.xml">
]>
<book>
@@ -44,8 +47,24 @@
<toc/>
&intro;
&getting_started;
+ &overview;
<!-- &application_overview; -->
+ <chapter id="patterns" xreflabel="patterns">
+ <?dbhtml filename="patterns.html"?>
+ <chapterinfo>
+ <keywordset>
+ <keyword>RichFaces</keyword>
+ </keywordset>
+ </chapterinfo>
+ <title>How it works</title>
+ <para>
+ In this chapter we explain how the Photo Album works.
+ </para>
+ <?forseChanks?>
+ &usedComponents;
+ &navigationPanel;
&hiw;
+ </chapter>
</book>
Deleted: 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-25 10:41:47
UTC (rev 13850)
+++ trunk/docs/realworld_app_guide/en/src/main/docbook/modules/hiw.xml 2009-04-25 11:26:25
UTC (rev 13851)
@@ -1,853 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<chapter id="patterns" xreflabel="patterns">
- <?dbhtml filename="patterns.html"?>
- <chapterinfo>
- <keywordset>
- <keyword>RichFaces</keyword>
-
- </keywordset>
- </chapterinfo>
- <title>How it works</title>
-
- <section>
- <title>Used Components</title>
-
- <para>Below there is a list of components used in <property>Photo
Album</property>.</para>
- <table>
- <title>Components used in "Photo Album
Demo"</title>
- <tgroup cols="2">
- <thead>
- <row>
- <entry>Name</entry>
- <entry>Value</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>a4j:commandLink</entry>
- <entry>The component is very similar to the
<h:commandLink> component, the only difference is that an Ajax form submit
is generated on a click and it allows dynamic rerendering after a response comes back.
It's not necessary to plug any support into the component, as Ajax support is already
built in.</entry>
- </row>
- <row>
- <entry>a4j:commandButton</entry>
- <entry>The component is very similar to the
<h:commandButton> component, the only difference is that an Ajax form submit
is generated on a click and it allows dynamic rerendering after a response comes back.
It's not necessary to plug any support into the component, as Ajax support is already
built in.</entry>
- </row>
- <!-- row>
- <entry>a4j:push</entry>
- <entry>The component periodically perform Ajax
request to the server, to simulate 'push' data.</entry>
- </row-->
- <row>
- <entry>a4j:poll</entry>
- <entry>The component allows periodical sending
of Ajax requests to the server and is used for a page updating according to a specified
time interval.</entry>
- </row>
- <row>
- <entry>rich:calendar</entry>
- <entry>The component is used for creating
monthly calendar elements on a page.</entry>
- </row>
- <row>
- <entry>rich:contextMenu</entry>
- <entry>The component is used for creation of
multileveled context menus that are activated after a user defines an event
("onmouseover", "onclick", etc.) on any element on the
page.</entry>
- </row>
- <row>
- <entry>rich:dataGrid</entry>
- <entry>The component to render data as a grid
that allows choosing data from a model and obtains built-in support of Ajax
updates.</entry>
- </row>
- <row>
- <entry>rich:datascroller</entry>
- <entry>The component is designed for providing
the functionality of tables scrolling using Ajax requests.</entry>
- </row>
- <row>
- <entry>rich:fileUpload</entry>
- <entry>The component is designed to perform
Ajax-ed files upload to the server.</entry>
- </row>
- <row>
- <entry>rich:inplaceInput</entry>
- <entry>The component is an input component used
for displaying and editing data inputted. </entry>
- </row>
- <row>
- <entry>rich:inplaceSelect</entry>
- <entry>The component is used to create select
based inputs: it shows the value as text in one state and enables editing the value,
providing a list of options in another state.</entry>
- </row>
- <row>
- <entry>rich:mediaOutput</entry>
- <entry>The component implements one of the
basic features specified in the framework. The component is a facility for generating
images, video, sounds and other binary resources defined by you on-the-fly.</entry>
- </row>
- <row>
- <entry>rich:modalPanel</entry>
- <entry>The component implements a modal dialog
window. All operations in the main application window are locked out while this window is
active. Opening and closing the window is done with client JavaScript code.</entry>
- </row>
- <row>
- <entry>rich:progressBar</entry>
- <entry>The component is designed for displaying
a progress bar which shows the current status of the process. </entry>
- </row>
- <row>
- <entry>rich:tree</entry>
- <entry>The component is designed for
hierarchical data presentation and is applied for building a tree structure with a
drag-and-drop capability. The component also uses built-in drag and drop.</entry>
- </row>
-
-
- </tbody>
- </tgroup>
- </table>
- </section>
-
- <section>
- <title>Navigation panel represented by
<rich:tree></title>
- <para>
- The <emphasis
role="bold"><property><rich:tree></property></emphasis>
component takes one of the main places
- in the <property>Photo Album</property> and is
tightly bounded with the application logic.
- It helps to represent
- and implement inherently the "Shelves - Albums"
hierarchy.
- Shelf is the highest possible level in the tree hierarchy, that
- is used to group thematic albums and may contain as many albums
as needed.
- </para>
- <para>
- There are two types of navigation panel in the application: for a
registered user and for a guest.
- The difference between them is that the first one has a context
menu and drag-and-drop possibility.
- </para>
- <section>
- <title>Navigation panel for a guest</title>
-
- <!--para>
- The <emphasis
role="bold"><property><rich:tree></property></emphasis>
component is designed for
- hierarchical data presentation and is used to build a tree
structure.
- The component also supports built-in drag-and-drop
functionality.
- </para-->
- <para>
- Navigation panel for a guest is represented as a simple
- <emphasis
role="bold"><property><rich:tree></property></emphasis>
component.
- </para>
- <para>
- There are several ways to implement the <emphasis
role="bold"><property><rich:tree></property></emphasis>
on a page.
- In the current application the <emphasis
role="bold"><property><rich:tree></property></emphasis>
is designed
- using a model tag <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>.
- </para>
- <para>
- The <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
component
- has a
<emphasis><property>"nodes"</property></emphasis>
attribute that accepts
- a collection of elements, so <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
- iterates over the collection and renders hierarchical tree
structure on a page.
- </para>
- <para>
- According to the "Shelves - Albums" hierarchy we
need two nested
- <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
components.
- The first one iterates through the Shelves collection
- that is returned from the
<code>getPredefinedShelves()</code> method:
- </para>
- <programlisting role="JAVA"><![CDATA[...
-public List<Shelf> getPredefinedShelves() {
- if (shelves == null) {
- shelves = shelfAction.getPredefinedShelves();
- }
- return shelves;
- }
-}
-...]]></programlisting>
- <para>
- The second <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
component
- iterates through the Albums collection of the current Shelf which is available via
- <emphasis><property>"var"</property></emphasis>
attribute.
- The
<emphasis><property>"var"</property></emphasis>
attribute
- is used to get access to the data object of the current collection element Shelf,
- so it is possible to output any necessary data:
- </para>
- <!--
- The <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
component accepts
- a collection of elements, which are allowed to include lists,
arrays, maps, XML NodeList or NamedNodeMap, and iterate over it.
- The <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
component repeats a hierarchical tree structure on a <code>xhtml</code> or
<code>jsp</code> page in a component tree and can be nested without any
limitations.
- The code snippet below schematically shows the idea of how a tree
can be built on a page with the help of adaptors:
-
-
-
- <programlisting
role="XML"><![CDATA[<rich:tree>
- <rich:treeNodesAdaptor>
- <rich:treeNode />
-
- <rich:treeNodesAdaptor>
- <rich:treeNode />
-
- <rich:treeNodesAdaptor>
- <rich:treeNode />
- ...
- </rich:treeNodesAdaptor>
- </rich:treeNodesAdaptor>
- </rich:treeNodesAdaptor>
-</rich:tree>]]></programlisting>
-
- <para>
- When using <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
component
- there is no need to specify
- the
<emphasis><property>"value"</property></emphasis>
and
-
<emphasis><property>"var"</property></emphasis>
attributes
- for the <emphasis
role="bold"><property><rich:tree></property></emphasis>.
- The values for nodes to render are passed directly into the
corresponding adaptor
- and the component performs all the necessary iterative work
- (vizit the <ulink
url="http://livedemo.exadel.com/richfaces-demo/richfaces/treeNodesAd...
page</ulink> on the RichFacesLiveDemo for more details).
- </para>
-
- <para>
- Implementation of the <emphasis
role="bold"><property><rich:tree></property></emphasis>
in the application
- is very similar to the model shown above.
- The top <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
- in the <emphasis
role="bold"><property><rich:tree></property></emphasis>
in the Photo Album
- application is responsible for shelves rendering.
- Its
<emphasis><property>"nodes"</property></emphasis>
attribute refers
- to
<emphasis><property>getShelves()</property></emphasis> method of
the <code>ShelfManager</code> class
- and gets the collection of shelves associated with the current
user including all shared shelves in the system.
- Take a look at this method:
- </para-->
-
- <!-- para>
- When the
<emphasis><property>"nodes"</property></emphasis>
attribute of the <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>,
which is responsible for shelves rendering, receives the collection of shelves,
- the iteration process switches to the nested <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>,
which is responsible for albums.
- The "albums" <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
<emphasis><property>"nodes"</property></emphasis>
attribute refers in its turn to the <code>albums</code> field of the
<code>Shelf</code> class
- and takes a collection of all albums associated with the current
shelf.
- The "albums" <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
renders all albums that belong to the current iterating shelf and then switches back to
the "shelf" <emphasis
role="bold"><property><rich:treeNodesAdaptor></property></emphasis>
to render and iterate the next shelf in the shelves collection received earlier.
- Here is how it looks in the
<code>webapp/includes/index/tree.xhtml</code> file:
- </para-->
-
- <programlisting role="XML"><![CDATA[...
-<rich:tree
- adviseNodeOpened="#{treeManager.adviseNodeSelected}"
- adviseNodeSelected="#{treeManager.adviseNodeSelected}"
- ajaxSubmitSelection="false" id="PreDefinedTree"
- treeNodeVar="treeNode" switchType="client"
- iconCollapsed="/img/shell/tree_icon_plus.png"
- iconExpanded="/img/shell/tree_icon_minus.png"
- showConnectingLines="false">
- <rich:treeNodesAdaptor nodes="#{shelfManager.getPredefinedShelves()}"
var="shelf">
- <rich:treeNode style="cursor:pointer"
reRender="treeform,mainArea"
- selectedClass="tree-selected-node">
- <f:facet name="icon">
- <h:graphicImage style="border: none"
value="/img/shell/tree_icon_shelf.png">
- <a4j:support reRender="treeform,mainArea" event="onclick"
actionListener="#{controller.showShelf(shelf)}"
similarityGroupingId="sel" />
- </h:graphicImage>
- </f:facet>
- <a4j:outputPanel >
- <h:outputText style="cursor:pointer" value="#{shelf.name}"
/>
- <h:outputText value=" :: " />
- <strong>#{shelf.unvisitedImages.size()}</strong> new
- <a4j:support reRender="treeform,mainArea" event="onclick"
actionListener="#{controller.showShelf(shelf)}"
similarityGroupingId="sel" />
- </a4j:outputPanel>
- </rich:treeNode>
- <rich:treeNodesAdaptor var="album"
- nodes="#{shelf.albums}">
- <rich:treeNode style="cursor:pointer"
reRender="treeform,mainArea"
- selectedClass="tree-selected-node"
- icon="img/shell/tree_icon_album.png">
- <f:facet name="iconLeaf">
- <h:graphicImage style="border: none"
value="img/shell/tree_icon_album.png">
- <a4j:support reRender="treeform,mainArea" event="onclick"
actionListener="#{controller.showAlbum(album)}"
similarityGroupingId="sel" />
- </h:graphicImage>
- </f:facet>
- <a4j:outputPanel>
- <h:outputText style="cursor:pointer" value="#{album.name}"
/>
- <h:outputText value=" :: " />
- <strong>#{album.unvisitedImages.size()}</strong> new
- <a4j:support reRender="treeform,mainArea" event="onclick"
actionListener="#{controller.showAlbum(album)}"
similarityGroupingId="sel" />
- </a4j:outputPanel>
- </rich:treeNode>
- </rich:treeNodesAdaptor>
- </rich:treeNodesAdaptor>
- </rich:tree>
-...]]></programlisting>
-
- <para>
- The illustration below shows how the navigation panel for a guest
is rendered on the page.
- </para>
-
- <figure>
- <title>Shelves and albums nodes rendered with the help of
the <rich:treeNodesAdaptor></title>
- <mediaobject>
- <imageobject>
- <imagedata
fileref="images/tree.png"/>
- </imageobject>
- </mediaobject>
- </figure>
-
-</section>
-
- <section>
- <title>Navigation panel for a registered user</title>
- <para>
- As it was mentioned before a navigation panel for a registered user
has
- two main features: drag-and-drop and context menu. Context menu is
described
- in the <link linkend="contextMenu">"Context
menu"</link> chapter.
- </para>
- <para>
- Drag-and-drop feature supported in the Photo Album application is
not so complicated
- as it may seem from the first view.
- In this application we can mark out two types of drag-and-drop:
- one type takes place only inside the tree (between tree nodes)
- and another one - between the watching area and the tree.
- The difference is not considerable enough to describe two types
separately,
- but also not at all insignificant to be omitted here.
- </para>
-
- <para>
- Let's examine drag-and-drop inside tree.
- The tree related components
- (<emphasis
role="bold"><property><rich:tree></property></emphasis>
and <emphasis
role="bold"><property><rich:treeNode></property></emphasis>)
- have their own attributes that provide drag-and-drop
functionality.
- These attributes can be divided into two groups:
- those which provide drag (dragValue, dragListener,
dragIndicator, dragType attributes)
- and those which provide drop operations (dropValue, dropListener,
acceptedTypes, typeMapping).
- </para>
- <note>
- <title>
- Note:
- </title>
- <para>
- Due to "Shelves - Albums - Photos"
hierarchy we can say that photos could be moved between albums,
- albums could be moved between shelves.
- To avoid a mishmash, it's not allowed to place photos
directly in shelves as well as nesting shelves
- inside shelves or albums inside albums.
- </para>
- </note>
-
- <para>
- All albums or images, which are assumed to be dragged, must be
marked somehow in the application code.
- For albums that are represented as tree nodes we will use
previously mentioned
- <property>drag group</property> attributes:
- </para>
- <programlisting
role="XML"><![CDATA[<rich:treeNodesAdaptor var="album"
nodes="#{shelf.albums}">
- <rich:treeNode dropListener="#{dndManager.processDrop}"
dragType="album" dragValue="#{album}" dropValue="#{album}"
acceptedTypes="image" reRender="mainArea, treeform">
- ...
- </rich:treeNode>
-</rich:treeNodesAdaptor>
-]]></programlisting>
-
- <para>
- To provide drop functionality for the marked albums we should mark
shelves as drop zones in the application code too.
- For this purpose we add the <emphasis
role="bold"><property>rich:dropSupport</property></emphasis>
component to the "shelf" node:
- </para>
- <programlisting
role="XML"><![CDATA[<rich:treeNodesAdaptor
nodes="#{shelfManager.getShelves()}" var="shelf">
- <rich:treeNode>
- <rich:dropSupport id="shelfDND" acceptedTypes="album"
dropValue="#{album}" dropListener="#{dndManager.processDrop}"
reRender="mainArea">
- </rich:dropSupport>
- ...
- </rich:treeNode>
-</rich:treeNodeAdaptor>]]></programlisting>
-
- <para>
- The
<emphasis><property>"acceptedType"</property></emphasis>
attribute tells the "shelf" node what types of dragged zones (albums in
this case) it can accept.
- The value for the
<emphasis><property>"acceptedType"</property></emphasis>
attribute corresponds the album
<emphasis><property>"dragType"</property></emphasis>
attribute.
- The method binding that will process drag-and-drop operation should
be pointed via
<emphasis><property>"dropListener"</property></emphasis>
attribute of the <emphasis
role="bold"><property>rich:tree</property></emphasis>.
- This method is shown in the listing below:
- </para>
- <programlisting role="JAVA"><![CDATA[...
-public void processDrop(DropEvent dropEvent) {
- Dropzone dropzone = (Dropzone) dropEvent.getComponent();
- Object dragValue = dropEvent.getDragValue();
- Object dropValue = dropzone.getDropValue();
- if(dragValue instanceof Image){
- if(!((Album)dropValue).getOwner().getLogin().equals(user.getLogin())){
- Events.instance().raiseEvent(Constants.ADD_ERROR_EVENT,
Constants.DND_PHOTO_ERROR);
- return;
- }
- handleImage((Image)dragValue, (Album)dropValue);
- }else if(dragValue instanceof Album){
- if(!((Shelf)dropValue).getOwner().getLogin().equals(user.getLogin())){
- Events.instance().raiseEvent(Constants.ADD_ERROR_EVENT,
Constants.DND_ALBUM_ERROR);
- return;
- }
- handleAlbum((Album)dragValue, (Shelf)dropValue);
- }
-}
-...]]></programlisting>
- <para>
- The illustration below shows how the described above drag-and-drop
features are rendered in the Photo Album Demo.
- </para>
- <figure>
- <title>Dragging the "Flora" album from
"Sport" shelf into the "Nature" (left) and the tree
after drag-and-drop (right).</title>
- <mediaobject>
- <imageobject>
- <imagedata
fileref="images/dnd.png"/>
- </imageobject>
- </mediaobject>
- </figure>
-
- </section>
-
- </section>
-
- <section>
- <title>Upload images</title>
- <para>The implementation of <emphasis
role="bold"><property><rich:fileUpload></property></emphasis>
in the Photo Album Demo uses the embedded Flash module that adds extra functionality to
the component.
- Here are the additional features that the Flash module provides:
- </para>
- <itemizedlist>
- <listitem><para>Multiple files
choosing;</para></listitem>
- <listitem><para>Specification of permitted file types in
the "Open File" dialog window;</para></listitem>
- <listitem><para>A number of additional entry object
properties.</para></listitem>
- </itemizedlist>
-
- <para>
- The photos uploading functionality is based on the
<code>/includes/fileUpload/fileUploader.xhtml page</code>. Let's have a
look at this page to find out how the uploader is implemented:
- </para>
-
- <programlisting role="XML"><![CDATA[<rich:fileUpload
style="margin : 0px 0px 20px 0px; width : 504px; height : 200px; background :
#f1f1f1; border : 1px solid #A1A1A1"
- id="fileUpload"
- allowFlash="true"
- immediateUpload="false"
- acceptedTypes="jpg,jpeg"
- maxFilesQuantity="100"
- autoclear="true"
- fileUploadListener="#{fileUploadManager.listener}" >
- <a4j:support event="onuploadcomplete" reRender="filesPanel, tree"
/>
- <a4j:support event="onfileuploadcomplete" />
- </rich:fileUpload>]]></programlisting>
-
- <para>
- The
"<emphasis><property>FileUploadListener</property></emphasis>"
attribute is binded with <code>fileUploadManager.listener</code> method which
makes the main job on the upload.
- Below is the <code>fileUploadManager.listener</code>
method:
- </para>
- <programlisting role="JAVA"><![CDATA[public void
listener(UploadEvent event) throws Exception {
- UploadItem item = event.getUploadItem();
- Image image = constructImage(item);
- try {
- extractMetadata(item, image);
- } catch (Exception e1) {
- addError(item, image, Constants.FILE_PROCESSING_ERROR);
- return;
- }
- image.setAlbum(model.getSelectedAlbum());
- if(image.getAlbum() == null){
- addError(item, image, Constants.NO_ALBUM_TO_DOWNLOAD_ERROR);
- return;
- }
- try{
- if(imageAction.isImageWithThisPathExist(image)){
- image.setPath(generateNewPath(image.getPath()));
- }
- imageAction.addImage(image);
- }catch(Exception e){
- addError(item, image, Constants.IMAGE_SAVING_ERROR);
- return;
- }
- if(!fileManager.addImage(image.getFullPath(), item.getFile().getPath())){
- addError(item, image, Constants.FILE_SAVE_ERROR);
- return;
- }
- fileWrapper.getFiles().add(image);
- Events.instance().raiseEvent(Constants.IMAGE_ADDED_EVENT, image);
- item.getFile().delete();
-}]]></programlisting>
-
-<para>
- When a photo is added into the uploader, the first thing it does is saving this
photo in the file system.
- The uploaded files are stored in the temporary folder in the computer file system.
- For this purpose the value of the <code>createTempFile</code>
parameter in <code>Ajax4jsf Filter</code> section should be set to
<code>true</code>.
- Below in the <code>Web.xml</code> file Ajax filter section:
-</para>
-
- <programlisting role="XML"><![CDATA[...
-<init-param>
- <param-name>createTempFiles</param-name>
- <param-value>true</param-value>
-</init-param>
-...]]></programlisting>
-
- <para>
- Then the uploader creates an <code>Image</code> object and
extracts all image metadata such as Camera name, Image size etc.
- Then it sets an album and generates a new path to it.
- The system should save six different sizes of the photo.
- After the photo was added into the database the system removes a temporary
file created for storage.
- </para>
-
-
- </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 example 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>
- <section id="contextMenu">
- <title>Context Menu</title>
- <para>Work in progress...</para>
- </section>
-
-</chapter>
Modified: trunk/docs/realworld_app_guide/en/src/main/docbook/modules/intro.xml
===================================================================
--- trunk/docs/realworld_app_guide/en/src/main/docbook/modules/intro.xml 2009-04-25
10:41:47 UTC (rev 13850)
+++ trunk/docs/realworld_app_guide/en/src/main/docbook/modules/intro.xml 2009-04-25
11:26:25 UTC (rev 13851)
@@ -23,19 +23,19 @@
<itemizedlist>
<listitem>
<para>
- <emphasis>wide variety of UI components</emphasis> — the
<property>RichFaces</property> provides
+ <emphasis>wide variety of UI components</emphasis> - the
<property>RichFaces</property> provides
a Lego-like way of building user interfaces for web applications;
</para>
</listitem>
<listitem>
<para>
- <emphasis>built-in Ajax capability</emphasis> — the
<property>RichFaces</property> offers
+ <emphasis>built-in Ajax capability</emphasis> - the
<property>RichFaces</property> offers
both component-wide and very flexible page-wide Ajax support with no need
to write any JavaScript code;
</para>
</listitem>
<listitem>
<para>
- <emphasis>highly customizable look-and-feel</emphasis> — the
<property>RichFaces</property> have special feature called
+ <emphasis>highly customizable look-and-feel</emphasis> - the
<property>RichFaces</property> have special feature called
<ulink
url="http://www.jboss.org/file-access/default/members/jbossrichfaces...;.
</para>
</listitem>
Added: trunk/docs/realworld_app_guide/en/src/main/docbook/modules/overview.xml
===================================================================
--- trunk/docs/realworld_app_guide/en/src/main/docbook/modules/overview.xml
(rev 0)
+++ trunk/docs/realworld_app_guide/en/src/main/docbook/modules/overview.xml 2009-04-25
11:26:25 UTC (rev 13851)
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="overview" xreflabel="overview">
+ <?dbhtml filename="overview.html"?>
+ <chapterinfo>
+ <keywordset>
+ <keyword>RichFaces</keyword>
+
+ </keywordset>
+ </chapterinfo>
+ <title>Application Overview</title>
+ <section id="pageFlows">
+ <title>Page flows</title>
+ <para>
+ Work in progress...
+ </para>
+ </section>
+ <section id="dataModel">
+ <title>Data Model</title>
+ <para>
+ Work in progress...
+ </para>
+ </section>
+ <section id="setUp">
+ <title>Project Set Up</title>
+ <para>
+ Work in progress...
+ </para>
+ </section>
+</chapter>
+
Property changes on:
trunk/docs/realworld_app_guide/en/src/main/docbook/modules/overview.xml
___________________________________________________________________
Name: svn:executable
+ *