/* (c) 2014 - 2017 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wms.web.data;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.NumberTextField;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.IValidator;
import org.apache.wicket.validation.ValidationError;
import org.apache.wicket.validation.validator.RangeValidator;
import org.geoserver.catalog.LegendInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.impl.LegendInfoImpl;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.web.GeoServerApplication;
import org.geoserver.web.wicket.GeoServerAjaxFormLink;
/**
* Allows setting the data for using an ExternalImage
*
*
*/
@SuppressWarnings("serial")
public class ExternalGraphicPanel extends Panel {
private static final long serialVersionUID = 5098470683723890874L;
private TextField<String> onlineResource;
private TextField<String> format;
private TextField<Integer> width;
private TextField<Integer> height;
private WebMarkupContainer table;
private GeoServerAjaxFormLink autoFill;
private Form<StyleInfo> showhideForm;
private AjaxButton show;
private AjaxButton hide;
private Model<String> showhideStyleModel = new Model<String>("");
/**
* @param id
* @param model Must return a {@link ResourceInfo}
*/
public ExternalGraphicPanel(String id, final CompoundPropertyModel<StyleInfo> styleModel, final Form<?> styleForm) {
super(id, styleModel);
// container for ajax updates
final WebMarkupContainer container = new WebMarkupContainer("externalGraphicContainer");
container.setOutputMarkupId(true);
add(container);
table = new WebMarkupContainer("list");
table.setOutputMarkupId(true);
IModel<String> bind = styleModel.bind("legend.onlineResource");
onlineResource = new TextField<String>("onlineResource", bind );
onlineResource.add(new IValidator<String>() {
final List<String> EXTENSIONS = Arrays.asList(new String[]{"png","gif","jpeg","jpg"});
@Override
public void validate(IValidatable<String> input) {
String value = input.getValue();
int last = value == null ? -1 : value.lastIndexOf('.');
if (last == -1 || !EXTENSIONS.contains( value.substring(last + 1).toLowerCase() ) ){
ValidationError error = new ValidationError();
error.setMessage( "Not an image" );
error.addKey("nonImage");
input.error(error);
return;
}
URI uri = null;
try {
uri = new URI(value);
} catch (URISyntaxException e1) {
// Unable to check if absolute
}
if( uri != null && uri.isAbsolute()){
try {
String baseUrl = baseURL(onlineResource.getForm());
if( !value.startsWith(baseUrl)){
onlineResource.warn("Recommend use of styles directory at "+baseUrl);
}
URL url = uri.toURL();
URLConnection conn = url.openConnection();
if("text/html".equals(conn.getContentType())){
ValidationError error = new ValidationError();
error.setMessage("Unable to access image");
error.addKey("imageUnavailable");
input.error(error);
return; // error message back!
}
} catch (MalformedURLException e) {
ValidationError error = new ValidationError();
error.setMessage("Unable to access image");
error.addKey("imageUnavailable");
input.error(error);
} catch (IOException e) {
ValidationError error = new ValidationError();
error.setMessage("Unable to access image");
error.addKey("imageUnavailable");
input.error(error);
}
return; // no further checks possible
}
else {
GeoServerResourceLoader resources = GeoServerApplication.get().getResourceLoader();
try {
File styles = resources.find("styles");
String[] path = value.split(Pattern.quote(File.separator));
WorkspaceInfo wsInfo = styleModel.getObject().getWorkspace();
File test = null;
if (wsInfo != null){
String wsName = wsInfo.getName();
List<String> list = new ArrayList();
list.addAll(Arrays.asList("workspaces", wsName, "styles"));
list.addAll(Arrays.asList(path));
test = resources.find(list.toArray(new String[list.size()]));
}
if ( test == null){
test = resources.find(styles, path);
}
if (test == null) {
ValidationError error = new ValidationError();
error.setMessage("File not found in styles directory");
error.addKey("imageNotFound");
input.error(error);
}
} catch (IOException e) {
ValidationError error = new ValidationError();
error.setMessage("File not found in styles directory");
error.addKey("imageNotFound");
input.error(error);
}
}
}
});
onlineResource.setOutputMarkupId(true);
table.add(onlineResource);
// add the autofill button
autoFill = new GeoServerAjaxFormLink("autoFill", styleForm) {
@Override
public void onClick(AjaxRequestTarget target, Form<?> form) {
URLConnection conn = getExternalGraphic(target, form);
if (conn == null) {
ValidationError error = new ValidationError();
error.setMessage("Unable to access image");
error.addKey("imageUnavailable");
onlineResource.error(error);
} else {
format.setModelValue(new String[] {conn.getContentType()});
BufferedImage image;
try {
image = ImageIO.read(conn.getInputStream());
width.setModelValue(new String[] {"" + image.getWidth()});
height.setModelValue(new String[] {"" + image.getHeight()});
} catch (IOException e) {
e.printStackTrace();
}
target.add(format);
target.add(width);
target.add(height);
}
}
};
table.add(autoFill);
format = new TextField<String>("format", styleModel.bind("legend.format"));
format.setOutputMarkupId(true);
table.add(format);
width = new TextField<Integer>("width", styleModel.bind("legend.width"), Integer.class);
width.add(RangeValidator.minimum(0));
width.setRequired(true);
width.setOutputMarkupId(true);
table.add(width);
height = new TextField<Integer>("height", styleModel.bind("legend.height"), Integer.class);
height.add(RangeValidator.minimum(0));
height.setRequired(true);
height.setOutputMarkupId(true);
table.add(height);
table.add(new AttributeModifier("style", showhideStyleModel));
container.add(table);
showhideForm = new Form<StyleInfo>("showhide") {
@Override
protected void onSubmit() {
super.onSubmit();
}
};
showhideForm.setMarkupId("showhideForm");
container.add(showhideForm);
showhideForm.setMultiPart(true);
show = new AjaxButton("show") {
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
updateVisibility(true);
target.add(ExternalGraphicPanel.this);
}
};
container.add(show);
showhideForm.add(show);
hide = new AjaxButton("hide") {
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
onlineResource.setModelObject("");
onlineResource.clearInput();
format.setModelObject("");
format.clearInput();
width.setModelObject(0);
width.clearInput();
height.setModelObject(0);
height.clearInput();
updateVisibility(false);
target.add(ExternalGraphicPanel.this);
}
};
container.add(hide);
showhideForm.add(hide);
LegendInfo legend = styleModel.getObject().getLegend();
boolean visible = legend != null && legend.getOnlineResource() != null && !legend.getOnlineResource().isEmpty();
updateVisibility(visible);
}
/**
* Lookup base URL using provided form
* @param form
* @see ResponseUtils
* @return baseUrl
*/
protected String baseURL(Form<?> form) {
HttpServletRequest httpServletRequest = (HttpServletRequest) form.getRequest().getContainerRequest();
String baseUrl = GeoServerExtensions.getProperty("PROXY_BASE_URL");
if (StringUtils.isEmpty(baseUrl)) {
GeoServer gs = GeoServerApplication.get().getGeoServer();
baseUrl = gs.getGlobal().getSettings().getProxyBaseUrl();
if (StringUtils.isEmpty(baseUrl)) {
return ResponseUtils.baseURL(httpServletRequest);
}
}
return baseUrl;
}
/**
* Validates the external graphic and returns a connection to the graphic.
* If validation fails, error messages will be added to the passed form
*
* @param target
* @param form
* @return URLConnection to the External Graphic file
*/
protected URLConnection getExternalGraphic(AjaxRequestTarget target, Form<?> form) {
onlineResource.processInput();
if (onlineResource.getModelObject() != null) {
URL url = null;
try {
String baseUrl = baseURL(form);
String external = onlineResource.getModelObject().toString();
URI uri = new URI( external );
if( uri.isAbsolute() ){
url = uri.toURL();
if( !external.startsWith(baseUrl)){
form.warn( "Recommend use of styles directory at "+baseUrl);
}
}
else {
WorkspaceInfo wsInfo = ((StyleInfo)getDefaultModelObject()).getWorkspace();
if (wsInfo != null) {
url = new URL(ResponseUtils.appendPath(baseUrl, "styles", wsInfo.getName(), external));
} else {
url = new URL(ResponseUtils.appendPath(baseUrl, "styles", external));
}
}
URLConnection conn = url.openConnection();
if("text/html".equals(conn.getContentType())){
form.error("Unable to access url");
return null; // error message back!
}
return conn;
} catch (FileNotFoundException notFound ){
form.error( "Unable to access "+url);
} catch (Exception e) {
e.printStackTrace();
form.error( "Recommend use of styles directory at "+e);
}
}
return null;
}
/**
* @return the value of the onlineResource field
*/
protected String getOnlineResource() {
return onlineResource.getInput();
}
private void updateVisibility(boolean b) {
if (b) {
showhideStyleModel.setObject("");
} else {
showhideStyleModel.setObject("display:none");
}
//table.setVisible(b);
autoFill.setVisible(b);
hide.setVisible(b);
show.setVisible(!b);
}
}