/**
* Copyright (c) 2008-2012 The Sakai Foundation
*
* Licensed under the Educational Community 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.osedu.org/licenses/ECL-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.sakaiproject.profile2.tool.components;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.IResourceListener;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.image.resource.BufferedDynamicImageResource;
import org.apache.wicket.markup.html.image.resource.LocalizedImageResource;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.sakaiproject.profile2.logic.ProfileImageLogic;
import org.sakaiproject.profile2.model.Person;
import org.sakaiproject.profile2.model.ProfileImage;
import org.sakaiproject.profile2.model.ProfilePreferences;
import org.sakaiproject.profile2.model.ProfilePrivacy;
import org.sakaiproject.profile2.util.ProfileConstants;
/**
* Profile2 ProfileImageRenderer component.
*
* <p>This component should be used whenever you want to render a user's profile image.
* Choose the most appropriate constructor for your needs and situation.</p>
*
* <p>Note that in order to request another user's image you should supply either a full Person object
* containing the Privacy settings, or the ProfilePrivacy settings directly. If you do not have this information
* you can pass null as the ProfilePrivacy attribute and it will be consulted for you.</p>
*
* <p>If you do not provide a ProfilePreferences object (or Person object containing this info), it will be looked up.</p>
*
* <p>If you do not provide the size or cache settings, they will be defaults (size=main, cache=true).
*
* <p>In short, always provide all information (and preferably a full Person object)</p>
*
* @author Steve Swinsburg (steve.swinsburg@gmail.com)
*
*/
public class ProfileImageRenderer extends Image implements IResourceListener {
private static final long serialVersionUID = 1L;
private String userUuid;
private boolean cache;
private int size;
private ProfilePreferences prefs;
private ProfilePrivacy privacy;
@SpringBean(name="org.sakaiproject.profile2.logic.ProfileImageLogic")
private ProfileImageLogic imageLogic;
private final LocalizedImageResource localizedImageResource = new LocalizedImageResource(this);
/**
* Minimal constructor. Use this for when requesting your own image and only if you don't have access to the ProfilePreferences object.
* Will lookup ProfilePreferences and uses defaults for size(main) and cache(true).
*
* @param id markup ID
* @param userUuid uuid of the user to retrieve the image for
*/
public ProfileImageRenderer(final String id, final String userUuid) {
super(id);
//set incoming
this.userUuid = userUuid;
//set defaults
this.prefs = null;
this.privacy = null;
this.size = getDefaultSize();
this.cache = getDefaultCache();
}
/**
* Minimal constructor. Use this for when requesting your own image. Uses defaults for size(main) and cache(true).
*
* @param id markup ID
* @param userUuid uuid of the user to retrieve the image for
* @param prefs ProfilePreferences object for the user
*/
public ProfileImageRenderer(final String id, final String userUuid, final ProfilePreferences prefs) {
super(id);
//set incoming
this.userUuid = userUuid;
this.prefs = prefs;
//set defaults
this.privacy = null;
this.size = getDefaultSize();
this.cache = getDefaultCache();
}
/**
* Minimal constructor. Use this for when requesting your own image and want to control the size and cache setting.
*
* @param id markup ID
* @param userUuid uuid of the user to retrieve the image for.
* @param prefs ProfilePreferences object for the user.
* @param size image size: 1 for main, 2 for thumbnail.
* @param cache if this image is allowed to be cached by the browser or not. If having issues with
* dynamic images sticking from AJAX updates, set this to false to ensure the image is updated every request.
*/
public ProfileImageRenderer(final String id, final String userUuid, final ProfilePreferences prefs, final int size, final boolean cache) {
super(id);
//set incoming
this.userUuid = userUuid;
this.prefs = prefs;
this.size = size;
this.cache = cache;
//set defaults
this.privacy = null;
}
/**
* Minimal constructor. Use this when requesting someone else's image. Uses defaults for size and cache.
* @param id markup ID
* @param userUuid uuid of the user to retrieve the image for
* @param prefs ProfilePreferences object for the user
* @param privacy ProfilePrivacy object for the user
*/
public ProfileImageRenderer(final String id, final String userUuid, final ProfilePreferences prefs, final ProfilePrivacy privacy) {
super(id);
//set incoming
this.userUuid = userUuid;
this.privacy = privacy;
this.prefs = prefs;
//set defaults
this.size = getDefaultSize();
this.cache = getDefaultCache();
}
/**
* Full constructor where each item is explicitly provided.
* @param id markup ID
* @param userUuid uuid of the user to retrieve the image for
* @param prefs ProfilePreferences object for the user
* @param privacy ProfilePrivacy object for the user
* @param size image size: 1 for main, 2 for thumbnail.
* @param cache if this image is allowed to be cached by the browser or not. If having issues with
* dynamic images sticking from AJAX updates, set this to false to ensure the image is updated every request.
*/
public ProfileImageRenderer(final String id, final String userUuid, final ProfilePreferences prefs, final ProfilePrivacy privacy, final int size, final boolean cache) {
super(id);
//set incoming
this.userUuid = userUuid;
this.privacy = privacy;
this.prefs = prefs;
this.size = size;
this.cache = cache;
}
/**
* Full constructor that takes a Person object instead of split data. Defaults will be used for size(main) and cache(true).
* @param id markup ID
* @param person Person object for the user containing all data
*/
public ProfileImageRenderer(final String id, final Person person) {
super(id);
//extract data
this.userUuid = person.getUuid();
this.prefs = person.getPreferences();
this.privacy = person.getPrivacy();
//set defaults
this.size = getDefaultSize();
this.cache = getDefaultCache();
}
/**
* Full constructor that takes a Person object and allows control over the size and cache settings.
*
* @param id markup ID
* @param person Person object for the user containing all data
* @param size image size: 1 for main, 2 for thumbnail.
* @param cache if this image is allowed to be cached by the browser or not. If having issues with
* dynamic images sticking from AJAX updates, set this to false to ensure the image is updated every request.
*/
public ProfileImageRenderer(final String id, final Person person, final int size, final boolean cache) {
super(id);
//extract data
this.userUuid = person.getUuid();
this.prefs = person.getPreferences();
this.privacy = person.getPrivacy();
//set incoming
this.size = size;
this.cache = cache;
}
/**
* @see org.apache.wicket.IResourceListener#onResourceRequested()
*/
public void onResourceRequested(){
localizedImageResource.onResourceRequested();
}
/**
* Render the tag
*/
@Override
public void onComponentTag(final ComponentTag tag) {
super.onComponentTag(tag);
//get the image
ProfileImage image = imageLogic.getProfileImage(userUuid, prefs, privacy, size);
//do binary
final byte[] bytes = image.getBinary();
if(bytes != null && bytes.length > 0) {
BufferedDynamicImageResource photoResource = new BufferedDynamicImageResource(){
private static final long serialVersionUID = 1L;
protected byte[] getImageData() {
return bytes;
}
};
localizedImageResource.setResource(photoResource);
localizedImageResource.setSrcAttribute(tag);
if(!cache){
addNoCacheNoise(tag);
}
//add alt text
tag.put("alt", image.getAltText());
return;
}
//do url
String url = image.getUrl();
if(StringUtils.isNotBlank(url)) {
tag.put("src", url);
/* DO NOT add cache noise to URL based images as they won't stick, it's only the dynamic ones that can sometimes.
if(!cache){
addNoCacheNoise(tag);
}
*/
//add alt text
tag.put("alt", image.getAltText());
return;
}
//do default
tag.put("src", getDefaultImage(size));
//add alt text
tag.put("alt", image.getAltText());
}
/**
* Get the default image URL
*
* @param size image size: 1 for main, 2 for thumbnail.
* @return
*/
private String getDefaultImage(final int size) {
if (ProfileConstants.PROFILE_IMAGE_THUMBNAIL == size) {
return getRequest().getRelativePathPrefixToContextRoot() + ProfileConstants.UNAVAILABLE_IMAGE_THUMBNAIL;
}
return getRequest().getRelativePathPrefixToContextRoot() + ProfileConstants.UNAVAILABLE_IMAGE;
}
/**
* Get the default image size
* @return
*/
private int getDefaultSize() {
return ProfileConstants.PROFILE_IMAGE_MAIN;
}
/**
* Get the default cache setting
* @return
*/
private boolean getDefaultCache() {
return ProfileConstants.PROFILE_IMAGE_CACHE;
}
/**
* Add noise to the image URL, similar to Wicket's NonCachingImage.
*
* @param tag
* @param url
* @return
*/
private void addNoCacheNoise(ComponentTag tag) {
StringBuilder url = new StringBuilder();
String tagSrc = tag.getAttributes().getString("src");
url.append(tagSrc);
url.append((tagSrc.indexOf('?') >= 0) ? "&" : "?");
url.append("wicket:antiCache=" + System.currentTimeMillis());
tag.put("src", url);
}
}