All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.wicket.extensions.rating.RatingPanel Maven / Gradle / Ivy

There is a newer version: 10.1.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.wicket.extensions.rating;

import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.Loop;
import org.apache.wicket.markup.html.list.LoopItem;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
import org.apache.wicket.request.resource.PackageResourceReference;
import org.apache.wicket.request.resource.ResourceReference;

/**
 * Rating component that generates a number of stars where a user can click on to rate something.
 * Subclasses should implement {@link #onRated(int, AjaxRequestTarget)} to provide the calculation
 * of the rating, and {@link #onIsStarActive(int)} to indicate whether to render an active star or
 * an inactive star.
 * 

* Active stars are the stars that show the rating, inactive stars are the left overs. E.G. a rating * of 3.4 on a scale of 5 stars will render 3 active stars, and 2 inactive stars (provided that the * {@link #onIsStarActive(int)} returns true for each of the first three stars). *

* Use this component in the following way: * *

 * add(new RatingPanel("rating", new PropertyModel(rating, "rating"), 5)
 * {
 * 	protected boolean onIsStarActive(int star)
 * 	{
 * 		return rating.isActive(star);
 * 	}
 * 
 * 	protected void onRated(int rating, AjaxRequestTarget target)
 * 	{
 * 		rating1.addRating(rating);
 * 	}
 * });
 * 
* * The user of this component is responsible for creating a model that supplies a Double (or Float) * value for the rating message, however the rating panel doesn't necessarily have to contain a * float or number rating value. *

* Though not obligatory, you could also supply a value for the number of votes cast, which allows * the component to render a more complete message in the rating label. * *

Customizing the rating value and label

* To customize the rating value, one should override the * {@link #newRatingLabel(String, IModel, IModel)} method and create another label instead, based on * the provided models. If you do so, and use another system of rating than returning a Float or * Double, then you should also customize the rating resource bundle to reflect your message. The * default resource bundle assumes a numeric value for the rating. * *

Resource bundle

* This component uses two types of messages: rating.simple and rating.complete. The first message * is used when no model is given for the number of cast votes. The complete message shows the text * 'Rating xx.yy from zz votes'. * *
 *       rating.simple=Rated {0,number,#.#}
 *       rating.complete=Rated {0,number,#.#} from {1,number,#} votes
 * 
* *

Customizing the star images

* To customize the images shown, override the {@link #getActiveStarUrl(int)} and * {@link #getInactiveStarUrl(int)} methods. Using the iteration parameter it is possible to use a * different image for each star, creating a fade effect or something similar. * * @author Martijn Dashorst */ public abstract class RatingPanel extends Panel { /** * Renders the stars and the links necessary for rating. */ private final class RatingStarBar extends Loop { /** For serialization. */ private static final long serialVersionUID = 1L; private RatingStarBar(final String id, final IModel model) { super(id, model); } @Override protected void populateItem(final LoopItem item) { // Use an AjaxFallbackLink for rating to make voting work even // without Ajax. AjaxFallbackLink link = new AjaxFallbackLink("link") { private static final long serialVersionUID = 1L; @Override public void onClick(final AjaxRequestTarget target) { LoopItem item = (LoopItem)getParent(); // adjust the rating, and provide the target to the subclass // of our rating component, so other components can also get // updated in case of an AJAX event. onRated(item.getIndex() + 1, target); // if we process an AJAX event, update this panel if (target != null) { target.add(RatingPanel.this.get("rater")); } } @Override public boolean isEnabled() { return !hasVoted.getObject(); } }; int iteration = item.getIndex(); // add the star image, which is either active (highlighted) or // inactive (no star) link.add(new WebMarkupContainer("star").add(new SimpleAttributeModifier("src", (onIsStarActive(iteration) ? getActiveStarUrl(iteration) : getInactiveStarUrl(iteration))))); item.add(link); } } /** For serialization. */ private static final long serialVersionUID = 1L; /** * Star image for no selected star */ public static final ResourceReference STAR0 = new PackageResourceReference(RatingPanel.class, "star0.gif"); /** * Star image for selected star */ public static final ResourceReference STAR1 = new PackageResourceReference(RatingPanel.class, "star1.gif"); /** * The number of stars that need to be shown, should result in an Integer object. */ private IModel nrOfStars = new Model(5); /** * The number of votes that have been cast, should result in an Integer object. */ private final IModel nrOfVotes; /** * The flag on whether the current user has voted already. */ private final IModel hasVoted; /** * Handle to the rating label to set the visibility. */ private Component ratingLabel; private final boolean addDefaultCssStyle; /** * Constructs a rating component with 5 stars, using a compound property model as its model to * retrieve the rating. * * @param id * the component id. */ public RatingPanel(final String id) { this(id, null, 5, true); } /** * Constructs a rating component with 5 stars, using the rating for retrieving the rating. * * @param id * the component id * @param rating * the model to get the rating */ public RatingPanel(final String id, final IModel rating) { this(id, rating, new Model(5), null, new Model(Boolean.FALSE), true); } /** * Constructs a rating component with nrOfStars stars, using a compound property model as its * model to retrieve the rating. * * @param id * the component id * @param nrOfStars * the number of stars to display */ public RatingPanel(final String id, final int nrOfStars) { this(id, null, nrOfStars, true); } /** * Constructs a rating component with nrOfStars stars, using the rating for retrieving the * rating. * * @param id * the component id * @param rating * the model to get the rating * @param nrOfStars * the number of stars to display * @param addDefaultCssStyle * should this component render its own default CSS style? */ public RatingPanel(final String id, final IModel rating, final int nrOfStars, final boolean addDefaultCssStyle) { this(id, rating, new Model(nrOfStars), null, new Model(Boolean.FALSE), addDefaultCssStyle); } /** * Constructs a rating panel with nrOfStars stars, where the rating model is used to retrieve * the rating, the nrOfVotes model to retrieve the number of casted votes. This panel doens't * keep track of whether the user has already voted. * * @param id * the component id * @param rating * the model to get the rating * @param nrOfStars * the number of stars to display * @param nrOfVotes * the number of cast votes * @param addDefaultCssStyle * should this component render its own default CSS style? */ public RatingPanel(final String id, final IModel rating, final int nrOfStars, final IModel nrOfVotes, final boolean addDefaultCssStyle) { this(id, rating, new Model(nrOfStars), nrOfVotes, new Model(Boolean.FALSE), addDefaultCssStyle); } /** * Constructs a rating panel with nrOfStars stars, where the rating model is used to retrieve * the rating, the nrOfVotes model used to retrieve the number of votes cast and the hasVoted * model to retrieve whether the user already had cast a vote. * * @param id * the component id. * @param rating * the (calculated) rating, i.e. 3.4 * @param nrOfStars * the number of stars to display * @param nrOfVotes * the number of cast votes * @param hasVoted * has the user already voted? * @param addDefaultCssStyle * should this component render its own default CSS style? */ public RatingPanel(final String id, final IModel rating, final IModel nrOfStars, final IModel nrOfVotes, final IModel hasVoted, final boolean addDefaultCssStyle) { super(id, rating); this.addDefaultCssStyle = addDefaultCssStyle; this.nrOfStars = wrap(nrOfStars); this.nrOfVotes = wrap(nrOfVotes); this.hasVoted = wrap(hasVoted); WebMarkupContainer rater = new WebMarkupContainer("rater"); rater.add(newRatingStarBar("element", this.nrOfStars)); // add the text label for the message 'Rating 4.5 out of 25 votes' rater.add(ratingLabel = newRatingLabel("rating", wrap(rating), this.nrOfVotes)); // set auto generation of the markup id on, such that ajax calls work. rater.setOutputMarkupId(true); add(rater); // don't render the outer tags in the target document, just the div that // is inside the panel. setRenderBodyOnly(true); } @Override public void renderHead(final IHeaderResponse response) { super.renderHead(response); if (addDefaultCssStyle) { response.renderCSSReference(new PackageResourceReference(RatingPanel.class, "RatingPanel.css")); } } /** * Creates a new bar filled with stars to click on. * * @param id * the bar id * @param nrOfStars * the number of stars to generate * @return the bar with rating stars */ protected Component newRatingStarBar(final String id, final IModel nrOfStars) { return new RatingStarBar(id, nrOfStars); } /** * Creates a new rating label, showing a message like 'Rated 5.4 from 53 votes'. * * @param id * the id of the label * @param rating * the model containing the rating * @param nrOfVotes * the model containing the number of votes (may be null) * @return the label component showing the message. */ protected Component newRatingLabel(final String id, final IModel rating, final IModel nrOfVotes) { IModel model; if (nrOfVotes == null) { Object[] parameters = new Object[] { rating }; model = new StringResourceModel("rating.simple", this, null, parameters); } else { Object[] parameters = new Object[] { rating, nrOfVotes }; model = new StringResourceModel("rating.complete", this, null, parameters); } return new Label(id, model); } /** * Returns the url pointing to the image of active stars, is used to set the URL for the image * of an active star. Override this method to provide your own images. * * @param iteration * the sequence number of the star * @return the url pointing to the image for active stars. */ protected String getActiveStarUrl(final int iteration) { IRequestHandler handler = new ResourceReferenceRequestHandler(STAR1); return getRequestCycle().urlFor(handler).toString(); } /** * Returns the url pointing to the image of inactive stars, is used to set the URL for the image * of an inactive star. Override this method to provide your own images. * * @param iteration * the sequence number of the star * @return the url pointing to the image for inactive stars. */ protected String getInactiveStarUrl(final int iteration) { IRequestHandler handler = new ResourceReferenceRequestHandler(STAR0); return getRequestCycle().urlFor(handler).toString(); } /** * Sets the visibility of the rating label. * * @param visible * true when the label should be visible * @return this for chaining. */ public RatingPanel setRatingLabelVisible(final boolean visible) { ratingLabel.setVisible(visible); return this; } /** * Returns true when the star identified by its sequence number should be shown as * active. * * @param star * the sequence number of the star (ranging from 0 to nrOfStars) * @return true when the star should be rendered as active */ protected abstract boolean onIsStarActive(int star); /** * Notification of a click on a rating star. Add your own components to the request target when * you want to have them updated in the Ajax request. NB the target may be null * when the click isn't handled using AJAX, but using a fallback scenario. * * @param rating * the number of the star that is clicked, ranging from 1 to nrOfStars * @param target * the request target, null if the request is a regular, non-AJAX request. */ protected abstract void onRated(int rating, AjaxRequestTarget target); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy