You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/02/13 12:44:49 UTC
[45/51] [partial] syncope git commit: [SYNCOPE-620] Re-organization
completed
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);
+ }
+}