http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/panels/PolicyBeanPanel.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/PolicyBeanPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/PolicyBeanPanel.java new file mode 100644 index 0000000..67d6684 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/PolicyBeanPanel.java @@ -0,0 +1,328 @@ +/* + * 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.panels; + +import java.beans.PropertyDescriptor; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.rest.PolicyRestClient; +import org.apache.syncope.client.console.rest.SchemaRestClient; +import org.apache.syncope.client.console.wicket.markup.html.form.AbstractFieldPanel; +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.client.console.wicket.markup.html.form.AjaxPalettePanel; +import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel; +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.annotation.ClassList; +import org.apache.syncope.common.lib.annotation.SchemaList; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.common.lib.types.PolicySpec; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.model.ResourceModel; +import org.apache.wicket.model.util.ListModel; +import org.apache.wicket.spring.injection.annot.SpringBean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.util.ClassUtils; + +public class PolicyBeanPanel extends Panel { + + private static final long serialVersionUID = -3035998190456928143L; + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(PolicyBeanPanel.class); + + @SpringBean + private SchemaRestClient schemaRestClient; + + @SpringBean + private PolicyRestClient policyRestClient; + + final IModel<List<String>> userSchemas = new LoadableDetachableModel<List<String>>() { + + private static final long serialVersionUID = -2012833443695917883L; + + @Override + protected List<String> load() { + return schemaRestClient.getPlainSchemaNames(AttributableType.USER); + } + }; + + final IModel<List<String>> roleSchemas = new LoadableDetachableModel<List<String>>() { + + private static final long serialVersionUID = 5275935387613157437L; + + @Override + protected List<String> load() { + return schemaRestClient.getPlainSchemaNames(AttributableType.ROLE); + } + }; + + final IModel<List<String>> correlationRules = new LoadableDetachableModel<List<String>>() { + + private static final long serialVersionUID = 5275935387613157437L; + + @Override + protected List<String> load() { + return policyRestClient.getCorrelationRuleClasses(); + } + }; + + public PolicyBeanPanel(final String id, final PolicySpec policy) { + super(id); + + final List<FieldWrapper> items = new ArrayList<>(); + + for (Field field : policy.getClass().getDeclaredFields()) { + if (!"serialVersionUID".equals(field.getName())) { + FieldWrapper fieldWrapper = new FieldWrapper(); + fieldWrapper.setName(field.getName()); + fieldWrapper.setType(field.getType()); + + final SchemaList schemaList = field.getAnnotation(SchemaList.class); + fieldWrapper.setSchemaList(schemaList); + + final ClassList classList = field.getAnnotation(ClassList.class); + fieldWrapper.setClassList(classList); + + items.add(fieldWrapper); + } + } + + final ListView<FieldWrapper> policies = new AltListView<FieldWrapper>("policies", items) { + + private static final long serialVersionUID = 9101744072914090143L; + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected void populateItem(final ListItem<FieldWrapper> item) { + final FieldWrapper field = item.getModelObject(); + + final PropertyDescriptor propDesc = BeanUtils.getPropertyDescriptor(policy.getClass(), field.getName()); + + item.add(new Label("label", new ResourceModel(field.getName()))); + + AbstractFieldPanel component; + try { + if (field.getClassList() != null) { + component = new AjaxDropDownChoicePanel("field", field.getName(), new PropertyModel(policy, + field.getName())); + + final List<String> rules = correlationRules.getObject(); + + if (rules != null && !rules.isEmpty()) { + ((AjaxDropDownChoicePanel) component).setChoices(correlationRules.getObject()); + } + + item.add(component); + + item.add(getActivationControl( + component, + propDesc.getReadMethod().invoke(policy, new Object[] {}) != null, + null, + null)); + + } else if (field.getType().isEnum()) { + component = new AjaxDropDownChoicePanel("field", field.getName(), new PropertyModel(policy, + field.getName())); + + final Serializable[] values = (Serializable[]) field.getType().getEnumConstants(); + + if (values != null && values.length > 0) { + ((AjaxDropDownChoicePanel) component).setChoices(Arrays.asList(values)); + } + + item.add(component); + + item.add(getActivationControl( + component, + (Enum<?>) propDesc.getReadMethod().invoke(policy, new Object[] {}) != null, + values[0], + values[0])); + + } else if (ClassUtils.isAssignable(Boolean.class, field.getType())) { + item.add(new AjaxCheckBoxPanel("check", field.getName(), + new PropertyModel<Boolean>(policy, field.getName()))); + + item.add(new Label("field", new Model(null))); + } else if (Collection.class.isAssignableFrom(field.getType())) { + if (field.getSchemaList() != null) { + final List<String> values = new ArrayList<>(); + if (field.getName().charAt(0) == 'r') { + values.addAll(roleSchemas.getObject()); + + if (field.getSchemaList().extended()) { + values.add("name"); + } + } else { + values.addAll(userSchemas.getObject()); + + if (field.getSchemaList().extended()) { + values.add("key"); + values.add("username"); + } + } + + component = new AjaxPalettePanel("field", new PropertyModel(policy, field.getName()), + new ListModel<>(values)); + item.add(component); + + Collection<?> collection = (Collection) propDesc.getReadMethod().invoke(policy); + item.add(getActivationControl(component, + !collection.isEmpty(), new ArrayList<String>(), new ArrayList<String>())); + } else { + final FieldPanel panel = new AjaxTextFieldPanel("panel", field.getName(), + new Model<String>(null)); + panel.setRequired(true); + + component = new MultiFieldPanel<String>("field", + new PropertyModel(policy, field.getName()), panel); + + item.add(component); + + final List<String> reinitializedValue = new ArrayList<String>(); + + reinitializedValue.add(""); + + item.add(getActivationControl(component, + !((Collection) propDesc.getReadMethod().invoke(policy, new Object[] {})).isEmpty(), + new ArrayList<String>(), (Serializable) reinitializedValue)); + } + } else if (ClassUtils.isAssignable(Number.class, field.getType())) { + component = new SpinnerFieldPanel<Number>("field", field.getName(), + (Class<Number>) field.getType(), new PropertyModel<Number>(policy, field.getName()), + null, null); + item.add(component); + + item.add(getActivationControl(component, + (Integer) propDesc.getReadMethod().invoke(policy, new Object[] {}) > 0, 0, 0)); + } else if (field.getType().equals(String.class)) { + component = new AjaxTextFieldPanel("field", field.getName(), + new PropertyModel(policy, field.getName())); + + item.add(component); + + item.add(getActivationControl(component, + propDesc.getReadMethod().invoke(policy, new Object[] {}) != null, null, null)); + } else { + item.add(new AjaxCheckBoxPanel("check", field.getName(), new Model())); + item.add(new Label("field", new Model(null))); + } + } catch (Exception e) { + LOG.error("Error retrieving policy fields", e); + } + } + }; + + add(policies); + } + + private <T extends Serializable> AjaxCheckBoxPanel getActivationControl(final AbstractFieldPanel<T> panel, + final Boolean checked, final T defaultModelObject, final T reinitializedValue) { + + final AjaxCheckBoxPanel check = new AjaxCheckBoxPanel("check", "check", new Model<Boolean>(checked)); + + panel.setEnabled(checked); + + check.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + if (check.getModelObject()) { + panel.setEnabled(true); + panel.setModelObject(reinitializedValue); + } else { + panel.setModelObject(defaultModelObject); + panel.setEnabled(false); + } + + target.add(panel); + } + }); + + return check; + } + + private static class FieldWrapper implements Serializable { + + private static final long serialVersionUID = -6770429509752964215L; + + private Class<?> type; + + private String name; + + private transient SchemaList schemaList; + + private transient ClassList classList; + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public Class<?> getType() { + return type; + } + + public void setType(final Class<?> type) { + this.type = type; + } + + public SchemaList getSchemaList() { + return schemaList; + } + + public void setSchemaList(final SchemaList schemaList) { + this.schemaList = schemaList; + } + + public ClassList getClassList() { + return classList; + } + + public void setClassList(ClassList classList) { + this.classList = classList; + } + } +}
http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/panels/PropagationTasks.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/PropagationTasks.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/PropagationTasks.java new file mode 100644 index 0000000..41fdb03 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/PropagationTasks.java @@ -0,0 +1,264 @@ +/* + * 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.panels; + +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.pages.PropagationTaskModalPage; +import org.apache.syncope.client.console.pages.Tasks; +import org.apache.syncope.client.console.pages.Tasks.TasksProvider; +import org.apache.syncope.client.console.panels.AbstractSearchResultPanel.EventDataWrapper; +import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn; +import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn; +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.SyncopeClientException; +import org.apache.syncope.common.lib.to.AbstractTaskTO; +import org.apache.syncope.common.lib.to.PropagationTaskTO; +import org.apache.wicket.Component; +import org.apache.wicket.Page; +import org.apache.wicket.PageReference; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; +import org.apache.wicket.event.IEvent; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; +import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; +import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.DropDownChoice; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.model.StringResourceModel; +import org.apache.wicket.request.http.WebResponse; + +/** + * Tasks page. + */ +public class PropagationTasks extends AbstractTasks { + + private static final long serialVersionUID = 4984337552918213290L; + + private int paginatorRows; + + private WebMarkupContainer container; + + private boolean operationResult = false; + + private ModalWindow window; + + private AjaxDataTablePanel<AbstractTaskTO, String> table; + + public PropagationTasks(final String id, final PageReference pageRef) { + super(id, pageRef); + + container = new WebMarkupContainer("container"); + container.setOutputMarkupId(true); + add(container); + + add(window = new ModalWindow("taskWin")); + + paginatorRows = prefMan.getPaginatorRows(getWebRequest(), Constants.PREF_PROPAGATION_TASKS_PAGINATOR_ROWS); + + table = Tasks.updateTaskTable( + getColumns(), + new TasksProvider<PropagationTaskTO>(restClient, paginatorRows, getId(), PropagationTaskTO.class), + container, + 0, + pageRef, + restClient); + + window.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() { + + private static final long serialVersionUID = 8804221891699487139L; + + @Override + public void onClose(final AjaxRequestTarget target) { + target.add(container); + if (operationResult) { + info(getString(Constants.OPERATION_SUCCEEDED)); + target.add(getPage().get(Constants.FEEDBACK)); + operationResult = false; + } + } + }); + + window.setCssClassName(ModalWindow.CSS_CLASS_GRAY); + window.setInitialHeight(WIN_HEIGHT); + window.setInitialWidth(WIN_WIDTH); + window.setCookieName(VIEW_TASK_WIN_COOKIE_NAME); + + @SuppressWarnings("rawtypes") + Form paginatorForm = new Form("PaginatorForm"); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + final DropDownChoice rowsChooser = new DropDownChoice( + "rowsChooser", new PropertyModel(this, "paginatorRows"), prefMan.getPaginatorChoices()); + + rowsChooser.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + prefMan.set(getWebRequest(), (WebResponse) getResponse(), + Constants.PREF_PROPAGATION_TASKS_PAGINATOR_ROWS, String.valueOf(paginatorRows)); + + table = Tasks.updateTaskTable( + getColumns(), + new TasksProvider<>(restClient, paginatorRows, getId(), PropagationTaskTO.class), + container, + table == null ? 0 : (int) table.getCurrentPage(), + pageRef, + restClient); + + target.add(container); + } + }); + + paginatorForm.add(rowsChooser); + add(paginatorForm); + } + + private List<IColumn<AbstractTaskTO, String>> getColumns() { + final List<IColumn<AbstractTaskTO, String>> columns = new ArrayList<>(); + + columns.add(new PropertyColumn<AbstractTaskTO, String>( + new StringResourceModel("key", this, null), "key", "key")); + columns.add(new PropertyColumn<AbstractTaskTO, String>( + new StringResourceModel("resource", this, null), "resource", "resource")); + columns.add(new PropertyColumn<AbstractTaskTO, String>( + new StringResourceModel("accountId", this, null), "accountId", "accountId")); + columns.add(new PropertyColumn<AbstractTaskTO, String>( + new StringResourceModel("propagationMode", this, null), "propagationMode", "propagationMode")); + columns.add(new PropertyColumn<AbstractTaskTO, String>(new StringResourceModel( + "propagationOperation", this, null), "propagationOperation", "propagationOperation")); + columns.add(new DatePropertyColumn<AbstractTaskTO>( + new StringResourceModel("startDate", this, null), "startDate", "startDate")); + columns.add(new DatePropertyColumn<AbstractTaskTO>( + new StringResourceModel("endDate", this, null), "endDate", "endDate")); + columns.add(new PropertyColumn<AbstractTaskTO, String>( + new StringResourceModel("latestExecStatus", this, null), "latestExecStatus", "latestExecStatus")); + columns.add(new ActionColumn<AbstractTaskTO, String>(new StringResourceModel("actions", this, null, "")) { + + private static final long serialVersionUID = 2054811145491901166L; + + @Override + public String getCssClass() { + return "action"; + } + + @Override + public ActionLinksPanel getActions(final String componentId, final IModel<AbstractTaskTO> model) { + + final AbstractTaskTO taskTO = model.getObject(); + + final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, pageRef); + + panel.add(new ActionLink() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target) { + + window.setPageCreator(new ModalWindow.PageCreator() { + + private static final long serialVersionUID = -7834632442532690940L; + + @Override + public Page createPage() { + return new PropagationTaskModalPage(taskTO); + } + }); + + window.show(target); + } + }, ActionLink.ActionType.EDIT, TASKS); + + panel.add(new ActionLink() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target) { + try { + restClient.startExecution(taskTO.getKey(), false); + getSession().info(getString(Constants.OPERATION_SUCCEEDED)); + } catch (SyncopeClientException scce) { + error(scce.getMessage()); + } + + ((NotificationPanel) getPage().get(Constants.FEEDBACK)).refresh(target); + target.add(container); + } + }, ActionLink.ActionType.EXECUTE, TASKS); + + panel.add(new ActionLink() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target) { + try { + restClient.delete(taskTO.getKey(), PropagationTaskTO.class); + info(getString(Constants.OPERATION_SUCCEEDED)); + } catch (SyncopeClientException scce) { + error(scce.getMessage()); + } + target.add(container); + ((NotificationPanel) getPage().get(Constants.FEEDBACK)).refresh(target); + } + }, ActionLink.ActionType.DELETE, TASKS); + + return panel; + } + + @Override + public Component getHeader(final String componentId) { + final ActionLinksPanel panel = new ActionLinksPanel(componentId, new Model(), pageRef); + + panel.add(new ActionLink() { + + private static final long serialVersionUID = -7978723352517770644L; + + @Override + public void onClick(final AjaxRequestTarget target) { + if (target != null) { + target.add(table); + } + } + }, ActionLink.ActionType.RELOAD, TASKS, "list"); + + return panel; + } + }); + + return columns; + } + + @Override + public void onEvent(final IEvent<?> event) { + if (event.getPayload() instanceof EventDataWrapper) { + ((EventDataWrapper) event.getPayload()).getTarget().add(container); + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/panels/PushTasksPanel.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/PushTasksPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/PushTasksPanel.java new file mode 100644 index 0000000..a93000d --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/PushTasksPanel.java @@ -0,0 +1,184 @@ +/* + * 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.panels; + +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.pages.PushTaskModalPage; +import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn; +import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn; +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.SyncopeClientException; +import org.apache.syncope.common.lib.to.AbstractTaskTO; +import org.apache.syncope.common.lib.to.PushTaskTO; +import org.apache.syncope.common.lib.to.SyncTaskTO; +import org.apache.wicket.Component; +import org.apache.wicket.Page; +import org.apache.wicket.PageReference; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; +import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; +import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.StringResourceModel; + +public class PushTasksPanel extends AbstractProvisioningTasksPanel<PushTaskTO> { + + private static final long serialVersionUID = -2492299671757861889L; + + public PushTasksPanel(final String id, final PageReference pageRef) { + super(id, pageRef, PushTaskTO.class); + initTasksTable(); + } + + @Override + protected List<IColumn<AbstractTaskTO, String>> getColumns() { + final List<IColumn<AbstractTaskTO, String>> pushTasksColumns = new ArrayList<IColumn<AbstractTaskTO, String>>(); + + pushTasksColumns.add(new PropertyColumn<AbstractTaskTO, String>( + new StringResourceModel("key", this, null), "key", "key")); + pushTasksColumns.add(new PropertyColumn<AbstractTaskTO, String>( + new StringResourceModel("name", this, null), "name", "name")); + pushTasksColumns.add(new PropertyColumn<AbstractTaskTO, String>( + new StringResourceModel("description", this, null), "description", "description")); + pushTasksColumns.add(new PropertyColumn<AbstractTaskTO, String>( + new StringResourceModel("resourceName", this, null), "resource", "resource")); + pushTasksColumns.add(new DatePropertyColumn<AbstractTaskTO>( + new StringResourceModel("lastExec", this, null), "lastExec", "lastExec")); + pushTasksColumns.add(new DatePropertyColumn<AbstractTaskTO>( + new StringResourceModel("nextExec", this, null), "nextExec", "nextExec")); + pushTasksColumns.add(new PropertyColumn<AbstractTaskTO, String>( + new StringResourceModel("latestExecStatus", this, null), "latestExecStatus", "latestExecStatus")); + + pushTasksColumns.add( + new ActionColumn<AbstractTaskTO, String>(new StringResourceModel("actions", this, null, "")) { + + private static final long serialVersionUID = 2054811145491901166L; + + @Override + public ActionLinksPanel getActions(final String componentId, final IModel<AbstractTaskTO> model) { + + final PushTaskTO taskTO = (PushTaskTO) model.getObject(); + + final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, pageRef); + + panel.add(new ActionLink() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target) { + + window.setPageCreator(new ModalWindow.PageCreator() { + + private static final long serialVersionUID = -7834632442532690940L; + + @Override + public Page createPage() { + return new PushTaskModalPage(window, taskTO, pageRef); + } + }); + + window.show(target); + } + }, ActionLink.ActionType.EDIT, TASKS); + + panel.add(new ActionLink() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target) { + try { + restClient.startExecution(taskTO.getKey(), false); + getSession().info(getString(Constants.OPERATION_SUCCEEDED)); + } catch (SyncopeClientException scce) { + error(scce.getMessage()); + } + + target.add(container); + ((NotificationPanel) getPage().get(Constants.FEEDBACK)).refresh(target); + } + }, ActionLink.ActionType.EXECUTE, TASKS); + + panel.add(new ActionLink() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target) { + try { + restClient.startExecution(taskTO.getKey(), true); + getSession().info(getString(Constants.OPERATION_SUCCEEDED)); + } catch (SyncopeClientException scce) { + error(scce.getMessage()); + } + + target.add(container); + ((NotificationPanel) getPage().get(Constants.FEEDBACK)).refresh(target); + } + }, ActionLink.ActionType.DRYRUN, TASKS); + + panel.add(new ActionLink() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target) { + try { + restClient.delete(taskTO.getKey(), SyncTaskTO.class); + info(getString(Constants.OPERATION_SUCCEEDED)); + } catch (SyncopeClientException scce) { + error(scce.getMessage()); + } + target.add(container); + ((NotificationPanel) getPage().get(Constants.FEEDBACK)).refresh(target); + } + }, ActionLink.ActionType.DELETE, TASKS); + + return panel; + } + + @Override + public Component getHeader(final String componentId) { + final ActionLinksPanel panel = new ActionLinksPanel(componentId, new Model(), pageRef); + + panel.add(new ActionLink() { + + private static final long serialVersionUID = -7978723352517770644L; + + @Override + public void onClick(final AjaxRequestTarget target) { + if (target != null) { + target.add(table); + } + } + }, ActionLink.ActionType.RELOAD, TASKS, "list"); + + return panel; + } + }); + + return pushTasksColumns; + + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceConnConfPanel.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceConnConfPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceConnConfPanel.java new file mode 100644 index 0000000..b3e63c5 --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceConnConfPanel.java @@ -0,0 +1,187 @@ +/* + * 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.panels; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.syncope.client.console.pages.BaseModalPage; +import org.apache.syncope.client.console.pages.ResourceModalPage.ResourceEvent; +import org.apache.syncope.client.console.panels.ResourceDetailsPanel.DetailsModEvent; +import org.apache.syncope.client.console.rest.ConnectorRestClient; +import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel.MultiValueSelectorEvent; +import org.apache.syncope.client.console.wicket.markup.html.list.ConnConfPropertyListView; +import org.apache.syncope.common.lib.to.ResourceTO; +import org.apache.syncope.common.lib.types.ConnConfProperty; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.form.AjaxButton; +import org.apache.wicket.event.Broadcast; +import org.apache.wicket.event.IEvent; +import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.model.ResourceModel; +import org.apache.wicket.spring.injection.annot.SpringBean; + +public class ResourceConnConfPanel extends Panel { + + private static final long serialVersionUID = -7982691107029848579L; + + @SpringBean + private ConnectorRestClient restClient; + + private final ResourceTO resourceTO; + + private final boolean createFlag; + + private List<ConnConfProperty> connConfProperties; + + private WebMarkupContainer connConfPropContainer; + + private AjaxButton check; + + public ResourceConnConfPanel(final String id, final ResourceTO resourceTO, final boolean createFlag) { + super(id); + setOutputMarkupId(true); + + this.createFlag = createFlag; + this.resourceTO = resourceTO; + + connConfProperties = getConnConfProperties(); + + connConfPropContainer = new WebMarkupContainer("connectorPropertiesContainer"); + connConfPropContainer.setOutputMarkupId(true); + add(connConfPropContainer); + + /* + * the list of overridable connector properties + */ + final ListView<ConnConfProperty> connPropView = new ConnConfPropertyListView("connectorProperties", + new PropertyModel<List<ConnConfProperty>>(this, "connConfProperties"), + false, resourceTO.getConnConfProperties()); + connPropView.setOutputMarkupId(true); + connConfPropContainer.add(connPropView); + + check = new IndicatingAjaxButton("check", new ResourceModel("check")) { + + private static final long serialVersionUID = -4199438518229098169L; + + @Override + public void onSubmit(final AjaxRequestTarget target, final Form<?> form) { + final ResourceTO resourceTO = (ResourceTO) form.getModelObject(); + + if (restClient.check(resourceTO)) { + info(getString("success_connection")); + } else { + error(getString("error_connection")); + } + + ((BaseModalPage) getPage()).getFeedbackPanel().refresh(target); + } + }; + + check.setEnabled(!connConfProperties.isEmpty()); + connConfPropContainer.add(check); + } + + /** + * Get overridable properties. + * + * @return overridable properties. + */ + private List<ConnConfProperty> getConnConfProperties() { + final List<ConnConfProperty> props = new ArrayList<ConnConfProperty>(); + final Long connectorId = resourceTO.getConnectorId(); + if (connectorId != null && connectorId > 0) { + for (ConnConfProperty property : restClient.getConnectorProperties(connectorId)) { + if (property.isOverridable()) { + props.add(property); + } + } + } + if (createFlag || resourceTO.getConnConfProperties().isEmpty()) { + resourceTO.getConnConfProperties().clear(); + } else { + Map<String, ConnConfProperty> valuedProps = new HashMap<String, ConnConfProperty>(); + for (ConnConfProperty prop : resourceTO.getConnConfProperties()) { + valuedProps.put(prop.getSchema().getName(), prop); + } + + for (int i = 0; i < props.size(); i++) { + if (valuedProps.containsKey(props.get(i).getSchema().getName())) { + props.set(i, valuedProps.get(props.get(i).getSchema().getName())); + } + } + } + + // re-order properties + Collections.sort(props); + + return props; + } + + @Override + public void onEvent(final IEvent<?> event) { + AjaxRequestTarget target = null; + if (event.getPayload() instanceof DetailsModEvent) { + // connector change: update properties and forward event + target = ((ResourceEvent) event.getPayload()).getTarget(); + + connConfProperties = getConnConfProperties(); + check.setEnabled(!connConfProperties.isEmpty()); + + target.add(connConfPropContainer); + } else if (event.getPayload() instanceof MultiValueSelectorEvent) { + // multi value connector property change: forward event + target = ((MultiValueSelectorEvent) event.getPayload()).getTarget(); + } + + if (target != null) { + send(getPage(), Broadcast.BREADTH, new ConnConfModEvent(target, connConfProperties)); + } + } + + /** + * Connector configuration properties modification event. + */ + public static class ConnConfModEvent extends ResourceEvent { + + private List<ConnConfProperty> configuration; + + /** + * Constructor. + * + * @param target request target. + * @param configuration connector configuration properties. + */ + public ConnConfModEvent(final AjaxRequestTarget target, final List<ConnConfProperty> configuration) { + super(target); + this.configuration = configuration; + } + + public List<ConnConfProperty> getConfiguration() { + return configuration; + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDetailsPanel.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDetailsPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDetailsPanel.java new file mode 100644 index 0000000..3bff23f --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDetailsPanel.java @@ -0,0 +1,306 @@ +/* + * 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.panels; + +import java.util.Arrays; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.pages.ResourceModalPage.ResourceEvent; +import org.apache.syncope.client.console.rest.ConnectorRestClient; +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.client.console.wicket.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.SpinnerFieldPanel; +import org.apache.syncope.common.lib.to.ConnInstanceTO; +import org.apache.syncope.common.lib.to.ResourceTO; +import org.apache.syncope.common.lib.types.PropagationMode; +import org.apache.syncope.common.lib.types.TraceLevel; +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.event.Broadcast; +import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.ChoiceRenderer; +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.markup.html.panel.Panel; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.model.ResourceModel; +import org.apache.wicket.spring.injection.annot.SpringBean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ResourceDetailsPanel extends Panel { + + private static final long serialVersionUID = -7982691107029848579L; + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(ResourceDetailsPanel.class); + + @SpringBean + private ConnectorRestClient connRestClient; + + private ConnInstanceTO connInstanceTO; + + public ResourceDetailsPanel(final String id, final ResourceTO resourceTO, final List<String> actionClassNames, + final boolean createFlag) { + + super(id); + setOutputMarkupId(true); + + final AjaxTextFieldPanel resourceName = new AjaxTextFieldPanel("name", new ResourceModel("name", "name"). + getObject(), new PropertyModel<String>(resourceTO, "key")); + + resourceName.setEnabled(createFlag); + resourceName.addRequiredLabel(); + add(resourceName); + + final AjaxCheckBoxPanel enforceMandatoryCondition = new AjaxCheckBoxPanel("enforceMandatoryCondition", + new ResourceModel("enforceMandatoryCondition", "enforceMandatoryCondition").getObject(), + new PropertyModel<Boolean>(resourceTO, "enforceMandatoryCondition")); + add(enforceMandatoryCondition); + + final AjaxCheckBoxPanel propagationPrimary = new AjaxCheckBoxPanel("propagationPrimary", new ResourceModel( + "propagationPrimary", "propagationPrimary").getObject(), new PropertyModel<Boolean>(resourceTO, + "propagationPrimary")); + add(propagationPrimary); + + final SpinnerFieldPanel<Integer> propagationPriority = + new SpinnerFieldPanel<>("propagationPriority", "propagationPriority", Integer.class, + new PropertyModel<Integer>(resourceTO, "propagationPriority"), null, null); + add(propagationPriority); + + final AjaxDropDownChoicePanel<PropagationMode> propagationMode = new AjaxDropDownChoicePanel<>( + "propagationMode", new ResourceModel("propagationMode", "propagationMode").getObject(), + new PropertyModel<PropagationMode>(resourceTO, "propagationMode")); + propagationMode.setChoices(Arrays.asList(PropagationMode.values())); + add(propagationMode); + + final AjaxCheckBoxPanel randomPwdIfNotProvided = new AjaxCheckBoxPanel("randomPwdIfNotProvided", + new ResourceModel("randomPwdIfNotProvided", "randomPwdIfNotProvided").getObject(), + new PropertyModel<Boolean>(resourceTO, "randomPwdIfNotProvided")); + add(randomPwdIfNotProvided); + + final WebMarkupContainer propagationActionsClassNames = new WebMarkupContainer("propagationActionsClassNames"); + propagationActionsClassNames.setOutputMarkupId(true); + add(propagationActionsClassNames); + + final AjaxLink<Void> first = new IndicatingAjaxLink<Void>("first") { + + private static final long serialVersionUID = -7978723352517770644L; + + @Override + public void onClick(final AjaxRequestTarget target) { + resourceTO.getPropagationActionsClassNames().add(StringUtils.EMPTY); + setVisible(false); + target.add(propagationActionsClassNames); + } + }; + first.setOutputMarkupPlaceholderTag(true); + first.setVisible(resourceTO.getPropagationActionsClassNames().isEmpty()); + propagationActionsClassNames.add(first); + + final ListView<String> actionsClasses = new ListView<String>("actionsClasses", + new PropertyModel<List<String>>(resourceTO, "propagationActionsClassNames")) { + + 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<>( + "actionsClass", new Model<>(className), actionClassNames); + actionsClass.setNullValid(true); + actionsClass.setRequired(true); + actionsClass.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + resourceTO.getPropagationActionsClassNames(). + set(item.getIndex(), actionsClass.getModelObject()); + } + }); + 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) { + resourceTO.getPropagationActionsClassNames().remove(className); + first.setVisible(resourceTO.getPropagationActionsClassNames().isEmpty()); + target.add(propagationActionsClassNames); + } + }; + item.add(minus); + + final AjaxLink<Void> plus = new IndicatingAjaxLink<Void>("add") { + + private static final long serialVersionUID = -7978723352517770644L; + + @Override + public void onClick(final AjaxRequestTarget target) { + resourceTO.getPropagationActionsClassNames().add(StringUtils.EMPTY); + target.add(propagationActionsClassNames); + } + }; + plus.setOutputMarkupPlaceholderTag(true); + plus.setVisible(item.getIndex() == resourceTO.getPropagationActionsClassNames().size() - 1); + item.add(plus); + } + }; + propagationActionsClassNames.add(actionsClasses); + + final AjaxDropDownChoicePanel<TraceLevel> createTraceLevel = new AjaxDropDownChoicePanel<>( + "createTraceLevel", new ResourceModel("createTraceLevel", "createTraceLevel").getObject(), + new PropertyModel<TraceLevel>(resourceTO, "createTraceLevel")); + createTraceLevel.setChoices(Arrays.asList(TraceLevel.values())); + add(createTraceLevel); + + final AjaxDropDownChoicePanel<TraceLevel> updateTraceLevel = new AjaxDropDownChoicePanel<>( + "updateTraceLevel", new ResourceModel("updateTraceLevel", "updateTraceLevel").getObject(), + new PropertyModel<TraceLevel>(resourceTO, "updateTraceLevel")); + updateTraceLevel.setChoices(Arrays.asList(TraceLevel.values())); + add(updateTraceLevel); + + final AjaxDropDownChoicePanel<TraceLevel> deleteTraceLevel = new AjaxDropDownChoicePanel<>( + "deleteTraceLevel", new ResourceModel("deleteTraceLevel", "deleteTraceLevel").getObject(), + new PropertyModel<TraceLevel>(resourceTO, "deleteTraceLevel")); + deleteTraceLevel.setChoices(Arrays.asList(TraceLevel.values())); + add(deleteTraceLevel); + + final AjaxDropDownChoicePanel<TraceLevel> syncTraceLevel = new AjaxDropDownChoicePanel<>( + "syncTraceLevel", new ResourceModel("syncTraceLevel", "syncTraceLevel").getObject(), + new PropertyModel<TraceLevel>(resourceTO, "syncTraceLevel")); + syncTraceLevel.setChoices(Arrays.asList(TraceLevel.values())); + add(syncTraceLevel); + + final IModel<List<ConnInstanceTO>> connectors = new LoadableDetachableModel<List<ConnInstanceTO>>() { + + private static final long serialVersionUID = 5275935387613157437L; + + @Override + protected List<ConnInstanceTO> load() { + return connRestClient.getAllConnectors(); + } + }; + + connInstanceTO = getConectorInstanceTO(connectors.getObject(), resourceTO); + + final AjaxDropDownChoicePanel<ConnInstanceTO> conn = new AjaxDropDownChoicePanel<>("connector", + new ResourceModel("connector", "connector").getObject(), + new PropertyModel<ConnInstanceTO>(this, "connInstanceTO")); + conn.setChoices(connectors.getObject()); + conn.setChoiceRenderer(new ChoiceRenderer("displayName", "key")); + + conn.getField().setModel(new IModel<ConnInstanceTO>() { + + private static final long serialVersionUID = -4202872830392400310L; + + @Override + public ConnInstanceTO getObject() { + return connInstanceTO; + } + + @Override + public void setObject(final ConnInstanceTO connector) { + resourceTO.setConnectorId(connector.getKey()); + connInstanceTO = connector; + } + + @Override + public void detach() { + } + }); + + conn.addRequiredLabel(); + conn.setEnabled(createFlag); + + conn.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + send(getPage(), Broadcast.BREADTH, new DetailsModEvent(target)); + } + }); + + add(conn); + } + + /** + * Get the connetorTO linked to the resource. + * + * @param connectorTOs list of all connectors. + * @param resourceTO resource. + * @return selected connector instance: in case of no connectors available, null; in case of new resource + * specification, the first on connector available + */ + private ConnInstanceTO getConectorInstanceTO(final List<ConnInstanceTO> connectorTOs, final ResourceTO resourceTO) { + if (connectorTOs.isEmpty()) { + resourceTO.setConnectorId(null); + return null; + } else { + // use the first element as default + ConnInstanceTO res = connectorTOs.get(0); + + for (ConnInstanceTO to : connectorTOs) { + if (Long.valueOf(to.getKey()).equals(resourceTO.getConnectorId())) { + res = to; + } + } + + // in case of no match + resourceTO.setConnectorId(res.getKey()); + + return res; + } + } + + /** + * Connector instance modification event. + */ + public static class DetailsModEvent extends ResourceEvent { + + /** + * Constructor. + * + * @param target request target. + */ + public DetailsModEvent(final AjaxRequestTarget target) { + super(target); + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java new file mode 100644 index 0000000..ecd7e0c --- /dev/null +++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java @@ -0,0 +1,644 @@ +/* + * 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.panels; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.syncope.client.console.commons.Constants; +import org.apache.syncope.client.console.commons.JexlHelpUtil; +import org.apache.syncope.client.console.panels.ResourceConnConfPanel.ConnConfModEvent; +import org.apache.syncope.client.console.rest.ConnectorRestClient; +import org.apache.syncope.client.console.rest.SchemaRestClient; +import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDecoratedCheckbox; +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.FieldPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.MappingPurposePanel; +import org.apache.syncope.common.lib.to.ConnIdObjectClassTO; +import org.apache.syncope.common.lib.to.ConnInstanceTO; +import org.apache.syncope.common.lib.to.MappingItemTO; +import org.apache.syncope.common.lib.to.MappingTO; +import org.apache.syncope.common.lib.to.ResourceTO; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.common.lib.types.ConnConfProperty; +import org.apache.syncope.common.lib.types.IntMappingType; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.wicket.Component; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.attributes.AjaxCallListener; +import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; +import org.apache.wicket.ajax.markup.html.AjaxLink; +import org.apache.wicket.ajax.markup.html.form.AjaxButton; +import org.apache.wicket.event.IEvent; +import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton; +import org.apache.wicket.markup.html.WebMarkupContainer; +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.markup.html.panel.Panel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.model.ResourceModel; +import org.apache.wicket.spring.injection.annot.SpringBean; + +/** + * Resource mapping panel. + */ +public class ResourceMappingPanel extends Panel { + + private static final long serialVersionUID = -7982691107029848579L; + + /** + * Mapping field style sheet. + */ + private static final String FIELD_STYLE = "ui-widget-content ui-corner-all short_fixedsize"; + + /** + * Mapping field style sheet. + */ + private static final String DEF_FIELD_STYLE = "ui-widget-content ui-corner-all"; + + /** + * Mapping field style sheet. + */ + private static final String SHORT_FIELD_STYLE = "ui-widget-content ui-corner-all veryshort_fixedsize"; + + /** + * Schema rest client. + */ + @SpringBean + private SchemaRestClient schemaRestClient; + + /** + * ConnInstance rest client. + */ + @SpringBean + private ConnectorRestClient connRestClient; + + /** + * Resource schema name. + */ + private final List<String> schemaNames; + + /** + * Add mapping button. + */ + private final AjaxButton addMappingBtn; + + /** + * All mappings. + */ + private final ListView<MappingItemTO> mappings; + + /** + * External resource to be updated. + */ + private final ResourceTO resourceTO; + + /** + * User / role. + */ + private final AttributableType attrType; + + /** + * Mapping container. + */ + private final WebMarkupContainer mappingContainer; + + /** + * AccountLink container. + */ + private final WebMarkupContainer accountLinkContainer; + + private final AjaxCheckBoxPanel accountLinkCheckbox; + + private MappingTO getMapping() { + MappingTO result = null; + + if (AttributableType.USER == this.attrType) { + if (this.resourceTO.getUmapping() == null) { + this.resourceTO.setUmapping(new MappingTO()); + } + result = this.resourceTO.getUmapping(); + } + if (AttributableType.ROLE == this.attrType) { + if (this.resourceTO.getRmapping() == null) { + this.resourceTO.setRmapping(new MappingTO()); + } + result = this.resourceTO.getRmapping(); + } + + return result; + } + + /** + * Attribute Mapping Panel. + * + * @param id panel id + * @param resourceTO external resource + * @param attrType USER / ROLE + */ + public ResourceMappingPanel(final String id, final ResourceTO resourceTO, final AttributableType attrType) { + super(id); + setOutputMarkupId(true); + + this.resourceTO = resourceTO; + this.attrType = attrType; + + this.mappingContainer = new WebMarkupContainer("mappingContainer"); + this.mappingContainer.setOutputMarkupId(true); + add(this.mappingContainer); + + this.accountLinkContainer = new WebMarkupContainer("accountLinkContainer"); + this.accountLinkContainer.setOutputMarkupId(true); + add(this.accountLinkContainer); + + if (this.resourceTO.getConnectorId() != null && this.resourceTO.getConnectorId() > 0) { + schemaNames = getSchemaNames(this.resourceTO.getConnectorId(), this.resourceTO.getConnConfProperties()); + + setEnabled(); + } else { + schemaNames = Collections.<String>emptyList(); + } + + final WebMarkupContainer jexlHelp = JexlHelpUtil.getJexlHelpWebContainer("jexlHelp"); + + AjaxLink<Void> questionMarkJexlHelp = JexlHelpUtil.getAjaxLink(jexlHelp, "questionMarkJexlHelp"); + mappingContainer.add(questionMarkJexlHelp); + questionMarkJexlHelp.add(jexlHelp); + + final Label passwordLabel = new Label("passwordLabel", new ResourceModel("password")); + mappingContainer.add(passwordLabel); + if (AttributableType.USER != ResourceMappingPanel.this.attrType) { + passwordLabel.setVisible(false); + } + + Collections.sort(getMapping().getItems(), new Comparator<MappingItemTO>() { + + @Override + public int compare(final MappingItemTO left, final MappingItemTO right) { + int compared; + if (left == null && right == null) { + compared = 0; + } else if (left == null) { + compared = 1; + } else if (right == null) { + compared = -1; + } else if (left.getPurpose() == MappingPurpose.BOTH && right.getPurpose() != MappingPurpose.BOTH) { + compared = -1; + } else if (left.getPurpose() != MappingPurpose.BOTH && right.getPurpose() == MappingPurpose.BOTH) { + compared = 1; + } else if (left.getPurpose() == MappingPurpose.PROPAGATION + && (right.getPurpose() == MappingPurpose.SYNCHRONIZATION || right.getPurpose() + == MappingPurpose.NONE)) { + compared = -1; + } else if (left.getPurpose() == MappingPurpose.SYNCHRONIZATION + && right.getPurpose() == MappingPurpose.PROPAGATION) { + compared = 1; + } else if (left.getPurpose() == MappingPurpose.SYNCHRONIZATION + && right.getPurpose() == MappingPurpose.NONE) { + compared = -1; + } else if (left.getPurpose() == MappingPurpose.NONE + && right.getPurpose() != MappingPurpose.NONE) { + compared = 1; + } else if (left.isAccountid()) { + compared = -1; + } else if (right.isAccountid()) { + compared = 1; + } else if (left.isPassword()) { + compared = -1; + } else if (right.isPassword()) { + compared = 1; + } else { + compared = left.getIntAttrName().compareTo(right.getIntAttrName()); + } + return compared; + } + }); + + mappings = new ListView<MappingItemTO>("mappings", getMapping().getItems()) { + + private static final long serialVersionUID = 4949588177564901031L; + + @Override + protected void populateItem(final ListItem<MappingItemTO> item) { + final MappingItemTO mapItem = item.getModelObject(); + if (mapItem.getPurpose() == null) { + mapItem.setPurpose(MappingPurpose.BOTH); + } + + AttributableType entity = null; + if (mapItem.getIntMappingType() != null) { + entity = mapItem.getIntMappingType().getAttributableType(); + } + + final List<IntMappingType> attrTypes = new ArrayList<IntMappingType>(getAttributeTypes(entity)); + + item.add(new AjaxDecoratedCheckbox("toRemove", new Model<Boolean>(Boolean.FALSE)) { + + private static final long serialVersionUID = 7170946748485726506L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + int index = -1; + for (int i = 0; i < getMapping().getItems().size() && index == -1; i++) { + if (mapItem.equals(getMapping().getItems().get(i))) { + index = i; + } + } + + if (index != -1) { + getMapping().getItems().remove(index); + item.getParent().removeAll(); + target.add(ResourceMappingPanel.this); + } + } + + @Override + protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) { + super.updateAjaxAttributes(attributes); + + final AjaxCallListener ajaxCallListener = new AjaxCallListener() { + + private static final long serialVersionUID = 7160235486520935153L; + + @Override + public CharSequence getPrecondition(final Component component) { + return "if (!confirm('" + getString("confirmDelete") + "')) return false;"; + } + }; + attributes.getAjaxCallListeners().add(ajaxCallListener); + } + }); + + final AjaxDropDownChoicePanel<String> intAttrNames = + new AjaxDropDownChoicePanel<String>("intAttrNames", getString("intAttrNames"), + new PropertyModel<String>(mapItem, "intAttrName"), false); + intAttrNames.setChoices(schemaNames); + intAttrNames.setRequired(true); + intAttrNames.setStyleSheet(FIELD_STYLE); + intAttrNames.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + } + }); + item.add(intAttrNames); + + final AjaxDropDownChoicePanel<IntMappingType> intMappingTypes = + new AjaxDropDownChoicePanel<IntMappingType>("intMappingTypes", + new ResourceModel("intMappingTypes", "intMappingTypes").getObject(), + new PropertyModel<IntMappingType>(mapItem, "intMappingType")); + intMappingTypes.setRequired(true); + intMappingTypes.setChoices(attrTypes); + intMappingTypes.setStyleSheet(FIELD_STYLE); + item.add(intMappingTypes); + + final AjaxDropDownChoicePanel<AttributableType> entitiesPanel = + new AjaxDropDownChoicePanel<AttributableType>("entities", + new ResourceModel("entities", "entities").getObject(), new Model<AttributableType>( + entity)); + entitiesPanel.setChoices(attrType == AttributableType.ROLE + ? Collections.<AttributableType>singletonList(AttributableType.ROLE) + : Arrays.asList(AttributableType.values())); + entitiesPanel.setStyleSheet(DEF_FIELD_STYLE); + entitiesPanel.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + attrTypes.clear(); + attrTypes.addAll(getAttributeTypes(entitiesPanel.getModelObject())); + intMappingTypes.setChoices(attrTypes); + + intAttrNames.setChoices(Collections.<String>emptyList()); + + target.add(intMappingTypes.getField()); + target.add(intAttrNames.getField()); + } + }); + item.add(entitiesPanel); + + final FieldPanel<String> extAttrNames = new AjaxTextFieldPanel("extAttrName", + new ResourceModel("extAttrNames", "extAttrNames").getObject(), + new PropertyModel<String>(mapItem, "extAttrName")); + ((AjaxTextFieldPanel) extAttrNames).setChoices(schemaNames); + + boolean required = false; + if (mapItem.isPassword()) { + ((AjaxTextFieldPanel) extAttrNames).setModelObject(null); + } else { + required = true; + } + extAttrNames.setRequired(required); + extAttrNames.setEnabled(required); + extAttrNames.setStyleSheet(FIELD_STYLE); + item.add(extAttrNames); + + final AjaxTextFieldPanel mandatory = new AjaxTextFieldPanel("mandatoryCondition", + new ResourceModel("mandatoryCondition", "mandatoryCondition").getObject(), + new PropertyModel<String>(mapItem, "mandatoryCondition")); + mandatory.setChoices(Arrays.asList(new String[] { "true", "false" })); + mandatory.setStyleSheet(SHORT_FIELD_STYLE); + item.add(mandatory); + + final AjaxCheckBoxPanel accountId = new AjaxCheckBoxPanel("accountId", + new ResourceModel("accountId", "accountId").getObject(), + new PropertyModel<Boolean>(mapItem, "accountid")); + accountId.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + if (accountId.getModelObject()) { + mapItem.setMandatoryCondition("true"); + mandatory.setEnabled(false); + } else { + mapItem.setMandatoryCondition("false"); + mandatory.setEnabled(true); + } + target.add(mandatory); + } + }); + item.add(accountId); + + final AjaxCheckBoxPanel password = new AjaxCheckBoxPanel("password", + new ResourceModel("password", "password").getObject(), + new PropertyModel<Boolean>(mapItem, "password")); + password.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + extAttrNames.setEnabled(!mapItem.isAccountid() && !password.getModelObject()); + extAttrNames.setModelObject(null); + extAttrNames.setRequired(!password.getModelObject()); + target.add(extAttrNames); + + setAccountId(intMappingTypes.getModelObject(), accountId, password); + target.add(accountId); + } + }); + item.add(password); + if (AttributableType.USER != ResourceMappingPanel.this.attrType) { + password.setVisible(false); + } + + final WebMarkupContainer purpose = new WebMarkupContainer("purpose"); + purpose.setOutputMarkupId(Boolean.TRUE); + + final MappingPurposePanel panel = new MappingPurposePanel("purposeActions", + new PropertyModel<MappingPurpose>(mapItem, "purpose"), purpose); + + purpose.add(panel); + + item.add(purpose); + + intMappingTypes.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + setAttrNames(intMappingTypes.getModelObject(), intAttrNames); + target.add(intAttrNames); + + setAccountId(intMappingTypes.getModelObject(), accountId, password); + target.add(accountId); + } + }); + + setAttrNames(mapItem.getIntMappingType(), intAttrNames); + setAccountId(mapItem.getIntMappingType(), accountId, password); + } + }; + + mappings.setReuseItems(true); + mappingContainer.add(mappings); + + addMappingBtn = new IndicatingAjaxButton("addMappingBtn", new ResourceModel("add")) { + + private static final long serialVersionUID = -4804368561204623354L; + + @Override + protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) { + getMapping().getItems().add(new MappingItemTO()); + target.add(ResourceMappingPanel.this); + } + }; + addMappingBtn.setDefaultFormProcessing(false); + addMappingBtn.setEnabled(this.resourceTO.getConnectorId() != null && this.resourceTO.getConnectorId() > 0); + mappingContainer.add(addMappingBtn); + + boolean accountLinkEnabled = false; + if (getMapping().getAccountLink() != null) { + accountLinkEnabled = true; + } + accountLinkCheckbox = new AjaxCheckBoxPanel("accountLinkCheckbox", + new ResourceModel("accountLinkCheckbox", "accountLinkCheckbox").getObject(), + new Model<Boolean>(Boolean.valueOf(accountLinkEnabled))); + accountLinkCheckbox.setEnabled(true); + + accountLinkContainer.add(accountLinkCheckbox); + + final AjaxTextFieldPanel accountLink = new AjaxTextFieldPanel("accountLink", + new ResourceModel("accountLink", "accountLink").getObject(), + new PropertyModel<String>(getMapping(), "accountLink")); + accountLink.setEnabled(accountLinkEnabled); + accountLinkContainer.add(accountLink); + + accountLinkCheckbox.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -1107858522700306810L; + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + if (accountLinkCheckbox.getModelObject()) { + accountLink.setEnabled(Boolean.TRUE); + accountLink.setModelObject(""); + } else { + accountLink.setEnabled(Boolean.FALSE); + accountLink.setModelObject(""); + } + + target.add(accountLink); + } + }); + } + + private List<String> getSchemaNames(final Long connectorId, final Set<ConnConfProperty> conf) { + final ConnInstanceTO connInstanceTO = new ConnInstanceTO(); + connInstanceTO.setKey(connectorId); + connInstanceTO.getConfiguration().addAll(conf); + + return connRestClient.getSchemaNames(connInstanceTO); + } + + private void setEnabled() { + final ConnInstanceTO connInstanceTO = new ConnInstanceTO(); + connInstanceTO.setKey(this.resourceTO.getConnectorId()); + connInstanceTO.getConfiguration().addAll(this.resourceTO.getConnConfProperties()); + + List<ConnIdObjectClassTO> objectClasses = connRestClient.getSupportedObjectClasses(connInstanceTO); + + boolean enabled = objectClasses.isEmpty() + || (AttributableType.USER == attrType && objectClasses.contains(ConnIdObjectClassTO.ACCOUNT)) + || (AttributableType.ROLE == attrType && objectClasses.contains(ConnIdObjectClassTO.GROUP)); + this.mappingContainer.setEnabled(enabled); + this.mappingContainer.setVisible(enabled); + this.accountLinkContainer.setEnabled(enabled); + this.accountLinkContainer.setVisible(enabled); + + if (!enabled) { + getMapping().getItems().clear(); + getMapping().setAccountLink(null); + if (this.accountLinkCheckbox != null) { + this.accountLinkCheckbox.setModelObject(null); + } + } + } + + @Override + public void onEvent(final IEvent<?> event) { + if (event.getPayload() instanceof ConnConfModEvent) { + final AjaxRequestTarget target = ((ConnConfModEvent) event.getPayload()).getTarget(); + + final List<ConnConfProperty> conf = ((ConnConfModEvent) event.getPayload()).getConfiguration(); + + mappings.removeAll(); + + addMappingBtn.setEnabled(resourceTO.getConnectorId() != null && resourceTO.getConnectorId() > 0); + + schemaNames.clear(); + schemaNames.addAll(getSchemaNames(resourceTO.getConnectorId(), new HashSet<ConnConfProperty>(conf))); + + setEnabled(); + + target.add(this); + } + } + + /** + * Set attribute names for a drop down choice list. + * + * @param type attribute type. + * @param toBeUpdated drop down choice to be updated. + */ + private void setAttrNames(final IntMappingType type, final AjaxDropDownChoicePanel<String> toBeUpdated) { + toBeUpdated.setRequired(true); + toBeUpdated.setEnabled(true); + + if (type == null || type.getAttributableType() == null) { + toBeUpdated.setChoices(Collections.<String>emptyList()); + } else { + switch (type) { + // user attribute names + case UserPlainSchema: + case RolePlainSchema: + case MembershipPlainSchema: + toBeUpdated.setChoices(schemaRestClient.getPlainSchemaNames(type.getAttributableType())); + break; + + case UserDerivedSchema: + case RoleDerivedSchema: + case MembershipDerivedSchema: + toBeUpdated.setChoices(schemaRestClient.getDerSchemaNames(type.getAttributableType())); + break; + + case UserVirtualSchema: + case RoleVirtualSchema: + case MembershipVirtualSchema: + toBeUpdated.setChoices(schemaRestClient.getVirSchemaNames(type.getAttributableType())); + break; + + case UserId: + case Password: + case Username: + case RoleId: + case RoleName: + default: + toBeUpdated.setRequired(false); + toBeUpdated.setEnabled(false); + toBeUpdated.setChoices(Collections.<String>emptyList()); + } + } + } + + /** + * Enable/Disable accountId checkbox. + * + * @param type attribute type. + * @param accountId accountId checkbox. + * @param password password checkbox. + */ + private void setAccountId(final IntMappingType type, final AjaxCheckBoxPanel accountId, + final AjaxCheckBoxPanel password) { + + if (type != null && type.getAttributableType() != null) { + switch (type) { + case UserVirtualSchema: + case RoleVirtualSchema: + case MembershipVirtualSchema: + // Virtual accountId is not permitted + case Password: + // AccountId cannot be derived from password. + accountId.setReadOnly(true); + accountId.setModelObject(false); + break; + + default: + if (password.getModelObject()) { + accountId.setReadOnly(true); + accountId.setModelObject(false); + } else { + accountId.setReadOnly(false); + } + } + } + } + + /** + * Get all attribute types from a selected attribute type. + * + * @param entity entity. + * @return all attribute types. + */ + private List<IntMappingType> getAttributeTypes(final AttributableType entity) { + final List<IntMappingType> res = new ArrayList<IntMappingType>(); + + if (entity != null) { + res.addAll(IntMappingType.getAttributeTypes(AttributableType.valueOf(entity.name()))); + } + + return res; + } +}
