/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.web.services; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.apache.wicket.Component; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.CheckBox; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.SubmitLink; import org.apache.wicket.markup.html.form.TextArea; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; 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.LoadableDetachableModel; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.validation.validator.UrlValidator; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.config.GeoServer; import org.geoserver.config.ServiceInfo; import org.geoserver.platform.GeoServerEnvironment; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.web.GeoServerSecuredPage; import org.geoserver.web.data.workspace.WorkspaceChoiceRenderer; import org.geoserver.web.data.workspace.WorkspacesModel; import org.geoserver.web.wicket.GeoServerDialog; import org.geoserver.web.wicket.HelpLink; import org.geoserver.web.wicket.KeywordsEditor; import org.geoserver.web.wicket.LiveCollectionModel; /** * Base page for service administration pages. * <p> * Subclasses of this page should contribute form components in the {@link #build(ServiceInfo, Form)} * method. Each component that is added to the form should have a corresponding * markup entry of the following form: * <pre> * <wicket:extend> * <li> * <span> * <label><wicket:message key="maxFeatures.title">Maximum Features</wicket:message></label> * <input wicket:id="maxFeatures" class="field text" type="text"> * </span> * <p class="instruct"> * </p> * </li> * * </wicket:extend> * </pre> * </p> * *@author Justin Deoliveira, The Open Planning Project * */ public abstract class BaseServiceAdminPage<T extends ServiceInfo> extends GeoServerSecuredPage { protected GeoServerDialog dialog; public BaseServiceAdminPage() { this(new PageParameters()); } public BaseServiceAdminPage(PageParameters pageParams) { String wsName = pageParams.get("workspace").toString(); init(new ServiceModel(getServiceClass(), wsName)); } public BaseServiceAdminPage(T service) { init(new ServiceModel(service)); } void init(final IModel<T> infoModel) { T service = infoModel.getObject(); dialog = new GeoServerDialog("dialog"); add(dialog); Form form = new Form( "form", new CompoundPropertyModel(infoModel)); add(form); if (service.getWorkspace() == null) { //create the panel that has the drop down list to switch between workspace form.add(new GlobalWorkspacePanel("workspace")); } else { //create just a panel with a label that signifies the workspace form.add(new LocalWorkspacePanel("workspace", service)); } form.add(new HelpLink("workspaceHelp").setDialog(dialog)); form.add(new Label("service.enabled", new StringResourceModel("service.enabled", this).setParameters(getServiceName()))); form.add(new TextField("maintainer")); TextField onlineResource = new TextField("onlineResource"); final GeoServerEnvironment gsEnvironment = GeoServerExtensions.bean(GeoServerEnvironment.class); // AF: Disable Binding if GeoServer Env Parametrization is enabled! if (gsEnvironment == null || !GeoServerEnvironment.ALLOW_ENV_PARAMETRIZATION) { onlineResource.add(new UrlValidator()); } form.add(onlineResource); form.add(new CheckBox("enabled")); form.add(new CheckBox("citeCompliant")); form.add(new TextField("title")); form.add(new TextArea("abstract")); form.add(new KeywordsEditor("keywords", LiveCollectionModel.list(new PropertyModel(infoModel, "keywords")))); form.add(new TextField("fees")); form.add(new TextField("accessConstraints")); build(infoModel, form); //add the extension panels ListView extensionPanels = createExtensionPanelList("extensions", infoModel); extensionPanels.setReuseItems(true); form.add(extensionPanels); SubmitLink submit = new SubmitLink("submit",new StringResourceModel( "save", (Component)null, null) ) { @Override public void onSubmit() { try { handleSubmit((T)infoModel.getObject()); doReturn(); } catch(Exception e) { error(e); } } }; form.add(submit); Button cancel = new Button( "cancel" ) { public void onSubmit() { doReturn(); } }; form.add( cancel ); //cancel.setDefaultFormProcessing( false ); } protected ListView createExtensionPanelList(String id, final IModel infoModel) { List<AdminPagePanelInfo> panels = getGeoServerApplication().getBeansOfType(AdminPagePanelInfo.class); for (Iterator<AdminPagePanelInfo> it = panels.iterator(); it.hasNext();) { AdminPagePanelInfo panel = it.next(); if (!getServiceClass().equals(panel.getServiceClass())) { it.remove(); } } return new ListView<AdminPagePanelInfo>(id, panels) { @Override protected void populateItem(ListItem<AdminPagePanelInfo> item) { AdminPagePanelInfo info = item.getModelObject(); try { AdminPagePanel panel = info.getComponentClass().getConstructor( String.class, IModel.class).newInstance("content", infoModel); item.add(panel); } catch (Exception e) { throw new WicketRuntimeException("Failed to create admin extension panel of " + "type " + info.getComponentClass().getSimpleName(), e); } } }; } /** * The class of the service. * <p> * This value is used to obtain a reference to the service info * object via {@link GeoServer#getService(Class)}. * </p> */ protected abstract Class<T> getServiceClass(); /** * Builds the form for the page. * <p> * The form uses a {@link CompoundPropertyModel} so in the normal * case components do not need a model as its inherited from the parent. * This means that component id's should match the info bean property * they correspond to. * </p> * @param info The service info object. * @param form The page form. */ protected abstract void build(IModel info, Form form ); // { // } /** * Callback for submit. * <p> * This implementation simply saves the service. Subclasses may * extend / override if need be. * </p> * @param info */ protected void handleSubmit( T info ) { if (info.getId() != null) { getGeoServer().save( info ); } else { //means a non attached instance was passed to us, do nothing, up to caller to add it // to configuration } } /** * The string to use when representing this service to users. * Subclasses must override. */ protected abstract String getServiceName(); class ServiceModel<T extends ServiceInfo> extends LoadableDetachableModel<T> { /* id reference */ String id; /* reference via local workspace */ Class<T> serviceClass; String workspaceName; /* direct reference */ T service; ServiceModel(T service) { this.id = service.getId(); if (this.id == null) { this.service = service; } } ServiceModel(Class<T> serviceClass, String workspaceName) { this.serviceClass = serviceClass; this.workspaceName = workspaceName; } @Override protected T load() { if (id != null) { return (T) getGeoServer().getService(id, getServiceClass()); } if (serviceClass != null) { if (workspaceName != null) { WorkspaceInfo ws = getCatalog().getWorkspaceByName(workspaceName); return (T) getGeoServer().getService(ws, getServiceClass()); } return (T) getGeoServer().getService(getServiceClass()); } return service; } @Override public void detach() { if (id == null && serviceClass == null) { //keep reference serialize service object service = getObject(); } else { service = null; } } } class ServiceFilteredWorkspacesModel extends LoadableDetachableModel { WorkspacesModel wsModel; ServiceFilteredWorkspacesModel(WorkspacesModel wsModel) { this.wsModel = wsModel; } @Override protected Object load() { Collection<WorkspaceInfo> workspaces = (Collection<WorkspaceInfo>) wsModel.getObject(); GeoServer gs = getGeoServer(); for (Iterator<WorkspaceInfo> it = workspaces.iterator(); it.hasNext();) { if (gs.getService(it.next(), getServiceClass()) == null) { it.remove(); } } return workspaces; } } class GlobalWorkspacePanel extends Panel { public GlobalWorkspacePanel(String id) { super(id); final DropDownChoice<WorkspaceInfo> wsChoice = new DropDownChoice<WorkspaceInfo>("workspace", new ServiceFilteredWorkspacesModel(new WorkspacesModel()), new WorkspaceChoiceRenderer()); wsChoice.setNullValid(true); wsChoice.add(new AjaxFormComponentUpdatingBehavior("change") { @Override protected void onUpdate(AjaxRequestTarget target) { WorkspaceInfo ws = wsChoice.getModelObject(); PageParameters pp = new PageParameters(); if (ws != null) { pp.add("workspace", ws.getName()); } setResponsePage(BaseServiceAdminPage.this.getClass(), pp); } }); add(wsChoice); } } class LocalWorkspacePanel extends Panel { public LocalWorkspacePanel(String id, T service) { super(id); add(new Label("workspace", new PropertyModel(service, "workspace.name"))); } } }