diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/ajax/form/IndicatorAjaxFormComponentUpdatingBehavior.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/ajax/form/IndicatorAjaxFormComponentUpdatingBehavior.java index 93b58565d6..b38e1ea7f6 100644 --- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/ajax/form/IndicatorAjaxFormComponentUpdatingBehavior.java +++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/ajax/form/IndicatorAjaxFormComponentUpdatingBehavior.java @@ -23,7 +23,7 @@ import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; /** - * An {@link AjaxFormComponentUpdatingBehavior} not showin veil. + * An {@link AjaxFormComponentUpdatingBehavior} not showing veil. */ public abstract class IndicatorAjaxFormComponentUpdatingBehavior extends AjaxFormComponentUpdatingBehavior implements IAjaxIndicatorAware { diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPasswordFieldPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPasswordFieldPanel.java index 6253f5f50a..da0a64337e 100644 --- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPasswordFieldPanel.java +++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPasswordFieldPanel.java @@ -66,6 +66,11 @@ protected void onUpdate(final AjaxRequestTarget target) { } } + public AjaxPasswordFieldPanel setResetPassword(final boolean resetPassword) { + ((PasswordTextField) field).setResetPassword(resetPassword); + return this; + } + @Override public FieldPanel addRequiredLabel() { if (!isRequired()) { diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java index 2cfc3cb4fc..efc0e32371 100644 --- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java +++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java @@ -19,19 +19,24 @@ package org.apache.syncope.client.ui.commons.panels; import java.text.ParseException; +import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.time.FastDateFormat; import org.apache.syncope.client.ui.commons.MapChoiceRenderer; +import org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel; +import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateTimeFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel; +import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; -import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel; import org.apache.syncope.common.lib.form.FormProperty; import org.apache.syncope.common.lib.form.FormPropertyValue; import org.apache.syncope.common.lib.form.SyncopeForm; @@ -39,8 +44,9 @@ 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.PropertyModel; +import org.apache.wicket.model.util.ListModel; +import org.apache.wicket.validation.validator.PatternValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,47 +59,36 @@ public class SyncopeFormPanel extends Panel { public SyncopeFormPanel(final String id, final F form) { super(id); - IModel> formProps = new LoadableDetachableModel<>() { + ListModel model = new ListModel<>(new ArrayList<>()); + model.getObject().addAll(form.getProperties()); - private static final long serialVersionUID = 3169142472626817508L; - - @Override - protected List load() { - return form.getProperties(); - } - }; - - ListView propView = new ListView<>("propView", formProps) { + ListView propView = new ListView<>("propView", model) { private static final long serialVersionUID = 9101744072914090143L; @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) protected void populateItem(final ListItem item) { FormProperty prop = item.getModelObject(); String label = StringUtils.isBlank(prop.getName()) ? prop.getId() : prop.getName(); - FieldPanel field; + AbstractFieldPanel field; switch (prop.getType()) { case Boolean: - field = new AjaxDropDownChoicePanel("value", label, new PropertyModel(prop, "value") { + field = new AjaxCheckBoxPanel("value", label, new PropertyModel(prop, "value") { private static final long serialVersionUID = -3743432456095828573L; @Override - public String getObject() { - return StringUtils.isBlank(prop.getValue()) - ? null - : prop.getValue().equals("true") ? "Yes" : "No"; + public Boolean getObject() { + return BooleanUtils.toBoolean(prop.getValue()); } @Override - public void setObject(final String object) { - prop.setValue(String.valueOf(object.equalsIgnoreCase("yes"))); + public void setObject(final Boolean object) { + prop.setValue(BooleanUtils.toStringTrueFalse(object)); } - - }, false).setChoices(List.of("Yes", "No")); + }, false); break; case Date: @@ -122,25 +117,56 @@ public void setObject(final Date object) { break; case Enum: - field = new AjaxDropDownChoicePanel( + field = new AjaxDropDownChoicePanel<>( "value", label, new PropertyModel(prop, "value"), false). setChoiceRenderer(new MapChoiceRenderer(prop.getEnumValues().stream(). collect(Collectors.toMap( FormPropertyValue::getKey, FormPropertyValue::getValue)))). - setChoices(prop.getEnumValues().stream(). - map(FormPropertyValue::getKey).collect(Collectors.toList())); + setChoices(prop.getEnumValues().stream().map(FormPropertyValue::getKey).toList()); break; case Dropdown: - field = new AjaxDropDownChoicePanel( - "value", label, new PropertyModel(prop, "value"), false). - setChoiceRenderer(new MapChoiceRenderer(prop.getDropdownValues().stream(). - collect(Collectors.toMap( - FormPropertyValue::getKey, - FormPropertyValue::getValue)))). - setChoices(prop.getDropdownValues().stream(). - map(FormPropertyValue::getKey).collect(Collectors.toList())); + if (prop.isDropdownFreeForm()) { + field = new AjaxTextFieldPanel("value", label, new PropertyModel<>(prop, "value"), false); + ((AjaxTextFieldPanel) field).setChoices(prop.getDropdownValues().stream(). + map(FormPropertyValue::getKey).toList()); + } else if (prop.isDropdownSingleSelection()) { + field = new AjaxDropDownChoicePanel<>( + "value", label, new PropertyModel(prop, "value"), false). + setChoiceRenderer(new MapChoiceRenderer(prop.getDropdownValues().stream(). + collect(Collectors.toMap( + FormPropertyValue::getKey, + FormPropertyValue::getValue)))). + setChoices(prop.getDropdownValues().stream(). + map(FormPropertyValue::getKey).toList()); + } else { + field = new AjaxPalettePanel.Builder().setName(label). + setRenderer(new MapChoiceRenderer(prop.getDropdownValues().stream(). + collect(Collectors.toMap( + FormPropertyValue::getKey, + FormPropertyValue::getValue)))).build( + "value", + new IModel>() { + + private static final long serialVersionUID = 1015030402166681242L; + + @Override + public List getObject() { + return Optional.ofNullable(prop.getValue()). + map(v -> List.of(v.split(";"))). + orElse(null); + } + + @Override + public void setObject(final List object) { + prop.setValue(Optional.ofNullable(object). + map(v -> v.stream().collect(Collectors.joining(";"))). + orElse(null)); + } + }, new ListModel<>(prop.getDropdownValues().stream(). + map(FormPropertyValue::getKey).toList())); + } break; case Long: @@ -167,12 +193,15 @@ public void setObject(final Long object) { break; case Password: - field = new AjaxPasswordFieldPanel("value", label, new PropertyModel<>(prop, "value"), false); + field = new AjaxPasswordFieldPanel("value", label, new PropertyModel<>(prop, "value"), false). + setResetPassword(false); break; case String: default: field = new AjaxTextFieldPanel("value", label, new PropertyModel<>(prop, "value"), false); + Optional.ofNullable(prop.getStringRegEx()). + ifPresent(re -> ((AjaxTextFieldPanel) field).addValidator(new PatternValidator(re))); break; } @@ -184,7 +213,6 @@ public void setObject(final Long object) { item.add(field); } }; - - add(propView); + add(propView.setReuseItems(true)); } } diff --git a/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.html b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.html index 127037bc48..6271a58363 100644 --- a/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.html +++ b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.html @@ -22,6 +22,6 @@ [value] - + diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java index 153a4d4118..543089041b 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java @@ -162,7 +162,7 @@ public void onException(final Exception e) { message = getApplication().getResourceSettings().getLocalizer(). getString(message, null, null, null, null, message); - error(message); + error(message.replace("\n", "
")); } public MediaType getMediaType() { diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java index 47ebc8bea4..8f52b00750 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java @@ -257,8 +257,7 @@ protected void onClickDelete(final AjaxRequestTarget target, final RealmTO realm target.add(content); } catch (Exception e) { LOG.error("While deleting realm", e); - // Escape line breaks - SyncopeConsoleSession.get().error(e.getMessage().replace("\n", " ")); + SyncopeConsoleSession.get().onException(e); } ((BaseWebPage) Realms.this.getPage()).getNotificationPanel().refresh(target); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java index 3d6210f97f..8b59be9fb3 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java @@ -21,6 +21,9 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.panels.AbstractModalPanel; @@ -49,6 +52,9 @@ import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.util.ListModel; import org.apache.wicket.spring.injection.annot.SpringBean; +import org.apache.wicket.validation.IValidatable; +import org.apache.wicket.validation.IValidator; +import org.apache.wicket.validation.ValidationError; public class FormPropertyDefsPanel extends AbstractModalPanel { @@ -70,8 +76,7 @@ public FormPropertyDefsPanel( this.task = task; WebMarkupContainer propertyDefContainer = new WebMarkupContainer("propertyDefContainer"); - propertyDefContainer.setOutputMarkupId(true); - add(propertyDefContainer); + add(propertyDefContainer.setOutputMarkupId(true)); model = new ListModel<>(new ArrayList<>()); model.getObject().addAll(task.getFormPropertyDefs()); @@ -127,20 +132,69 @@ protected void populateItem(final ListItem item) { type.setChoices(List.of(FormPropertyType.values())).setNullValid(false); item.add(type.setRequired(true).hideLabel()); + AjaxTextFieldPanel stringRegEx = new AjaxTextFieldPanel( + "stringRegEx", + "stringRegEx", + new IModel() { + + private static final long serialVersionUID = 1015030402166681242L; + + @Override + public String getObject() { + return Optional.ofNullable(fpd.getStringRegEx()).map(Pattern::pattern).orElse(null); + } + + @Override + public void setObject(final String object) { + fpd.setStringRegEx(Optional.ofNullable(object).map(Pattern::compile).orElse(null)); + } + }, true); + stringRegEx.getField().add(new IValidator() { + + private static final long serialVersionUID = 3978328825079032964L; + + @Override + public void validate(final IValidatable validatable) { + try { + Pattern.compile(validatable.getValue()); + } catch (PatternSyntaxException e) { + validatable.error(new ValidationError(fpd.getKey() + ": invalid RegEx")); + } + } + }); + stringRegEx.setVisible(fpd.getType() == FormPropertyType.String); + item.add(stringRegEx.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true)); + AjaxTextFieldPanel datePattern = new AjaxTextFieldPanel( "datePattern", "datePattern", new PropertyModel<>(fpd, "datePattern"), true); - datePattern.setEnabled(fpd.getType() == FormPropertyType.Date); - item.add(datePattern.hideLabel().setOutputMarkupId(true)); + datePattern.setVisible(fpd.getType() == FormPropertyType.Date); + item.add(datePattern.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true)); AjaxGridFieldPanel enumValues = new AjaxGridFieldPanel<>( "enumValues", "enumValues", new PropertyModel<>(fpd, "enumValues")); - enumValues.setEnabled(fpd.getType() == FormPropertyType.Enum); - item.add(enumValues.hideLabel().setOutputMarkupId(true)); + enumValues.setVisible(fpd.getType() == FormPropertyType.Enum); + item.add(enumValues.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true)); + + WebMarkupContainer dropdownConf = new WebMarkupContainer("dropdownConf"); + dropdownConf.setVisible(fpd.getType() == FormPropertyType.Dropdown); + item.add(dropdownConf.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true)); + AjaxCheckBoxPanel dropdownSingleSelection = new AjaxCheckBoxPanel( + "dropdownSingleSelection", + "dropdownSingleSelection", + new PropertyModel<>(fpd, "dropdownSingleSelection"), + true); + dropdownConf.add(dropdownSingleSelection.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true)); + AjaxCheckBoxPanel dropdownFreeForm = new AjaxCheckBoxPanel( + "dropdownFreeForm", + "dropdownFreeForm", + new PropertyModel<>(fpd, "dropdownFreeForm"), + true); + dropdownConf.add(dropdownFreeForm.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true)); type.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { @@ -149,30 +203,59 @@ protected void populateItem(final ListItem item) { @Override protected void onUpdate(final AjaxRequestTarget target) { switch (type.getModelObject()) { + case String -> { + stringRegEx.setVisible(true); + datePattern.setVisible(false); + enumValues.setVisible(false); + fpd.getEnumValues().clear(); + dropdownConf.setVisible(false); + } + case Date -> { - datePattern.setEnabled(true); - enumValues.setEnabled(false); + stringRegEx.setVisible(false); + fpd.setStringRegEx(null); + datePattern.setVisible(true); + enumValues.setVisible(false); fpd.getEnumValues().clear(); + dropdownConf.setVisible(false); } case Enum -> { - datePattern.setEnabled(false); - enumValues.setEnabled(true); + stringRegEx.setVisible(false); + fpd.setStringRegEx(null); + datePattern.setVisible(false); + enumValues.setVisible(true); + dropdownConf.setVisible(false); + } + + case Dropdown -> { + stringRegEx.setVisible(false); + fpd.setStringRegEx(null); + datePattern.setVisible(false); + enumValues.setVisible(false); + fpd.getEnumValues().clear(); + dropdownConf.setVisible(true); } default -> { - datePattern.setEnabled(false); - enumValues.setEnabled(false); + stringRegEx.setVisible(false); + fpd.setStringRegEx(null); + datePattern.setVisible(false); + enumValues.setVisible(false); fpd.getEnumValues().clear(); + dropdownConf.setVisible(false); } } + target.add(stringRegEx); target.add(datePattern); target.add(enumValues); + target.add(dropdownConf); } }); - ActionsPanel actions = new ActionsPanel<>("toRemove", null); + ActionsPanel actions = new ActionsPanel<>("actions", null); + item.add(actions); actions.add(new ActionLink<>() { private static final long serialVersionUID = -3722207913631435501L; @@ -180,15 +263,48 @@ protected void onUpdate(final AjaxRequestTarget target) { @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { model.getObject().remove(item.getIndex()); + item.getParent().removeAll(); target.add(propertyDefContainer); } }, ActionLink.ActionType.DELETE, StringUtils.EMPTY, true).hideLabel(); - item.add(actions); + if (model.getObject().size() > 1) { + if (item.getIndex() > 0) { + actions.add(new ActionLink<>() { + + private static final long serialVersionUID = 2041211756396714619L; + + @Override + public void onClick(final AjaxRequestTarget target, final Serializable ignore) { + FormPropertyDefTO pre = model.getObject().get(item.getIndex() - 1); + model.getObject().set(item.getIndex(), pre); + model.getObject().set(item.getIndex() - 1, fpd); + + item.getParent().removeAll(); + target.add(propertyDefContainer); + } + }, ActionLink.ActionType.UP, StringUtils.EMPTY).hideLabel(); + } + if (item.getIndex() < model.getObject().size() - 1) { + actions.add(new ActionLink<>() { + + private static final long serialVersionUID = 2041211756396714619L; + + @Override + public void onClick(final AjaxRequestTarget target, final Serializable ignore) { + FormPropertyDefTO post = model.getObject().get(item.getIndex() + 1); + model.getObject().set(item.getIndex(), post); + model.getObject().set(item.getIndex() + 1, fpd); + + item.getParent().removeAll(); + target.add(propertyDefContainer); + } + }, ActionLink.ActionType.DOWN, StringUtils.EMPTY).hideLabel(); + } + } } }; - propertyDefs.setReuseItems(true); - propertyDefContainer.add(propertyDefs); + propertyDefContainer.add(propertyDefs.setReuseItems(true)); IndicatingAjaxButton addPropertyDef = new IndicatingAjaxButton("addPropertyDef") { diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.java index 345fda7a3b..31cd627472 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.java @@ -138,6 +138,7 @@ public ActionsPanel getActions(final IModel model) { @Override public void onClick(final AjaxRequestTarget target, final MacroTaskTO ignore) { + model.setObject(restClient.readTask(TaskType.MACRO, model.getObject().getKey())); MacroTaskExecWizardBuilder wb = new MacroTaskExecWizardBuilder(model.getObject(), restClient, pageRef); wb.setEventSink(new ExecModalEventSink()); @@ -164,6 +165,7 @@ protected void addFurtherActions(final ActionsPanel panel, final IM @Override public void onClick(final AjaxRequestTarget target, final MacroTaskTO ignore) { + model.setObject(restClient.readTask(TaskType.MACRO, model.getObject().getKey())); target.add(modal.setContent(new CommandComposeDirectoryPanel( model.getObject().getKey(), commandRestClient, modal, pageRef))); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConsoleAuxClasses.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConsoleAuxClasses.java index 0fb1f700a0..bdb8a6168f 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConsoleAuxClasses.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConsoleAuxClasses.java @@ -38,7 +38,7 @@ public ConsoleAuxClasses(final AnyWrapper modelObject, fina } @Override - protected final List listAnyTypecClasses() { + protected List listAnyTypecClasses() { return anyTypeClassRestClient.list(); } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java index 735fb6d73d..096e813744 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java @@ -45,9 +45,17 @@ public class Details extends WizardStep { + private static final long serialVersionUID = -8995647450549098844L; + protected static final Logger LOG = LoggerFactory.getLogger(Details.class); - private static final long serialVersionUID = -8995647450549098844L; + protected static List getRealmsFromLinks(final List realmLinks) { + return realmLinks.stream(). + map(Component::getDefaultModelObject). + filter(RealmTO.class::isInstance). + map(RealmTO.class::cast). + toList(); + } @SpringBean protected RealmRestClient realmRestClient; @@ -113,12 +121,4 @@ public Details disableRealmSpecification() { protected AnnotatedBeanPanel getGeneralStatusInformation(final String id, final T anyTO) { return new AnnotatedBeanPanel(id, anyTO); } - - private static List getRealmsFromLinks(final List realmLinks) { - return realmLinks.stream(). - map(Component::getDefaultModelObject). - filter(RealmTO.class::isInstance). - map(RealmTO.class::cast). - collect(Collectors.toList()); - } } diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy index c920b030a7..354fa91ea2 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy @@ -18,9 +18,10 @@ */ import groovy.transform.CompileStatic import java.util.Map -import javax.xml.bind.ValidationException +import java.util.Optional +import javax.validation.ValidationException import org.apache.syncope.common.lib.command.CommandArgs -import org.apache.syncope.common.lib.form.MacroTaskForm +import org.apache.syncope.common.lib.form.SyncopeForm import org.apache.syncope.core.provisioning.api.macro.Command import org.apache.syncope.core.provisioning.api.macro.MacroActions @@ -28,14 +29,19 @@ import org.apache.syncope.core.provisioning.api.macro.MacroActions class MyMacroActions implements MacroActions { @Override - void validate(MacroTaskForm macroTaskForm) throws ValidationException { + Optional getDefaultValue(String formProperty) { + return Optional.empty(); } - + @Override Map getDropdownValues(String formProperty) { - return Map.of(); + return Map.of() } + @Override + void validate(SyncopeForm form, Map vars) throws ValidationException { + } + @Override void beforeAll() { } @@ -50,6 +56,6 @@ class MyMacroActions implements MacroActions { @Override StringBuilder afterAll(StringBuilder output) { - return output; + return output } } diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html index f40c2b55d7..933b16e786 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html @@ -27,12 +27,11 @@ - - - + + @@ -43,9 +42,6 @@ - - - @@ -56,14 +52,20 @@ - + + + +
+ + +
- +
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties index 8ba3a3a829..5831fc80ca 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties @@ -18,5 +18,9 @@ type=Type readable=Readable writable=Writable required=Required -datePattern=Date pattern -enumValues=Enum values +conf=Configuration +stringRegEx=Regular Expression +enumValues=Values +datePattern=Pattern +dropdownSingleSelection=Single Selection +dropdownFreeForm=Free Form diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties index 8ba3a3a829..5831fc80ca 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties @@ -18,5 +18,9 @@ type=Type readable=Readable writable=Writable required=Required -datePattern=Date pattern -enumValues=Enum values +conf=Configuration +stringRegEx=Regular Expression +enumValues=Values +datePattern=Pattern +dropdownSingleSelection=Single Selection +dropdownFreeForm=Free Form diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties index 62a3a2d422..5cd41d2b8b 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties @@ -18,5 +18,9 @@ type=Tipo readable=Lettura writable=Scrittura required=Obbligatorio -datePattern=Modello data -enumValues=Valori enum +conf=Configurazione +stringRegEx=Espressione Regolare +enumValues=Valori +datePattern=Modello +dropdownSingleSelection=Selezione Singola +dropdownFreeForm=Modalit\u00e0 libera diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties index 8ba3a3a829..5831fc80ca 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties @@ -18,5 +18,9 @@ type=Type readable=Readable writable=Writable required=Required -datePattern=Date pattern -enumValues=Enum values +conf=Configuration +stringRegEx=Regular Expression +enumValues=Values +datePattern=Pattern +dropdownSingleSelection=Single Selection +dropdownFreeForm=Free Form diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties index 8ba3a3a829..5831fc80ca 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties @@ -18,5 +18,9 @@ type=Type readable=Readable writable=Writable required=Required -datePattern=Date pattern -enumValues=Enum values +conf=Configuration +stringRegEx=Regular Expression +enumValues=Values +datePattern=Pattern +dropdownSingleSelection=Single Selection +dropdownFreeForm=Free Form diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties index 8ba3a3a829..5831fc80ca 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties @@ -18,5 +18,9 @@ type=Type readable=Readable writable=Writable required=Required -datePattern=Date pattern -enumValues=Enum values +conf=Configuration +stringRegEx=Regular Expression +enumValues=Values +datePattern=Pattern +dropdownSingleSelection=Single Selection +dropdownFreeForm=Free Form diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties index 0e53e9e9f2..b8d5d685ee 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties @@ -276,3 +276,9 @@ merge_accounts.alt=merge accounts icon explore_resource.class=fa fa-eye explore_resource.title=explore resource explore_resource.alt=explore resource icon +up.alt=up icon +up.class=fas fa-arrow-up +up.title=move up +down.alt=down icon +down.class=fas fa-arrow-down +down.title=move down diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties index 48d13be1b2..b42f43a855 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties @@ -221,3 +221,9 @@ merge_accounts.alt=merge accounts icon explore_resource.class=fa fa-eye explore_resource.title=explorer la ressource explore_resource.alt=ic\u00f4ne explorer la ressource +up.alt=up icon +up.class=fas fa-arrow-up +up.title=move up +down.alt=down icon +down.class=fas fa-arrow-down +down.title=move down diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties index dbbab266f3..e5d20e0446 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties @@ -276,3 +276,9 @@ merge_accounts.alt=merge accounts icon explore_resource.class=fa fa-eye explore_resource.title=esplora risorsa explore_resource.alt=explore resource icon +up.alt=up icon +up.class=fas fa-arrow-up +up.title=sposta su +down.alt=down icon +down.class=fas fa-arrow-down +down.title=sposta gi\u00f9 diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties index 12f5a73617..1a97e92fb5 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties @@ -277,3 +277,9 @@ merge_accounts.alt=merge accounts icon explore_resource.class=fa fa-eye explore_resource.title=\u30ea\u30bd\u30fc\u30b9\u3092\u63a2\u7d22\u3059\u308b explore_resource.alt=\u30ea\u30bd\u30fc\u30b9\u30a2\u30a4\u30b3\u30f3\u3092\u63a2\u3059 +up.alt=up icon +up.class=fas fa-arrow-up +up.title=move up +down.alt=down icon +down.class=fas fa-arrow-down +down.title=move down diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties index 368df37cc3..dbb9c181a4 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties @@ -281,3 +281,9 @@ merge_accounts.alt=merge accounts icon explore_resource.class=fa fa-eye explore_resource.title=explorar recurso explore_resource.alt=\u00edcone de explorar recurso +up.alt=up icon +up.class=fas fa-arrow-up +up.title=move up +down.alt=down icon +down.class=fas fa-arrow-down +down.title=move down diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties index 822849583a..7277a38550 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties @@ -277,3 +277,9 @@ merge_accounts.alt=merge accounts icon explore_resource.class=fa fa-eye explore_resource.title=\u0438\u0437\u0443\u0447\u0438\u0442\u044c\u0020\u0440\u0435\u0441\u0443\u0440\u0441 explore_resource.alt=\u0438\u0437\u0443\u0447\u0438\u0442\u044c\u0020\u0437\u043d\u0430\u0447\u043e\u043a\u0020\u0440\u0435\u0441\u0443\u0440\u0441\u0430 +up.alt=up icon +up.class=fas fa-arrow-up +up.title=move up +down.alt=down icon +down.class=fas fa-arrow-down +down.title=move down diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java index 471f072870..dbf8745ef9 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java @@ -23,6 +23,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -42,12 +43,18 @@ public class FormProperty implements Serializable { private boolean required; + private Pattern stringRegEx; + private String datePattern; private final List enumValues = new ArrayList<>(); private final List dropdownValues = new ArrayList<>(); + private boolean dropdownSingleSelection = true; + + private boolean dropdownFreeForm; + private String value; public String getId() { @@ -82,6 +89,14 @@ public void setRequired(final boolean required) { this.required = required; } + public Pattern getStringRegEx() { + return stringRegEx; + } + + public void setStringRegEx(final Pattern stringRegEx) { + this.stringRegEx = stringRegEx; + } + public FormPropertyType getType() { return type; } @@ -118,6 +133,22 @@ public List getDropdownValues() { return dropdownValues; } + public boolean isDropdownSingleSelection() { + return dropdownSingleSelection; + } + + public void setDropdownSingleSelection(final boolean dropdownSingleSelection) { + this.dropdownSingleSelection = dropdownSingleSelection; + } + + public boolean isDropdownFreeForm() { + return dropdownFreeForm; + } + + public void setDropdownFreeForm(final boolean dropdownFreeForm) { + this.dropdownFreeForm = dropdownFreeForm; + } + public String getValue() { return value; } @@ -135,9 +166,12 @@ public int hashCode() { append(readable). append(writable). append(required). + append(stringRegEx). append(datePattern). append(enumValues). append(dropdownValues). + append(dropdownSingleSelection). + append(dropdownFreeForm). append(value). build(); } @@ -161,9 +195,12 @@ public boolean equals(final Object obj) { append(readable, other.readable). append(writable, other.writable). append(required, other.required). + append(stringRegEx, other.stringRegEx). append(datePattern, other.datePattern). append(enumValues, other.enumValues). append(dropdownValues, other.dropdownValues). + append(dropdownSingleSelection, other.dropdownSingleSelection). + append(dropdownFreeForm, other.dropdownFreeForm). append(value, other.value). build(); } diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java index 5357b1df39..4f1d24aba5 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java @@ -20,6 +20,7 @@ import java.util.LinkedHashMap; import java.util.Map; +import java.util.regex.Pattern; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.syncope.common.lib.form.FormPropertyType; @@ -40,10 +41,16 @@ public class FormPropertyDefTO implements NamedEntityTO { private boolean required; + private Pattern stringRegEx; + private String datePattern; private final Map enumValues = new LinkedHashMap<>(); + private boolean dropdownSingleSelection = true; + + private boolean dropdownFreeForm; + @Override public String getKey() { return key; @@ -96,6 +103,14 @@ public void setRequired(final boolean required) { this.required = required; } + public Pattern getStringRegEx() { + return stringRegEx; + } + + public void setStringRegEx(final Pattern stringRegEx) { + this.stringRegEx = stringRegEx; + } + public String getDatePattern() { return datePattern; } @@ -108,6 +123,22 @@ public Map getEnumValues() { return enumValues; } + public boolean isDropdownSingleSelection() { + return dropdownSingleSelection; + } + + public void setDropdownSingleSelection(final boolean dropdownSingleSelection) { + this.dropdownSingleSelection = dropdownSingleSelection; + } + + public boolean isDropdownFreeForm() { + return dropdownFreeForm; + } + + public void setDropdownFreeForm(final boolean dropdownFreeForm) { + this.dropdownFreeForm = dropdownFreeForm; + } + @Override public int hashCode() { return new HashCodeBuilder(). @@ -117,8 +148,11 @@ public int hashCode() { append(readable). append(writable). append(required). + append(stringRegEx). append(datePattern). append(enumValues). + append(dropdownSingleSelection). + append(dropdownFreeForm). build(); } @@ -141,8 +175,11 @@ public boolean equals(final Object obj) { append(readable, other.readable). append(writable, other.writable). append(required, other.required). + append(stringRegEx, other.stringRegEx). append(datePattern, other.datePattern). append(enumValues, other.enumValues). + append(dropdownSingleSelection, other.dropdownSingleSelection). + append(dropdownFreeForm, other.dropdownFreeForm). build(); } } diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java index 3030630e14..99c57b1a19 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java @@ -420,7 +420,8 @@ public T delete(final TaskType type, final String key) { if (TaskType.SCHEDULED == taskUtils.getType() || TaskType.PULL == taskUtils.getType() - || TaskType.PUSH == taskUtils.getType()) { + || TaskType.PUSH == taskUtils.getType() + || TaskType.MACRO == taskUtils.getType()) { jobManager.unregister(task); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java index dbfcadd620..518ab750d7 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java @@ -19,6 +19,7 @@ package org.apache.syncope.core.persistence.api.entity.task; import java.util.Map; +import java.util.regex.Pattern; import org.apache.syncope.common.lib.form.FormPropertyType; import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity; @@ -48,6 +49,10 @@ public interface FormPropertyDef extends ProvidedKeyEntity { void setRequired(boolean required); + Pattern getStringRegEx(); + + void setStringRegExp(Pattern stringRegEx); + String getDatePattern(); void setDatePattern(String datePattern); @@ -55,4 +60,12 @@ public interface FormPropertyDef extends ProvidedKeyEntity { Map getEnumValues(); void setEnumValues(Map enumValues); + + boolean isDropdownSingleSelection(); + + void setDropdownSingleSelection(boolean dropdownSingleSelection); + + boolean isDropdownFreeForm(); + + void setDropdownFreeForm(boolean dropdownFreeForm); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java index 0d86db3fa7..f877ddacee 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java @@ -28,6 +28,7 @@ import jakarta.validation.constraints.NotNull; import java.util.Map; import java.util.Optional; +import java.util.regex.Pattern; import org.apache.syncope.common.lib.form.FormPropertyType; import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef; import org.apache.syncope.core.persistence.api.entity.task.MacroTask; @@ -44,6 +45,11 @@ public class JPAFormPropertyDef extends AbstractProvidedKeyEntity implements For public static final String TABLE = "FormPropertyDef"; + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + private int idx; + @ManyToOne(optional = false) private JPAMacroTask macroTask; @@ -63,11 +69,23 @@ public class JPAFormPropertyDef extends AbstractProvidedKeyEntity implements For @NotNull private Boolean required = Boolean.FALSE; + private String stringRegEx; + private String datePattern; @Lob private String enumValues; + @NotNull + private Boolean dropdownSingleSelection = Boolean.TRUE; + + @NotNull + private Boolean dropdownFreeForm = Boolean.FALSE; + + public void setIdx(final int idx) { + this.idx = idx; + } + @Override public JPAMacroTask getMacroTask() { return macroTask; @@ -129,6 +147,16 @@ public void setRequired(final boolean required) { this.required = required; } + @Override + public Pattern getStringRegEx() { + return Optional.ofNullable(stringRegEx).map(Pattern::compile).orElse(null); + } + + @Override + public void setStringRegExp(final Pattern stringRegEx) { + this.stringRegEx = Optional.ofNullable(stringRegEx).map(Pattern::pattern).orElse(null); + } + @Override public String getDatePattern() { return datePattern; @@ -141,13 +169,31 @@ public void setDatePattern(final String datePattern) { @Override public Map getEnumValues() { - return Optional.ofNullable(enumValues). - map(v -> POJOHelper.deserialize(v, new TypeReference>() { - })).orElse(Map.of()); + return Optional.ofNullable(enumValues).map(v -> POJOHelper.deserialize(v, TYPEREF)).orElse(Map.of()); } @Override public void setEnumValues(final Map enumValues) { this.enumValues = Optional.ofNullable(enumValues).map(POJOHelper::serialize).orElse(null); } + + @Override + public boolean isDropdownSingleSelection() { + return dropdownSingleSelection == null ? false : dropdownSingleSelection; + } + + @Override + public void setDropdownSingleSelection(final boolean dropdownSingleSelection) { + this.dropdownSingleSelection = dropdownSingleSelection; + } + + @Override + public boolean isDropdownFreeForm() { + return dropdownFreeForm == null ? false : dropdownFreeForm; + } + + @Override + public void setDropdownFreeForm(final boolean dropdownFreeForm) { + this.dropdownFreeForm = dropdownFreeForm; + } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java index 8660cd8e3a..64725466b5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java @@ -23,6 +23,7 @@ import jakarta.persistence.FetchType; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; import jakarta.persistence.Table; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -57,9 +58,11 @@ public class JPAMacroTask extends JPASchedTask implements MacroTask { private Boolean saveExecs = true; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "macroTask") + @OrderBy("idx") private List macroTaskCommands = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "macroTask") + @OrderBy("idx") @Valid private List formPropertyDefs = new ArrayList<>(); @@ -114,7 +117,8 @@ protected List> executions() { @Override public void add(final MacroTaskCommand macroTaskCommand) { checkType(macroTaskCommand, JPAMacroTaskCommand.class); - this.macroTaskCommands.add((JPAMacroTaskCommand) macroTaskCommand); + ((JPAMacroTaskCommand) macroTaskCommand).setIdx(macroTaskCommands.size()); + macroTaskCommands.add((JPAMacroTaskCommand) macroTaskCommand); } @Override @@ -125,7 +129,8 @@ public List getCommands() { @Override public void add(final FormPropertyDef formPropertyDef) { checkType(formPropertyDef, JPAFormPropertyDef.class); - this.formPropertyDefs.add((JPAFormPropertyDef) formPropertyDef); + ((JPAFormPropertyDef) formPropertyDef).setIdx(formPropertyDefs.size()); + formPropertyDefs.add((JPAFormPropertyDef) formPropertyDef); } @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java index df3bea0465..86ca5037c1 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java @@ -41,6 +41,8 @@ public class JPAMacroTaskCommand extends AbstractGeneratedKeyEntity implements M public static final String TABLE = "MacroTaskCommand"; + private int idx; + @ManyToOne(optional = false) private JPAMacroTask macroTask; @@ -50,6 +52,10 @@ public class JPAMacroTaskCommand extends AbstractGeneratedKeyEntity implements M @Lob private String args; + public void setIdx(final int idx) { + this.idx = idx; + } + @Override public JPAMacroTask getMacroTask() { return macroTask; diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java index af494c82e6..ab6f9c9caa 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java @@ -22,6 +22,7 @@ import jakarta.validation.constraints.NotNull; import java.util.Map; import java.util.Optional; +import java.util.regex.Pattern; import org.apache.syncope.common.lib.form.FormPropertyType; import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef; import org.apache.syncope.core.persistence.api.entity.task.MacroTask; @@ -39,6 +40,9 @@ public class Neo4jFormPropertyDef extends AbstractProvidedKeyNode implements For public static final String NODE = "FormPropertyDef"; + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + @NotNull @Relationship(type = Neo4jMacroTask.MACRO_TASK_FORM_PROPERTY_DEF_REL, direction = Relationship.Direction.OUTGOING) private Neo4jMacroTask macroTask; @@ -58,10 +62,18 @@ public class Neo4jFormPropertyDef extends AbstractProvidedKeyNode implements For @NotNull private Boolean required = Boolean.FALSE; + private String stringRegEx; + private String datePattern; private String enumValues; + @NotNull + private Boolean dropdownSingleSelection = Boolean.TRUE; + + @NotNull + private Boolean dropdownFreeForm = Boolean.FALSE; + @Override public Neo4jMacroTask getMacroTask() { return macroTask; @@ -123,6 +135,16 @@ public void setRequired(final boolean required) { this.required = required; } + @Override + public Pattern getStringRegEx() { + return Optional.ofNullable(stringRegEx).map(Pattern::compile).orElse(null); + } + + @Override + public void setStringRegExp(final Pattern stringRegEx) { + this.stringRegEx = Optional.ofNullable(stringRegEx).map(Pattern::pattern).orElse(null); + } + @Override public String getDatePattern() { return datePattern; @@ -135,13 +157,31 @@ public void setDatePattern(final String datePattern) { @Override public Map getEnumValues() { - return Optional.ofNullable(enumValues). - map(v -> POJOHelper.deserialize(v, new TypeReference>() { - })).orElse(Map.of()); + return Optional.ofNullable(enumValues).map(v -> POJOHelper.deserialize(v, TYPEREF)).orElse(Map.of()); } @Override public void setEnumValues(final Map enumValues) { this.enumValues = Optional.ofNullable(enumValues).map(POJOHelper::serialize).orElse(null); } + + @Override + public boolean isDropdownSingleSelection() { + return dropdownSingleSelection == null ? false : dropdownSingleSelection; + } + + @Override + public void setDropdownSingleSelection(final boolean dropdownSingleSelection) { + this.dropdownSingleSelection = dropdownSingleSelection; + } + + @Override + public boolean isDropdownFreeForm() { + return dropdownFreeForm == null ? false : dropdownFreeForm; + } + + @Override + public void setDropdownFreeForm(final boolean dropdownFreeForm) { + this.dropdownFreeForm = dropdownFreeForm; + } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDefRelationship.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDefRelationship.java new file mode 100644 index 0000000000..be1cfc5fb7 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDefRelationship.java @@ -0,0 +1,102 @@ +/* + * 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.core.persistence.neo4j.entity.task; + +import jakarta.validation.Valid; +import java.util.function.BiFunction; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSortedRelationsihip; +import org.springframework.data.neo4j.core.schema.RelationshipId; +import org.springframework.data.neo4j.core.schema.RelationshipProperties; +import org.springframework.data.neo4j.core.schema.TargetNode; + +@RelationshipProperties +public class Neo4jFormPropertyDefRelationship + extends Neo4jSortedRelationsihip + implements Comparable { + + public static BiFunction builder() { + return (Integer i, Neo4jFormPropertyDef e) -> new Neo4jFormPropertyDefRelationship(i, e); + } + + @RelationshipId + private Long id; + + private int index; + + @TargetNode + @Valid + private Neo4jFormPropertyDef formPropertyDef; + + public Neo4jFormPropertyDefRelationship(final int index, final Neo4jFormPropertyDef formPropertyDef) { + this.index = index; + this.formPropertyDef = formPropertyDef; + } + + @Override + public int getIndex() { + return index; + } + + @Override + public Neo4jFormPropertyDef getEntity() { + return formPropertyDef; + } + + @Override + public int compareTo(final Neo4jFormPropertyDefRelationship object) { + return Integer.compare(index, object.getIndex()); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Neo4jFormPropertyDefRelationship other = (Neo4jFormPropertyDefRelationship) obj; + return new EqualsBuilder(). + append(index, other.index). + append(formPropertyDef, other.formPropertyDef). + build(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + append(index). + append(formPropertyDef). + build(); + } + + @Override + public String toString() { + return "Neo4jMacroTaskCommandRelationship{" + + "id=" + id + + ", index=" + index + + ", formPropertyDef=" + formPropertyDef + + '}'; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java index ff830350df..8427d08c85 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java @@ -74,7 +74,11 @@ public class Neo4jMacroTask extends Neo4jSchedTask implements MacroTask { @Relationship(type = MACRO_TASK_FORM_PROPERTY_DEF_REL, direction = Relationship.Direction.INCOMING) @Valid - private List formPropertyDefs = new ArrayList<>(); + private SortedSet formPropertyDefs = new TreeSet<>(); + + @Transient + private List sortedFormPropertyDefs = new SortedSetList<>( + formPropertyDefs, Neo4jFormPropertyDefRelationship.builder()); @Relationship(type = MACRO_TASK_MACRO_ACTIONS_REL, direction = Relationship.Direction.OUTGOING) private Neo4jImplementation macroActions; @@ -142,12 +146,12 @@ public List getCommands() { @Override public void add(final FormPropertyDef formPropertyDef) { checkType(formPropertyDef, Neo4jFormPropertyDef.class); - this.formPropertyDefs.add((Neo4jFormPropertyDef) formPropertyDef); + sortedFormPropertyDefs.add((Neo4jFormPropertyDef) formPropertyDef); } @Override public List getFormPropertyDefs() { - return formPropertyDefs; + return sortedFormPropertyDefs; } @Override @@ -165,5 +169,6 @@ public void setMacroAction(final Implementation macroActions) { @PostLoad public void postLoad() { sortedCommands = new SortedSetList<>(commands, Neo4jMacroTaskCommandRelationship.builder()); + sortedFormPropertyDefs = new SortedSetList<>(formPropertyDefs, Neo4jFormPropertyDefRelationship.builder()); } } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java index a631586731..b0de301083 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java @@ -20,6 +20,7 @@ import jakarta.validation.ValidationException; import java.util.Map; +import java.util.Optional; import org.apache.syncope.common.lib.command.CommandArgs; import org.apache.syncope.common.lib.form.SyncopeForm; @@ -28,14 +29,18 @@ */ public interface MacroActions { - default void validate(SyncopeForm macroTaskForm) throws ValidationException { - // does nothing by default + default Optional getDefaultValue(String formProperty) { + return Optional.empty(); } default Map getDropdownValues(String formProperty) { return Map.of(); } + default void validate(SyncopeForm form, Map vars) throws ValidationException { + // does nothing by default + } + default void beforeAll() { // does nothing by default } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java index b92d98ebc8..53eec0b341 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java @@ -34,6 +34,7 @@ import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.provisioning.api.data.ImplementationDataBinder; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.spring.implementation.ImplementationManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -133,6 +134,15 @@ public void update(final Implementation implementation, final ImplementationTO i } break; } + } else if (implementation.getEngine() == ImplementationEngine.GROOVY) { + try { + ImplementationManager.build(implementation); + } catch (Exception e) { + LOG.error("While building Groovy class {}", implementation.getKey(), e); + + sce.getElements().add(e.getMessage()); + throw sce; + } } } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java index d4eb8492a6..2c19d573aa 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java @@ -275,9 +275,12 @@ protected void fill(final MacroTask macroTask, final MacroTaskTO macroTaskTO) { fpd.setType(fpdTO.getType()); fpd.setReadable(fpdTO.isReadable()); fpd.setWritable(fpdTO.isWritable()); + fpd.setStringRegExp(fpdTO.getStringRegEx()); fpd.setRequired(fpdTO.isRequired()); fpd.setDatePattern(fpdTO.getDatePattern()); fpd.setEnumValues(fpdTO.getEnumValues()); + fpd.setDropdownSingleSelection(fpdTO.isDropdownSingleSelection()); + fpd.setDropdownFreeForm(fpdTO.isDropdownFreeForm()); fpd.setMacroTask(macroTask); macroTask.add(fpd); @@ -500,8 +503,11 @@ public T getTaskTO(final Task task, final TaskUtils taskUt fpdTO.setReadable(fpd.isReadable()); fpdTO.setWritable(fpd.isWritable()); fpdTO.setRequired(fpd.isRequired()); + fpdTO.setStringRegEx(fpd.getStringRegEx()); fpdTO.setDatePattern(fpd.getDatePattern()); fpdTO.getEnumValues().putAll(fpd.getEnumValues()); + fpdTO.setDropdownSingleSelection(fpd.isDropdownSingleSelection()); + fpdTO.setDropdownFreeForm(fpd.isDropdownFreeForm()); macroTaskTO.getFormPropertyDefs().add(fpdTO); }); @@ -608,7 +614,11 @@ public SyncopeForm getMacroTaskForm(final MacroTask task) { prop.setRequired(fpd.isRequired()); prop.setWritable(fpd.isWritable()); prop.setType(fpd.getType()); + actions.flatMap(a -> a.getDefaultValue(fpd.getKey())).ifPresent(v -> prop.setValue(v)); switch (prop.getType()) { + case String -> + prop.setStringRegEx(fpd.getStringRegEx()); + case Date -> prop.setDatePattern(fpd.getDatePattern()); @@ -616,9 +626,12 @@ public SyncopeForm getMacroTaskForm(final MacroTask task) { fpd.getEnumValues(). forEach((key, value) -> prop.getEnumValues().add(new FormPropertyValue(key, value))); - case Dropdown -> + case Dropdown -> { actions.ifPresent(a -> a.getDropdownValues(fpd.getKey()). forEach((key, value) -> prop.getDropdownValues().add(new FormPropertyValue(key, value)))); + prop.setDropdownSingleSelection(fpd.isDropdownSingleSelection()); + prop.setDropdownFreeForm(fpd.isDropdownFreeForm()); + } default -> { } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java index e563026212..69e1a26856 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java @@ -22,7 +22,9 @@ import jakarta.validation.ConstraintViolation; import jakarta.validation.ValidationException; import jakarta.validation.Validator; +import java.time.format.DateTimeParseException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -39,6 +41,7 @@ import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.command.CommandArgs; +import org.apache.syncope.common.lib.form.FormProperty; import org.apache.syncope.common.lib.form.SyncopeForm; import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef; @@ -54,6 +57,7 @@ import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.apache.syncope.core.spring.implementation.ImplementationManager; import org.apache.syncope.core.spring.task.VirtualThreadPoolTaskExecutor; +import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.concurrent.DelegatingSecurityContextCallable; import org.springframework.util.ReflectionUtils; @@ -75,21 +79,6 @@ public class MacroJobDelegate extends AbstractSchedTaskJobDelegate { protected final Map> perContextCommands = new ConcurrentHashMap<>(); - protected boolean validate(final FormPropertyDef fpd, final String value, final Optional actions) { - if (!fpd.isWritable()) { - return false; - } - - return switch (fpd.getType()) { - case Enum -> - fpd.getEnumValues().containsKey(value); - case Dropdown -> - actions.map(a -> a.getDropdownValues(fpd.getKey()).containsKey(value)).orElse(false); - default -> - value != null; - }; - } - protected Optional check( final SyncopeForm macroTaskForm, final Optional actions, @@ -112,47 +101,81 @@ protected Optional check( throw new JobExecutionException("Required form properties missing: " + missingFormProperties); } + // build the JEXL context where variables are mapped to property values, built according to the defined type + Map vars = new HashMap<>(); + for (FormPropertyDef fpd : task.getFormPropertyDefs()) { + String value = macroTaskForm.getProperty(fpd.getKey()).map(FormProperty::getValue).orElse(null); + if (value == null) { + continue; + } + + switch (fpd.getType()) { + case String -> { + if (Optional.ofNullable(fpd.getStringRegEx()). + map(pattern -> !pattern.matcher(value).matches()). + orElse(false)) { + + throw new JobExecutionException("RegEx not matching for " + fpd.getKey() + ": " + value); + } + + vars.put(fpd.getKey(), value); + } + + case Password -> + vars.put(fpd.getKey(), value); + + case Boolean -> + vars.put(fpd.getKey(), BooleanUtils.toBoolean(value)); + + case Date -> { + try { + vars.put(fpd.getKey(), StringUtils.isBlank(fpd.getDatePattern()) + ? FormatUtils.parseDate(value) + : FormatUtils.parseDate(value, fpd.getDatePattern())); + } catch (DateTimeParseException e) { + throw new JobExecutionException("Unparseable date " + fpd.getKey() + ": " + value, e); + } + } + + case Long -> + vars.put(fpd.getKey(), NumberUtils.toLong(value)); + + case Enum -> { + if (!fpd.getEnumValues().containsKey(value)) { + throw new JobExecutionException("Not allowed for " + fpd.getKey() + ": " + value); + } + + vars.put(fpd.getKey(), value); + } + + case Dropdown -> { + if (!fpd.isDropdownFreeForm()) { + List values = fpd.isDropdownSingleSelection() + ? List.of(value) + : List.of(value.split(";")); + + if (!actions.map(a -> a.getDropdownValues(fpd.getKey()).keySet()). + orElse(Set.of()).containsAll(values)) { + + throw new JobExecutionException("Not allowed for " + fpd.getKey() + ": " + values); + } + } + + vars.put(fpd.getKey(), value); + } + + default -> { + } + } + } + // if validator is defined, validate the provided form try { - actions.ifPresent(a -> a.validate(macroTaskForm)); + actions.ifPresent(a -> a.validate(macroTaskForm, vars)); } catch (ValidationException e) { throw new JobExecutionException("Invalid form submitted for task " + task.getKey(), e); } - // build the JEXL context where variables are mapped to property values, built according to the defined type - Map vars = macroTaskForm.getProperties().stream(). - map(p -> task.getFormPropertyDefs().stream(). - filter(fpd -> fpd.getKey().equals(p.getId()) && validate(fpd, p.getValue(), actions)).findFirst(). - map(fpd -> Pair.of(fpd, p.getValue()))). - filter(Optional::isPresent).map(Optional::get). - map(pair -> { - Object value; - switch (pair.getLeft().getType()) { - case Boolean: - value = BooleanUtils.toBoolean(pair.getRight()); - break; - - case Date: - value = StringUtils.isBlank(pair.getLeft().getDatePattern()) - ? FormatUtils.parseDate(pair.getRight()) - : FormatUtils.parseDate(pair.getRight(), pair.getLeft().getDatePattern()); - break; - - case Long: - value = NumberUtils.toLong(pair.getRight()); - break; - - case Enum: - case Dropdown: - case String: - case Password: - default: - value = pair.getRight(); - } - - return Pair.of(pair.getLeft().getKey(), value); - }).collect(Collectors.toMap(Pair::getLeft, Pair::getRight)); - output.append("Form parameter values: ").append(vars).append("\n\n"); return vars.isEmpty() ? Optional.empty() : Optional.of(new MapContext(vars)); @@ -165,10 +188,10 @@ protected String run( final boolean dryRun) throws JobExecutionException { - Future>> future = executor.submit( + Future>> future = executor.submit( new DelegatingSecurityContextCallable<>(() -> { - AtomicReference> error = new AtomicReference<>(); + AtomicReference> error = new AtomicReference<>(); for (int i = 0; i < commands.size() && error.get() == null; i++) { Pair, CommandArgs> command = commands.get(i); @@ -187,14 +210,14 @@ protected String run( output.append(cmdOut); } - } catch (Exception e) { + } catch (Throwable t) { if (task.isContinueOnError()) { - output.append("Continuing on error: <").append(e.getMessage()).append('>'); + output.append("Continuing on error: <").append(t.getMessage()).append('>'); LOG.error("While running {} with args {}, continuing on error", - command.getLeft().getClass().getName(), command.getRight(), e); + command.getLeft().getClass().getName(), command.getRight(), t); } else { - error.set(Pair.of(command.getLeft().getClass().getName(), e)); + error.set(Pair.of(AopUtils.getTargetClass(command.getLeft()).getName(), t)); } } output.append("\n\n"); @@ -204,7 +227,7 @@ protected String run( })); try { - AtomicReference> error = future.get(); + AtomicReference> error = future.get(); if (error.get() != null) { throw new JobExecutionException("While running " + error.get().getLeft(), error.get().getRight()); } diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormPanel.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormPanel.java index 78f64fa198..e2e80e488a 100644 --- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormPanel.java +++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormPanel.java @@ -44,9 +44,7 @@ public void onClick(final AjaxRequestTarget target) { MetaDataRoleAuthorizationStrategy.authorize(userDetails, ENABLE, IdRepoEntitlement.USER_READ); boolean enabled = form.getUserTO() != null; - userDetails.setVisible(enabled).setEnabled(enabled); - - add(userDetails); + add(userDetails.setVisible(enabled).setEnabled(enabled)); } protected abstract void viewDetails(AjaxRequestTarget target);