http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/commons/status/StatusUtils.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/commons/status/StatusUtils.java b/client/console/src/main/java/org/apache/syncope/client/console/commons/status/StatusUtils.java new file mode 100644 index 0000000..3c0e56e --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/status/StatusUtils.java @@ -0,0 +1,324 @@ +/* + * 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.syncope.client.console.commons.status; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.apache.syncope.client.console.commons.ConnIdSpecialAttributeName; +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.panels.ImagePanel; +import org.apache.syncope.client.console.panels.StatusPanel; +import org.apache.syncope.client.console.rest.AbstractSubjectRestClient; +import org.apache.syncope.common.lib.mod.StatusMod; +import org.apache.syncope.common.lib.to.AbstractAttributableTO; +import org.apache.syncope.common.lib.to.AbstractSubjectTO; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.ConnObjectTO; +import org.apache.wicket.Component; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.behavior.Behavior; +import org.apache.wicket.markup.ComponentTag; +import org.apache.wicket.markup.html.image.Image; +import org.apache.wicket.request.resource.ContextRelativeResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StatusUtils implements Serializable { + + private static final long serialVersionUID = 7238009174387184309L; + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(StatusUtils.class); + + private static final String IMG_PREFIX = "/img/statuses/"; + + private final AbstractSubjectRestClient restClient; + + public StatusUtils(final AbstractSubjectRestClient restClient) { + this.restClient = restClient; + } + + public List<ConnObjectWrapper> getConnectorObjects(final AbstractSubjectTO subject) { + final List<ConnObjectWrapper> objects = new ArrayList<>(); + objects.addAll(getConnectorObjects(subject, subject.getResources())); + return objects; + } + + public List<ConnObjectWrapper> getConnectorObjects( + final Collection<AbstractSubjectTO> subjects, final Collection<String> resources) { + + final List<ConnObjectWrapper> objects = new ArrayList<>(); + + for (AbstractSubjectTO subject : subjects) { + objects.addAll(getConnectorObjects(subject, resources)); + } + + return objects; + } + + private List<ConnObjectWrapper> getConnectorObjects( + final AbstractSubjectTO subject, final Collection<String> resources) { + + final List<ConnObjectWrapper> objects = new ArrayList<>(); + + for (String resourceName : resources) { + ConnObjectTO objectTO = null; + try { + objectTO = restClient.getConnectorObject(resourceName, subject.getKey()); + } catch (Exception e) { + LOG.warn("ConnObject '{}' not found on resource '{}'", subject.getKey(), resourceName); + } + + objects.add(new ConnObjectWrapper(subject, resourceName, objectTO)); + } + + return objects; + } + + public StatusBean getStatusBean( + final AbstractAttributableTO attributable, + final String resourceName, + final ConnObjectTO objectTO, + final boolean isRole) { + + final StatusBean statusBean = new StatusBean(attributable, resourceName); + + if (objectTO != null) { + final Boolean enabled = isEnabled(objectTO); + + final Status status = enabled == null + ? (isRole ? Status.ACTIVE : Status.UNDEFINED) + : enabled + ? Status.ACTIVE + : Status.SUSPENDED; + + final String accountLink = getAccountLink(objectTO); + + statusBean.setStatus(status); + statusBean.setAccountLink(accountLink); + } + + return statusBean; + } + + private Boolean isEnabled(final ConnObjectTO objectTO) { + final Map<String, AttrTO> attributeTOs = objectTO.getPlainAttrMap(); + + final AttrTO status = attributeTOs.get(ConnIdSpecialAttributeName.ENABLE); + + return status != null && status.getValues() != null && !status.getValues().isEmpty() + ? Boolean.parseBoolean(status.getValues().get(0)) + : null; + } + + private String getAccountLink(final ConnObjectTO objectTO) { + final Map<String, AttrTO> attributeTOs = objectTO == null + ? Collections.<String, AttrTO>emptyMap() + : objectTO.getPlainAttrMap(); + + final AttrTO name = attributeTOs.get(ConnIdSpecialAttributeName.NAME); + + return name != null && name.getValues() != null && !name.getValues().isEmpty() + ? name.getValues().get(0) + : null; + } + + public static StatusMod buildStatusMod(final Collection<StatusBean> statuses) { + return buildStatusMod(statuses, null); + } + + public static StatusMod buildStatusMod(final Collection<StatusBean> statuses, final Boolean enable) { + StatusMod statusMod = new StatusMod(); + statusMod.setOnSyncope(false); + + for (StatusBean status : statuses) { + if (enable == null + || (enable && !status.getStatus().isActive()) || (!enable && status.getStatus().isActive())) { + + if ("syncope".equalsIgnoreCase(status.getResourceName())) { + statusMod.setOnSyncope(true); + } else { + statusMod.getResourceNames().add(status.getResourceName()); + } + + } + } + + return statusMod; + } + + public static void update( + final AbstractAttributableTO attributable, + final StatusPanel statusPanel, + final AjaxRequestTarget target, + final Collection<String> resourcesToAdd, + final Collection<String> resourcesToRemove) { + + if (statusPanel != null) { + Map<String, StatusBean> statusMap = new LinkedHashMap<>(); + for (StatusBean statusBean : statusPanel.getStatusBeans()) { + statusMap.put(statusBean.getResourceName(), statusBean); + } + + for (String resourceName : resourcesToAdd) { + if (!statusMap.keySet().contains(resourceName)) { + StatusBean statusBean; + if (statusPanel.getInitialStatusBeanMap().containsKey(resourceName)) { + statusBean = statusPanel.getInitialStatusBeanMap().get(resourceName); + } else { + statusBean = new StatusBean(attributable, resourceName); + statusBean.setStatus(Status.NOT_YET_SUBMITTED); + } + + statusMap.put(statusBean.getResourceName(), statusBean); + } + } + + for (String resource : resourcesToRemove) { + statusMap.remove(resource); + } + + statusPanel.updateStatusBeans(new ArrayList<>(statusMap.values())); + target.add(statusPanel); + } + } + + public ConnObjectTO getConnObjectTO( + final Long attributableId, final String resourceName, final List<ConnObjectWrapper> objects) { + + for (ConnObjectWrapper object : objects) { + if (attributableId.equals(object.getAttributable().getKey()) + && resourceName.equalsIgnoreCase(object.getResourceName())) { + + return object.getConnObjectTO(); + } + } + + return null; + } + + public Image getStatusImage(final String componentId, final Status status) { + final String alt, title, statusName; + + switch (status) { + + case NOT_YET_SUBMITTED: + statusName = Status.UNDEFINED.toString(); + alt = "undefined icon"; + title = "Not yet submitted"; + break; + + case ACTIVE: + statusName = Status.ACTIVE.toString(); + alt = "active icon"; + title = "Enabled"; + break; + + case UNDEFINED: + statusName = Status.UNDEFINED.toString(); + alt = "undefined icon"; + title = "Undefined status"; + break; + + case OBJECT_NOT_FOUND: + statusName = Status.OBJECT_NOT_FOUND.toString(); + alt = "notfound icon"; + title = "Not found"; + break; + + default: + statusName = Status.SUSPENDED.toString(); + alt = "inactive icon"; + title = "Disabled"; + } + + final Image img = new Image(componentId, + new ContextRelativeResource(IMG_PREFIX + statusName + Constants.PNG_EXT)); + img.add(new Behavior() { + + private static final long serialVersionUID = 1469628524240283489L; + + @Override + public void onComponentTag(final Component component, final ComponentTag tag) { + tag.put("alt", alt); + tag.put("title", title); + } + }); + + return img; + } + + public ImagePanel getStatusImagePanel(final String componentId, final Status status) { + final String alt, title, statusName; + + switch (status) { + + case NOT_YET_SUBMITTED: + statusName = Status.UNDEFINED.toString(); + alt = "undefined icon"; + title = "Not yet submitted"; + break; + + case ACTIVE: + statusName = Status.ACTIVE.toString(); + alt = "active icon"; + title = "Enabled"; + break; + + case UNDEFINED: + statusName = Status.UNDEFINED.toString(); + alt = "undefined icon"; + title = "Undefined status"; + break; + + case OBJECT_NOT_FOUND: + statusName = Status.OBJECT_NOT_FOUND.toString(); + alt = "notfound icon"; + title = "Not found"; + break; + + default: + statusName = Status.SUSPENDED.toString(); + alt = "inactive icon"; + title = "Disabled"; + } + + final ImagePanel imagePanel = new ImagePanel(componentId, + new ContextRelativeResource(IMG_PREFIX + statusName + Constants.PNG_EXT)); + imagePanel.add(new Behavior() { + + private static final long serialVersionUID = 1469628524240283489L; + + @Override + public void onComponentTag(final Component component, final ComponentTag tag) { + tag.put("alt", alt); + tag.put("title", title); + } + }); + + return imagePanel; + } +}
http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/init/ConsoleInitializer.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/init/ConsoleInitializer.java b/client/console/src/main/java/org/apache/syncope/client/console/init/ConsoleInitializer.java new file mode 100644 index 0000000..ec51d3e --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/init/ConsoleInitializer.java @@ -0,0 +1,72 @@ +/* + * 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.syncope.client.console.init; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.support.AopUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.stereotype.Component; + +/** + * Take care of all initializations needed by Syncope Console to run up and safe. + */ +@Component +public class ConsoleInitializer implements InitializingBean, BeanFactoryAware { + + private static final Logger LOG = LoggerFactory.getLogger(ConsoleInitializer.class); + + private DefaultListableBeanFactory beanFactory; + + @Override + public void setBeanFactory(final BeanFactory beanFactory) throws BeansException { + this.beanFactory = (DefaultListableBeanFactory) beanFactory; + } + + @Override + public void afterPropertiesSet() { + Map<String, SyncopeConsoleLoader> loaderMap = beanFactory.getBeansOfType(SyncopeConsoleLoader.class); + + List<SyncopeConsoleLoader> loaders = new ArrayList<>(loaderMap.values()); + Collections.sort(loaders, new Comparator<SyncopeConsoleLoader>() { + + @Override + public int compare(final SyncopeConsoleLoader o1, final SyncopeConsoleLoader o2) { + return o1.getPriority().compareTo(o2.getPriority()); + } + }); + + LOG.debug("Starting initialization..."); + for (SyncopeConsoleLoader loader : loaders) { + LOG.debug("Invoking {} with priority {}", AopUtils.getTargetClass(loader).getName(), loader.getPriority()); + loader.load(); + } + LOG.debug("Initialization completed"); + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/init/ImplementationClassNamesLoader.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/init/ImplementationClassNamesLoader.java b/client/console/src/main/java/org/apache/syncope/client/console/init/ImplementationClassNamesLoader.java new file mode 100644 index 0000000..0ff7282 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/init/ImplementationClassNamesLoader.java @@ -0,0 +1,109 @@ +/* + * 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.syncope.client.console.init; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.client.console.panels.AbstractExtensionPanel; +import org.apache.syncope.client.console.BinaryPreview; +import org.apache.syncope.client.console.wicket.markup.html.form.preview.AbstractBinaryPreviewer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.stereotype.Component; +import org.springframework.util.ClassUtils; + +@Component +public class ImplementationClassNamesLoader implements SyncopeConsoleLoader { + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(ImplementationClassNamesLoader.class); + + private List<Class<? extends AbstractBinaryPreviewer>> previewers; + + private List<Class<? extends AbstractExtensionPanel>> extPanels; + + @Override + public Integer getPriority() { + return 0; + } + + @Override + @SuppressWarnings("unchecked") + public void load() { + previewers = new ArrayList<>(); + extPanels = new ArrayList<>(); + + ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); + scanner.addIncludeFilter(new AssignableTypeFilter(AbstractBinaryPreviewer.class)); + scanner.addIncludeFilter(new AssignableTypeFilter(AbstractExtensionPanel.class)); + + for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) { + try { + Class<?> clazz = ClassUtils.resolveClassName( + bd.getBeanClassName(), ClassUtils.getDefaultClassLoader()); + boolean isAbsractClazz = Modifier.isAbstract(clazz.getModifiers()); + + if (AbstractBinaryPreviewer.class.isAssignableFrom(clazz) && !isAbsractClazz) { + previewers.add((Class<? extends AbstractBinaryPreviewer>) clazz); + } else if (AbstractExtensionPanel.class.isAssignableFrom(clazz) && !isAbsractClazz) { + extPanels.add((Class<? extends AbstractExtensionPanel>) clazz); + } + + } catch (Throwable t) { + LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t); + } + } + previewers = Collections.unmodifiableList(previewers); + extPanels = Collections.unmodifiableList(extPanels); + + LOG.debug("Binary previewers found: {}", previewers); + LOG.debug("Extension panels found: {}", extPanels); + } + + public Class<? extends AbstractBinaryPreviewer> getPreviewerClass(final String mimeType) { + LOG.debug("Searching for previewer class for MIME type: {}", mimeType); + Class<? extends AbstractBinaryPreviewer> previewer = null; + for (Class<? extends AbstractBinaryPreviewer> candidate : previewers) { + LOG.debug("Evaluating previewer class {} for MIME type {}", candidate.getName(), mimeType); + if (ArrayUtils.contains(candidate.getAnnotation(BinaryPreview.class).mimeTypes(), mimeType)) { + LOG.debug("Found existing previewer for MIME type {}: {}", mimeType, candidate.getName()); + previewer = candidate; + } + } + return previewer; + } + + public List<Class<? extends AbstractBinaryPreviewer>> getPreviewerClasses() { + return previewers; + } + + public List<Class<? extends AbstractExtensionPanel>> getExtPanelClasses() { + return extPanels; + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/init/MIMETypesLoader.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/init/MIMETypesLoader.java b/client/console/src/main/java/org/apache/syncope/client/console/init/MIMETypesLoader.java new file mode 100644 index 0000000..7a2f878 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/init/MIMETypesLoader.java @@ -0,0 +1,69 @@ +/* + * 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.syncope.client.console.init; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.wicket.util.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class MIMETypesLoader implements SyncopeConsoleLoader { + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(MIMETypesLoader.class); + + private List<String> mimeTypes; + + @Override + public Integer getPriority() { + return 10; + } + + @Override + public void load() { + final Set<String> mediaTypes = new HashSet<>(); + this.mimeTypes = new ArrayList<>(); + try { + final String mimeTypesFile = IOUtils.toString(getClass().getResourceAsStream("/MIMETypes")); + for (String fileRow : mimeTypesFile.split("\n")) { + if (StringUtils.isNotBlank(fileRow) && !fileRow.startsWith("#")) { + mediaTypes.add(fileRow); + } + } + this.mimeTypes.addAll(mediaTypes); + Collections.sort(this.mimeTypes); + } catch (Exception e) { + LOG.error("Error reading file MIMETypes from resources", e); + } + } + + public List<String> getMimeTypes() { + LOG.debug("Returning loaded MIME types list {}", mimeTypes); + return mimeTypes; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/init/SyncopeConsoleLoader.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/init/SyncopeConsoleLoader.java b/client/console/src/main/java/org/apache/syncope/client/console/init/SyncopeConsoleLoader.java new file mode 100644 index 0000000..7c4d3d4 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/init/SyncopeConsoleLoader.java @@ -0,0 +1,35 @@ +/* + * 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.syncope.client.console.init; + +/** + * Marker interface for Syncope console initialization. + */ +public interface SyncopeConsoleLoader { + + /** + * @return the priority that the implementing class has in the initialization process. + */ + Integer getPriority(); + + /** + * Perform initialization operations. + */ + void load(); +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractBasePage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractBasePage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractBasePage.java new file mode 100644 index 0000000..4a32700 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractBasePage.java @@ -0,0 +1,131 @@ +/* + * 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.syncope.client.console.pages; + +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.commons.XMLRolesReader; +import org.apache.syncope.client.console.init.MIMETypesLoader; +import org.apache.syncope.client.console.panels.NotificationPanel; +import org.apache.syncope.client.console.rest.ConfigurationRestClient; +import org.apache.syncope.client.console.rest.ReportRestClient; +import org.apache.syncope.client.console.rest.ResourceRestClient; +import org.apache.syncope.client.console.rest.RoleRestClient; +import org.apache.syncope.client.console.rest.SchemaRestClient; +import org.apache.syncope.client.console.rest.TaskRestClient; +import org.apache.syncope.client.console.rest.UserRestClient; +import org.apache.syncope.client.console.rest.UserSelfRestClient; +import org.apache.syncope.client.console.wicket.markup.head.MetaHeaderItem; +import org.apache.wicket.markup.head.HeaderItem; +import org.apache.wicket.markup.head.IHeaderResponse; +import org.apache.wicket.markup.head.PriorityHeaderItem; +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.apache.wicket.spring.injection.annot.SpringBean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AbstractBasePage extends WebPage { + + private static final long serialVersionUID = 8611724965544132636L; + + /** + * Logger. + */ + protected static final Logger LOG = LoggerFactory.getLogger(AbstractBasePage.class); + + protected static final String TASKS = "Tasks"; + + protected static final String FORM = "form"; + + protected static final String CANCEL = "cancel"; + + protected static final String SUBMIT = "submit"; + + protected static final String APPLY = "apply"; + + protected final HeaderItem meta = new MetaHeaderItem("X-UA-Compatible", "IE=edge"); + + @SpringBean + protected XMLRolesReader xmlRolesReader; + + @SpringBean + protected UserRestClient userRestClient; + + @SpringBean + protected UserSelfRestClient userSelfRestClient; + + @SpringBean + protected RoleRestClient roleRestClient; + + @SpringBean + protected TaskRestClient taskRestClient; + + @SpringBean + protected SchemaRestClient schemaRestClient; + + @SpringBean + protected ResourceRestClient resourceRestClient; + + @SpringBean + protected ReportRestClient reportRestClient; + + @SpringBean + protected ConfigurationRestClient confRestClient; + + @SpringBean + protected MIMETypesLoader mimeTypesInitializer; + + protected NotificationPanel feedbackPanel; + + /** + * Response flag set by the Modal Window after the operation is completed. + */ + protected boolean modalResult = false; + + public AbstractBasePage() { + this(null); + } + + public AbstractBasePage(final PageParameters parameters) { + super(parameters); + + feedbackPanel = new NotificationPanel(Constants.FEEDBACK); + feedbackPanel.setOutputMarkupId(true); + add(feedbackPanel); + } + + public NotificationPanel getFeedbackPanel() { + return feedbackPanel; + } + + public boolean isModalResult() { + return modalResult; + } + + public void setModalResult(final boolean operationResult) { + this.modalResult = operationResult; + } + + @Override + public void renderHead(final IHeaderResponse response) { + super.renderHead(response); + response.render(new PriorityHeaderItem(meta)); + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSchedTaskModalPage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSchedTaskModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSchedTaskModalPage.java new file mode 100644 index 0000000..78ca906 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSchedTaskModalPage.java @@ -0,0 +1,132 @@ +/* + * 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.syncope.client.console.pages; + +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.commons.DateFormatROModel; +import org.apache.syncope.client.console.wicket.markup.html.CrontabContainer; +import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.SchedTaskTO; +import org.apache.wicket.PageReference; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.form.AjaxButton; +import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; +import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.model.ResourceModel; +import org.springframework.util.StringUtils; + +/** + * Modal window with Task form (to stop and start execution). + */ +public abstract class AbstractSchedTaskModalPage extends TaskModalPage { + + private static final long serialVersionUID = 2892005971093059242L; + + protected CrontabContainer crontab; + + public AbstractSchedTaskModalPage(final ModalWindow window, final SchedTaskTO taskTO, + final PageReference pageRef) { + + super(taskTO); + + crontab = new CrontabContainer("crontab", new PropertyModel<String>(taskTO, "cronExpression"), + taskTO.getCronExpression()); + form.add(crontab); + + final AjaxTextFieldPanel name = + new AjaxTextFieldPanel("name", "name", new PropertyModel<String>(taskTO, "name")); + name.setEnabled(true); + profile.add(name); + + final AjaxTextFieldPanel description = new AjaxTextFieldPanel("description", "description", + new PropertyModel<String>(taskTO, "description")); + description.setEnabled(true); + profile.add(description); + + final AjaxTextFieldPanel lastExec = new AjaxTextFieldPanel("lastExec", getString("lastExec"), + new DateFormatROModel(new PropertyModel<String>(taskTO, "lastExec"))); + lastExec.setEnabled(false); + profile.add(lastExec); + + final AjaxTextFieldPanel nextExec = new AjaxTextFieldPanel("nextExec", getString("nextExec"), + new DateFormatROModel(new PropertyModel<String>(taskTO, "nextExec"))); + nextExec.setEnabled(false); + profile.add(nextExec); + + final AjaxButton submit = new IndicatingAjaxButton(APPLY, new ResourceModel(APPLY)) { + + private static final long serialVersionUID = -958724007591692537L; + + @Override + protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) { + SchedTaskTO taskTO = (SchedTaskTO) form.getModelObject(); + taskTO.setCronExpression(StringUtils.hasText(taskTO.getCronExpression()) + ? crontab.getCronExpression() + : null); + + try { + submitAction(taskTO); + + ((BasePage) pageRef.getPage()).setModalResult(true); + + window.close(target); + } catch (SyncopeClientException e) { + LOG.error("While creating or updating task", e); + error(getString(Constants.ERROR) + ": " + e.getMessage()); + feedbackPanel.refresh(target); + } + } + + @Override + protected void onError(final AjaxRequestTarget target, final Form<?> form) { + feedbackPanel.refresh(target); + } + }; + + final AjaxButton cancel = new IndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL)) { + + private static final long serialVersionUID = -958724007591692537L; + + @Override + protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) { + window.close(target); + } + }; + + cancel.setDefaultFormProcessing(false); + + if (taskTO.getKey() > 0) { + MetaDataRoleAuthorizationStrategy.authorize( + submit, RENDER, xmlRolesReader.getEntitlement(TASKS, "update")); + } else { + MetaDataRoleAuthorizationStrategy.authorize( + submit, RENDER, xmlRolesReader.getEntitlement(TASKS, "create")); + } + + form.add(submit); + form.add(cancel); + } + + protected abstract void submitAction(SchedTaskTO taskTO); + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSchemaModalPage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSchemaModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSchemaModalPage.java new file mode 100644 index 0000000..1c1bd0f --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSchemaModalPage.java @@ -0,0 +1,45 @@ +/* + * 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.syncope.client.console.pages; + +import org.apache.syncope.common.lib.to.AbstractSchemaTO; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.wicket.PageReference; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; + +/** + * Modal window with Schema form. + */ +public abstract class AbstractSchemaModalPage<T extends AbstractSchemaTO> extends BaseModalPage { + + private static final long serialVersionUID = 7369215690388444748L; + + protected AttributableType kind; + + public AbstractSchemaModalPage(final AttributableType kind) { + this.kind = kind; + } + + public abstract void setSchemaModalPage(PageReference callerPageRef, ModalWindow window, T schema, + boolean createFlag); + + public AttributableType getKind() { + return kind; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractStatusModalPage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractStatusModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractStatusModalPage.java new file mode 100644 index 0000000..f5dfbca --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractStatusModalPage.java @@ -0,0 +1,30 @@ +/* + * 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.syncope.client.console.pages; + +import org.apache.wicket.markup.html.panel.Fragment; + +public class AbstractStatusModalPage extends BaseModalPage { + + private static final long serialVersionUID = 6633408683036028540L; + + public AbstractStatusModalPage() { + add(new Fragment("pwdMgtFields", "emptyFragment", this)); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSyncTaskModalPage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSyncTaskModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSyncTaskModalPage.java new file mode 100644 index 0000000..aa62b41 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/AbstractSyncTaskModalPage.java @@ -0,0 +1,209 @@ +/* + * 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.syncope.client.console.pages; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.commons.SelectChoiceRenderer; +import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel; +import org.apache.syncope.common.lib.to.AbstractProvisioningTaskTO; +import org.apache.syncope.common.lib.to.ResourceTO; +import org.apache.syncope.common.lib.types.MatchingRule; +import org.apache.syncope.common.lib.types.UnmatchingRule; +import org.apache.wicket.PageReference; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; +import org.apache.wicket.ajax.markup.html.AjaxLink; +import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.DropDownChoice; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.PropertyModel; + +/** + * Abstract Modal window for Sync and Push Task form. + */ +public abstract class AbstractSyncTaskModalPage extends AbstractSchedTaskModalPage { + + private static final long serialVersionUID = 2148403203517274669L; + + protected AjaxDropDownChoicePanel<MatchingRule> matchingRule; + + protected AjaxDropDownChoicePanel<UnmatchingRule> unmatchingRule; + + protected abstract List<String> getSyncActions(); + + final IModel<List<String>> allResources = new LoadableDetachableModel<List<String>>() { + + private static final long serialVersionUID = 5275935387613157437L; + + @Override + protected List<String> load() { + final List<String> resourceNames = new ArrayList<>(); + + for (ResourceTO resourceTO : resourceRestClient.getAll()) { + resourceNames.add(resourceTO.getKey()); + } + + Collections.sort(resourceNames); + return resourceNames; + } + }; + + final IModel<List<String>> syncActionsClasses = new LoadableDetachableModel<List<String>>() { + + private static final long serialVersionUID = 5275935387613157438L; + + @Override + protected List<String> load() { + return getSyncActions(); + } + }; + + public AbstractSyncTaskModalPage( + final ModalWindow window, final AbstractProvisioningTaskTO taskTO, final PageReference pageRef) { + + super(window, taskTO, pageRef); + + final AjaxDropDownChoicePanel<String> resource = new AjaxDropDownChoicePanel<>("resource", + getString("resourceName"), new PropertyModel<String>(taskTO, "resource")); + resource.setChoices(allResources.getObject()); + resource.setChoiceRenderer(new SelectChoiceRenderer<String>()); + resource.addRequiredLabel(); + resource.setEnabled(taskTO.getKey() == 0); + resource.setStyleSheet("ui-widget-content ui-corner-all long_dynamicsize"); + + profile.add(resource); + + final WebMarkupContainer syncActionsClassNames = new WebMarkupContainer("syncActionsClassNames"); + syncActionsClassNames.setOutputMarkupId(true); + profile.add(syncActionsClassNames); + + final AjaxLink<Void> first = new IndicatingAjaxLink<Void>("first") { + + private static final long serialVersionUID = -7978723352517770644L; + + @Override + public void onClick(final AjaxRequestTarget target) { + taskTO.getActionsClassNames().add(StringUtils.EMPTY); + setVisible(false); + target.add(syncActionsClassNames); + } + }; + first.setOutputMarkupPlaceholderTag(true); + first.setVisible(taskTO.getActionsClassNames().isEmpty()); + syncActionsClassNames.add(first); + + final ListView<String> actionsClasses = new ListView<String>( + "actionsClasses", new PropertyModel<List<String>>(taskTO, "actionsClassNames")) { + + private static final long serialVersionUID = 9101744072914090143L; + + @Override + protected void populateItem(final ListItem<String> item) { + final String className = item.getModelObject(); + + final DropDownChoice<String> actionsClass = new DropDownChoice<String>( + "actionsClass", new Model<String>(className), syncActionsClasses.getObject()); + actionsClass.setNullValid(true); + actionsClass.setRequired(true); + actionsClass.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + taskTO.getActionsClassNames().set(item.getIndex(), actionsClass.getModelObject()); + target.add(syncActionsClassNames); + } + }); + actionsClass.setRequired(true); + actionsClass.setOutputMarkupId(true); + actionsClass.setRequired(true); + item.add(actionsClass); + + AjaxLink<Void> minus = new IndicatingAjaxLink<Void>("drop") { + + private static final long serialVersionUID = -7978723352517770644L; + + @Override + public void onClick(final AjaxRequestTarget target) { + taskTO.getActionsClassNames().remove(className); + first.setVisible(taskTO.getActionsClassNames().isEmpty()); + target.add(syncActionsClassNames); + } + }; + item.add(minus); + + final AjaxLink<Void> plus = new IndicatingAjaxLink<Void>("add") { + + private static final long serialVersionUID = -7978723352517770644L; + + @Override + public void onClick(final AjaxRequestTarget target) { + taskTO.getActionsClassNames().add(StringUtils.EMPTY); + target.add(syncActionsClassNames); + } + }; + plus.setOutputMarkupPlaceholderTag(true); + plus.setVisible(item.getIndex() == taskTO.getActionsClassNames().size() - 1); + item.add(plus); + } + }; + syncActionsClassNames.add(actionsClasses); + + syncActionsClassNames.setEnabled(!syncActionsClasses.getObject().isEmpty()); + + final AjaxCheckBoxPanel creates = new AjaxCheckBoxPanel("performCreate", getString("creates"), + new PropertyModel<Boolean>(taskTO, "performCreate")); + profile.add(creates); + + final AjaxCheckBoxPanel updates = new AjaxCheckBoxPanel("performUpdate", getString("updates"), + new PropertyModel<Boolean>(taskTO, "performUpdate")); + profile.add(updates); + + final AjaxCheckBoxPanel deletes = new AjaxCheckBoxPanel("performDelete", getString("updates"), + new PropertyModel<Boolean>(taskTO, "performDelete")); + profile.add(deletes); + + final AjaxCheckBoxPanel syncStatus = new AjaxCheckBoxPanel("syncStatus", getString("syncStatus"), + new PropertyModel<Boolean>(taskTO, "syncStatus")); + profile.add(syncStatus); + + matchingRule = new AjaxDropDownChoicePanel<MatchingRule>( + "matchingRule", "matchingRule", new PropertyModel<MatchingRule>(taskTO, "matchingRule")); + matchingRule.setChoices(Arrays.asList(MatchingRule.values())); + ((DropDownChoice) matchingRule.getField()).setNullValid(false); + + unmatchingRule = new AjaxDropDownChoicePanel<UnmatchingRule>( + "unmatchingRule", "unmatchingRule", new PropertyModel<UnmatchingRule>(taskTO, "unmatchingRule")); + unmatchingRule.setChoices(Arrays.asList(UnmatchingRule.values())); + ((DropDownChoice) unmatchingRule.getField()).setNullValid(false); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/ActivitiModelerPopupPage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/ActivitiModelerPopupPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/ActivitiModelerPopupPage.java new file mode 100644 index 0000000..05658fc --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/ActivitiModelerPopupPage.java @@ -0,0 +1,27 @@ +/* + * 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.syncope.client.console.pages; + +import org.apache.wicket.markup.html.WebPage; + +public class ActivitiModelerPopupPage extends WebPage { + + private static final long serialVersionUID = -7031206743629422898L; + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/ApprovalModalPage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/ApprovalModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/ApprovalModalPage.java new file mode 100644 index 0000000..79deeb0 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/ApprovalModalPage.java @@ -0,0 +1,286 @@ +/* + * 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.syncope.client.console.pages; + +import java.io.Serializable; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.commons.MapChoiceRenderer; +import org.apache.syncope.client.console.rest.ApprovalRestClient; +import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel; +import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.DateTimeFieldPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.SpinnerFieldPanel; +import org.apache.syncope.client.console.wicket.markup.html.list.AltListView; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.WorkflowFormPropertyTO; +import org.apache.syncope.common.lib.to.WorkflowFormTO; +import org.apache.wicket.Page; +import org.apache.wicket.PageReference; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.form.AjaxButton; +import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; +import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.ResourceModel; +import org.apache.wicket.spring.injection.annot.SpringBean; + +public class ApprovalModalPage extends BaseModalPage { + + private static final long serialVersionUID = -8847854414429745216L; + + private final static int USER_WIN_HEIGHT = 550; + + private final static int USER_WIN_WIDTH = 800; + + @SpringBean + private ApprovalRestClient restClient; + + private final ModalWindow editUserWin; + + public ApprovalModalPage(final PageReference pageRef, final ModalWindow window, final WorkflowFormTO formTO) { + super(); + + IModel<List<WorkflowFormPropertyTO>> formProps = new LoadableDetachableModel<List<WorkflowFormPropertyTO>>() { + + private static final long serialVersionUID = 3169142472626817508L; + + @Override + protected List<WorkflowFormPropertyTO> load() { + return formTO.getProperties(); + } + }; + + final ListView<WorkflowFormPropertyTO> propView = + new AltListView<WorkflowFormPropertyTO>("propView", formProps) { + + private static final long serialVersionUID = 9101744072914090143L; + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected void populateItem(final ListItem<WorkflowFormPropertyTO> item) { + final WorkflowFormPropertyTO prop = item.getModelObject(); + + Label label = new Label("key", prop.getName() == null + ? prop.getId() + : prop.getName()); + item.add(label); + + FieldPanel field; + switch (prop.getType()) { + case Boolean: + field = new AjaxDropDownChoicePanel("value", label.getDefaultModelObjectAsString(), + new Model<Boolean>(Boolean.valueOf(prop.getValue()))).setChoices(Arrays.asList( + new String[] { "Yes", "No" })); + break; + + case Date: + SimpleDateFormat df = StringUtils.isNotBlank(prop.getDatePattern()) + ? new SimpleDateFormat(prop.getDatePattern()) + : new SimpleDateFormat(); + Date parsedDate = null; + if (StringUtils.isNotBlank(prop.getValue())) { + try { + parsedDate = df.parse(prop.getValue()); + } catch (ParseException e) { + LOG.error("Unparsable date: {}", prop.getValue(), e); + } + } + + field = new DateTimeFieldPanel("value", label.getDefaultModelObjectAsString(), + new Model<Date>(parsedDate), df.toLocalizedPattern()); + break; + + case Enum: + MapChoiceRenderer<String, String> enumCR = + new MapChoiceRenderer<String, String>(prop.getEnumValues()); + + field = new AjaxDropDownChoicePanel("value", label.getDefaultModelObjectAsString(), + new Model(prop.getValue())).setChoiceRenderer(enumCR).setChoices(new Model() { + + private static final long serialVersionUID = -858521070366432018L; + + @Override + public Serializable getObject() { + return new ArrayList<String>(prop.getEnumValues().keySet()); + } + }); + break; + + case Long: + field = new SpinnerFieldPanel<Long>("value", label.getDefaultModelObjectAsString(), + Long.class, new Model<Long>(NumberUtils.toLong(prop.getValue())), + null, null); + break; + + case String: + default: + field = new AjaxTextFieldPanel("value", PARENT_PATH, + new Model<String>(prop.getValue())); + break; + } + + field.setReadOnly(!prop.isWritable()); + if (prop.isRequired()) { + field.addRequiredLabel(); + } + + item.add(field); + } + }; + + final AjaxButton userDetails = new IndicatingAjaxButton("userDetails", + new Model<String>(getString("userDetails"))) { + + private static final long serialVersionUID = -4804368561204623354L; + + @Override + protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) { + editUserWin.setPageCreator(new ModalWindow.PageCreator() { + + private static final long serialVersionUID = -7834632442532690940L; + + @Override + public Page createPage() { + return new ViewUserModalPage(ApprovalModalPage.this.getPageReference(), editUserWin, + userRestClient.read(formTO.getUserKey())) { + + private static final long serialVersionUID = -2819994749866481607L; + + @Override + protected void closeAction(final AjaxRequestTarget target, final Form form) { + setResponsePage(ApprovalModalPage.this); + } + }; + } + }); + + editUserWin.show(target); + } + }; + MetaDataRoleAuthorizationStrategy.authorize(userDetails, ENABLE, + xmlRolesReader.getEntitlement("Users", "read")); + + final AjaxButton submit = new IndicatingAjaxButton(APPLY, new Model<String>(getString(SUBMIT))) { + + private static final long serialVersionUID = -958724007591692537L; + + @Override + protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) { + + Map<String, WorkflowFormPropertyTO> props = formTO.getPropertyMap(); + + for (int i = 0; i < propView.size(); i++) { + @SuppressWarnings("unchecked") + ListItem<WorkflowFormPropertyTO> item = (ListItem<WorkflowFormPropertyTO>) propView.get(i); + String input = ((FieldPanel) item.get("value")).getField().getInput(); + + if (!props.containsKey(item.getModelObject().getId())) { + props.put(item.getModelObject().getId(), new WorkflowFormPropertyTO()); + } + + if (item.getModelObject().isWritable()) { + switch (item.getModelObject().getType()) { + case Boolean: + props.get(item.getModelObject().getId()).setValue(String.valueOf("0".equals(input))); + break; + + case Date: + case Enum: + case String: + case Long: + default: + props.get(item.getModelObject().getId()).setValue(input); + break; + } + } + } + + formTO.getProperties().clear(); + formTO.getProperties().addAll(props.values()); + try { + restClient.submitForm(formTO); + + ((Todo) pageRef.getPage()).setModalResult(true); + window.close(target); + } catch (SyncopeClientException e) { + error(getString(Constants.ERROR) + ": " + e.getMessage()); + LOG.error("While submitting form {}", formTO, e); + feedbackPanel.refresh(target); + } + } + + @Override + protected void onError(final AjaxRequestTarget target, final Form<?> form) { + feedbackPanel.refresh(target); + } + }; + + final AjaxButton cancel = new IndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL)) { + + private static final long serialVersionUID = -958724007591692537L; + + @Override + protected void onSubmit(final AjaxRequestTarget target, final Form form) { + window.close(target); + } + + @Override + protected void onError(final AjaxRequestTarget target, final Form form) { + // nothing + } + }; + + cancel.setDefaultFormProcessing(false); + + Form form = new Form(FORM); + form.add(propView); + form.add(userDetails); + form.add(submit); + form.add(cancel); + + MetaDataRoleAuthorizationStrategy.authorize(form, ENABLE, xmlRolesReader.getEntitlement("Approval", + SUBMIT)); + + editUserWin = new ModalWindow("editUserWin"); + editUserWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY); + editUserWin.setInitialHeight(USER_WIN_HEIGHT); + editUserWin.setInitialWidth(USER_WIN_WIDTH); + editUserWin.setCookieName("edit-user-modal"); + add(editUserWin); + + add(form); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/BaseModalPage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/BaseModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/BaseModalPage.java new file mode 100644 index 0000000..20892dd --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/BaseModalPage.java @@ -0,0 +1,35 @@ +/* + * 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.syncope.client.console.pages; + +import org.apache.syncope.client.console.commons.CloseOnESCBehavior; + +/** + * Syncope Modal Window. + */ +public abstract class BaseModalPage extends AbstractBasePage { + + private static final long serialVersionUID = -1443079028368471943L; + + public BaseModalPage() { + super(); + + add(new CloseOnESCBehavior("keyup")); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java new file mode 100644 index 0000000..2540c37 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java @@ -0,0 +1,111 @@ +/* + * 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.syncope.client.console.pages; + +import org.apache.syncope.client.console.SyncopeApplication; +import org.apache.syncope.client.console.commons.Constants; +import org.apache.wicket.Component; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.IAjaxIndicatorAware; +import org.apache.wicket.behavior.Behavior; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; +import org.apache.wicket.markup.ComponentTag; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.request.mapper.parameter.PageParameters; + +/** + * Syncope Wicket base-page. + */ +public class BasePage extends AbstractBasePage implements IAjaxIndicatorAware { + + private static final long serialVersionUID = 1571997737305598502L; + + public BasePage() { + this(null); + } + + public BasePage(final PageParameters parameters) { + super(parameters); + + pageSetup(); + } + + private void pageSetup() { + ((SyncopeApplication) getApplication()).setupNavigationPanel(this, xmlRolesReader, true); + + final String kind = getClass().getSimpleName().toLowerCase(); + final BookmarkablePageLink kindLink = (BookmarkablePageLink) get(kind); + if (kindLink != null) { + kindLink.add(new Behavior() { + + private static final long serialVersionUID = 1469628524240283489L; + + @Override + public void onComponentTag(final Component component, final ComponentTag tag) { + tag.put("class", kind); + } + }); + + Component kindIcon = kindLink.get(0); + if (kindIcon != null) { + kindIcon.add(new Behavior() { + + private static final long serialVersionUID = 1469628524240283489L; + + @Override + public void onComponentTag(final Component component, final ComponentTag tag) { + tag.put("src", "../.." + SyncopeApplication.IMG_PREFIX + kind + Constants.PNG_EXT); + } + }); + } + } + + ((SyncopeApplication) getApplication()).setupEditProfileModal(this, userSelfRestClient); + } + + @Override + public String getAjaxIndicatorMarkupId() { + return "veil"; + } + + /** + * Set a WindowClosedCallback for a ModalWindow instance. + * + * @param window window + * @param container container + */ + protected void setWindowClosedCallback(final ModalWindow window, final WebMarkupContainer container) { + + window.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() { + + private static final long serialVersionUID = 8804221891699487139L; + + @Override + public void onClose(final AjaxRequestTarget target) { + target.add(container); + if (isModalResult()) { + info(getString(Constants.OPERATION_SUCCEEDED)); + feedbackPanel.refresh(target); + setModalResult(false); + } + } + }); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePopupPage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePopupPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePopupPage.java new file mode 100644 index 0000000..856f469 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePopupPage.java @@ -0,0 +1,25 @@ +/* + * 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.syncope.client.console.pages; + +public class BasePopupPage extends AbstractBasePage { + + private static final long serialVersionUID = -2633667311332659505L; + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionModalPage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionModalPage.java new file mode 100644 index 0000000..95e514b --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionModalPage.java @@ -0,0 +1,166 @@ +/* + * 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.syncope.client.console.pages; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.rest.BaseRestClient; +import org.apache.syncope.client.console.wicket.ajax.markup.html.ClearIndicatingAjaxButton; +import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; +import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel; +import org.apache.syncope.common.lib.to.BulkAction; +import org.apache.syncope.common.lib.to.BulkActionResult; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.form.AjaxButton; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; +import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable; +import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; +import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.model.CompoundPropertyModel; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.ResourceModel; +import org.springframework.beans.BeanUtils; + +public class BulkActionModalPage<T, S> extends BaseModalPage { + + private static final long serialVersionUID = 4114026480146090962L; + + public BulkActionModalPage( + final ModalWindow window, + final Collection<T> items, + final List<IColumn<T, S>> columns, + final Collection<ActionLink.ActionType> actions, + final BaseRestClient bulkActionExecutor, + final String idFieldName, + final String pageId) { + + super(); + + final SortableDataProvider<T, S> dataProvider = new SortableDataProvider<T, S>() { + + private static final long serialVersionUID = 5291903859908641954L; + + @Override + public Iterator<? extends T> iterator(final long first, final long count) { + return items.iterator(); + } + + @Override + public long size() { + return items.size(); + } + + @Override + public IModel<T> model(final T object) { + return new CompoundPropertyModel<>(object); + } + }; + + add(new AjaxFallbackDefaultDataTable<>( + "selectedObjects", + new ArrayList<>(columns.subList(1, columns.size() - 1)), + dataProvider, + Integer.MAX_VALUE).setVisible(items != null && !items.isEmpty())); + + @SuppressWarnings("rawtypes") + final ActionLinksPanel actionPanel = new ActionLinksPanel("actions", new Model(), getPageReference()); + add(actionPanel); + + for (ActionLink.ActionType action : actions) { + final BulkAction bulkAction = new BulkAction(); + for (T item : items) { + try { + bulkAction.getTargets().add(getTargetId(item, idFieldName).toString()); + } catch (Exception e) { + LOG.error("Error retrieving item id {}", idFieldName, e); + } + } + + switch (action) { + case DELETE: + bulkAction.setOperation(BulkAction.Type.DELETE); + break; + case SUSPEND: + bulkAction.setOperation(BulkAction.Type.SUSPEND); + break; + case REACTIVATE: + bulkAction.setOperation(BulkAction.Type.REACTIVATE); + break; + case EXECUTE: + bulkAction.setOperation(BulkAction.Type.EXECUTE); + break; + case DRYRUN: + bulkAction.setOperation(BulkAction.Type.DRYRUN); + break; + default: + LOG.error("Bulk action type not supported"); + } + + actionPanel.add(new ActionLink() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target) { + try { + final BulkActionResult res = (BulkActionResult) bulkActionExecutor.getClass(). + getMethod("bulkAction", BulkAction.class).invoke(bulkActionExecutor, bulkAction); + + setResponsePage(new BulkActionResultModalPage<>(window, items, columns, res, idFieldName)); + } catch (Exception e) { + error(getString(Constants.ERROR) + + ": Operation " + bulkAction.getOperation() + " not supported"); + feedbackPanel.refresh(target); + } + + } + }, action, pageId, !items.isEmpty()); + } + + final Form<Void> form = new Form<>(FORM); + add(form); + + final AjaxButton cancel = + new ClearIndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL), getPageReference()) { + + private static final long serialVersionUID = -958724007591692537L; + + @Override + protected void onSubmitInternal(final AjaxRequestTarget target, final Form<?> form) { + window.close(target); + } + }; + + cancel.setDefaultFormProcessing(false); + form.add(cancel); + } + + private Object getTargetId(final Object target, final String idFieldName) + throws IllegalAccessException, InvocationTargetException { + + return BeanUtils.getPropertyDescriptor(target.getClass(), idFieldName). + getReadMethod().invoke(target, new Object[0]); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionResultModalPage.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionResultModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionResultModalPage.java new file mode 100644 index 0000000..4ada6ba --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionResultModalPage.java @@ -0,0 +1,97 @@ +/* + * 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.syncope.client.console.pages; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionResultColumn; +import org.apache.syncope.common.lib.to.BulkActionResult; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.AjaxLink; +import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; +import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable; +import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; +import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider; +import org.apache.wicket.model.CompoundPropertyModel; +import org.apache.wicket.model.IModel; + +/** + * Show user or role status after performing a successful operation. + */ +public class BulkActionResultModalPage<T, S> extends BaseModalPage { + + /** + * Serial version id. + */ + private static final long serialVersionUID = 2646115294319713724L; + + public BulkActionResultModalPage( + final ModalWindow window, + final Collection<T> items, + final List<IColumn<T, S>> columns, + final BulkActionResult results, + final String idFieldName) { + + super(); + + final List<IColumn<T, S>> newColumnList = new ArrayList<>(columns.subList(1, columns.size() - 1)); + newColumnList.add(newColumnList.size(), new ActionResultColumn<T, S>(results, idFieldName)); + + final SortableDataProvider<T, S> dataProvider = new SortableDataProvider<T, S>() { + + private static final long serialVersionUID = 5291903859908641954L; + + @Override + public Iterator<? extends T> iterator(final long first, final long count) { + return items.iterator(); + } + + @Override + public long size() { + return items.size(); + } + + @Override + public IModel<T> model(final T object) { + return new CompoundPropertyModel<T>(object); + } + }; + + add(new AjaxFallbackDefaultDataTable<T, S>( + "selectedObjects", + newColumnList, + dataProvider, + Integer.MAX_VALUE).setVisible(items != null && !items.isEmpty())); + + final AjaxLink<Void> close = new IndicatingAjaxLink<Void>("close") { + + private static final long serialVersionUID = -7978723352517770644L; + + @Override + public void onClick(final AjaxRequestTarget target) { + window.close(target); + } + }; + + add(close); + } +}
