You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2018/09/25 12:32:25 UTC

[27/34] syncope git commit: [SYNCOPE-1369] Flowable support refactored as extension + code review (now with variable cleanup)

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java
new file mode 100644
index 0000000..e16f5c2
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java
@@ -0,0 +1,355 @@
+/*
+ * 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.approvals;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.panels.DirectoryPanel;
+import org.apache.syncope.client.console.rest.UserRequestRestClient;
+import org.apache.syncope.client.console.approvals.ApprovalDirectoryPanel.ApprovalProvider;
+import org.apache.syncope.client.console.layout.FormLayoutInfoUtils;
+import org.apache.syncope.client.console.layout.UserFormLayoutInfo;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.widgets.ApprovalsWidget;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.syncope.client.console.wizards.any.AnyWrapper;
+import org.apache.syncope.client.console.wizards.any.UserWizardBuilder;
+import org.apache.syncope.common.lib.AnyOperations;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.patch.PasswordPatch;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.to.UserRequestForm;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.FlowableEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+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.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+
+public class ApprovalDirectoryPanel
+        extends DirectoryPanel<UserRequestForm, UserRequestForm, ApprovalProvider, UserRequestRestClient> {
+
+    private static final long serialVersionUID = -7122136682275797903L;
+
+    protected final BaseModal<UserRequestForm> manageApprovalModal = new BaseModal<UserRequestForm>("outer") {
+
+        private static final long serialVersionUID = 389935548143327858L;
+
+        @Override
+        protected void onConfigure() {
+            super.onConfigure();
+            addSubmitButton();
+            size(Modal.Size.Large);
+        }
+
+    };
+
+    public ApprovalDirectoryPanel(final String id, final PageReference pageReference) {
+        super(id, pageReference, true);
+        disableCheckBoxes();
+        setFooterVisibility(false);
+        modal.size(Modal.Size.Large);
+
+        addOuterObject(manageApprovalModal);
+
+        manageApprovalModal.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {
+
+            private static final long serialVersionUID = 8804221891699487139L;
+
+            @Override
+            public void onClose(final AjaxRequestTarget target) {
+                updateResultTable(target);
+
+                Serializable widget = SyncopeConsoleSession.get().getAttribute(ApprovalsWidget.class.getName());
+                if (widget instanceof ApprovalsWidget) {
+                    ((ApprovalsWidget) widget).refreshLatestAlerts(target);
+                }
+
+                manageApprovalModal.show(false);
+            }
+        });
+
+        restClient = new UserRequestRestClient();
+
+        initResultTable();
+
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, FlowableEntitlement.WORKFLOW_FORM_SUBMIT);
+    }
+
+    @Override
+    protected List<IColumn<UserRequestForm, String>> getColumns() {
+        List<IColumn<UserRequestForm, String>> columns = new ArrayList<>();
+
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("taskId"), "taskId", "taskId"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("key"), "formKey", "formKey"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("username"), "username"));
+        columns.add(new DatePropertyColumn<>(
+                new ResourceModel("createTime"), "createTime", "createTime"));
+        columns.add(new DatePropertyColumn<>(
+                new ResourceModel("dueDate"), "dueDate", "dueDate"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("owner"), "owner", "owner"));
+
+        return columns;
+    }
+
+    @Override
+    public ActionsPanel<UserRequestForm> getActions(final IModel<UserRequestForm> model) {
+        final ActionsPanel<UserRequestForm> panel = super.getActions(model);
+
+        panel.add(new ActionLink<UserRequestForm>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final UserRequestForm ignore) {
+                claimForm(model.getObject().getTaskId());
+                SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                target.add(container);
+            }
+        }, ActionLink.ActionType.CLAIM, FlowableEntitlement.WORKFLOW_FORM_CLAIM);
+
+        panel.add(new ActionLink<UserRequestForm>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final UserRequestForm ignore) {
+                manageApprovalModal.setFormModel(new CompoundPropertyModel<>(model.getObject()));
+
+                target.add(manageApprovalModal.setContent(
+                        new ApprovalModal(manageApprovalModal, pageRef, model.getObject()) {
+
+                    private static final long serialVersionUID = 5546519445061007248L;
+
+                    @Override
+                    public void onSubmit(final AjaxRequestTarget target) {
+                        try {
+                            super.onSubmit(target);
+
+                            ApprovalDirectoryPanel.this.getTogglePanel().close(target);
+                        } catch (SyncopeClientException e) {
+                            SyncopeConsoleSession.get().error(getString(Constants.ERROR) + ": " + e.getMessage());
+                        }
+                        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                    }
+
+                }));
+
+                manageApprovalModal.header(new Model<>(getString("approval.manage", new Model<>(model.getObject()))));
+                manageApprovalModal.show(true);
+            }
+
+            @Override
+            protected boolean statusCondition(final UserRequestForm modelObject) {
+                return SyncopeConsoleSession.get().getSelfTO().getUsername().
+                        equals(model.getObject().getOwner());
+            }
+
+        }, ActionLink.ActionType.MANAGE_APPROVAL, FlowableEntitlement.WORKFLOW_FORM_READ);
+
+        // SYNCOPE-1200 edit user while in approval state
+        panel.add(new ActionLink<UserRequestForm>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final UserRequestForm ignore) {
+                modal.setFormModel(new CompoundPropertyModel<>(model.getObject()));
+
+                UserRequestForm formTO = model.getObject();
+                UserTO newUserTO;
+                UserTO previousUserTO;
+                if (formTO.getUserPatch() == null) {
+                    newUserTO = formTO.getUserTO();
+                    previousUserTO = null;
+                } else if (formTO.getUserTO() == null) {
+                    // make it stronger by handling possible NPE
+                    previousUserTO = new UserTO();
+                    previousUserTO.setKey(formTO.getUserPatch().getKey());
+                    newUserTO = AnyOperations.patch(previousUserTO, formTO.getUserPatch());
+                } else {
+                    previousUserTO = formTO.getUserTO();
+                    formTO.getUserTO().setKey(formTO.getUserPatch().getKey());
+                    formTO.getUserTO().setPassword(null);
+                    newUserTO = AnyOperations.patch(formTO.getUserTO(), formTO.getUserPatch());
+                }
+
+                AjaxWizard.EditItemActionEvent<UserTO> editItemActionEvent =
+                        new AjaxWizard.EditItemActionEvent<>(newUserTO, target);
+                editItemActionEvent.forceModalPanel(new ApprovalUserWizardBuilder(
+                        model.getObject(),
+                        previousUserTO,
+                        newUserTO,
+                        new AnyTypeRestClient().read(AnyTypeKind.USER.name()).getClasses(),
+                        FormLayoutInfoUtils.fetch(Collections.singletonList(AnyTypeKind.USER.name())).getLeft(),
+                        pageRef
+                ).build(BaseModal.CONTENT_ID, AjaxWizard.Mode.EDIT));
+
+                send(ApprovalDirectoryPanel.this, Broadcast.EXACT, editItemActionEvent);
+            }
+
+            @Override
+            protected boolean statusCondition(final UserRequestForm modelObject) {
+                return SyncopeConsoleSession.get().getSelfTO().getUsername().
+                        equals(model.getObject().getOwner());
+            }
+
+        }, ActionLink.ActionType.EDIT_APPROVAL, FlowableEntitlement.WORKFLOW_FORM_SUBMIT);
+
+        return panel;
+    }
+
+    @Override
+    protected ApprovalProvider dataProvider() {
+        return new ApprovalProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_WORKFLOW_FORM_PAGINATOR_ROWS;
+    }
+
+    public static class ApprovalProvider extends DirectoryDataProvider<UserRequestForm> {
+
+        private static final long serialVersionUID = -2311716167583335852L;
+
+        private final UserRequestRestClient restClient = new UserRequestRestClient();
+
+        public ApprovalProvider(final int paginatorRows) {
+            super(paginatorRows);
+
+            setSort("createTime", SortOrder.ASCENDING);
+        }
+
+        @Override
+        public Iterator<UserRequestForm> iterator(final long first, final long count) {
+            int page = ((int) first / paginatorRows);
+            return restClient.getForms((page < 0 ? 0 : page) + 1, paginatorRows, getSort()).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.countForms();
+        }
+
+        @Override
+        public IModel<UserRequestForm> model(final UserRequestForm form) {
+            return new IModel<UserRequestForm>() {
+
+                private static final long serialVersionUID = -2566070996511906708L;
+
+                @Override
+                public UserRequestForm getObject() {
+                    return form;
+                }
+            };
+        }
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBatches() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+
+    private void claimForm(final String taskId) {
+        try {
+            restClient.claimForm(taskId);
+        } catch (SyncopeClientException scee) {
+            SyncopeConsoleSession.get().error(getString(Constants.ERROR) + ": " + scee.getMessage());
+        }
+    }
+
+    private class ApprovalUserWizardBuilder extends UserWizardBuilder {
+
+        private static final long serialVersionUID = 1854981134836384069L;
+
+        private final UserRequestForm formTO;
+
+        ApprovalUserWizardBuilder(
+                final UserRequestForm formTO,
+                final UserTO previousUserTO,
+                final UserTO userTO,
+                final List<String> anyTypeClasses,
+                final UserFormLayoutInfo formLayoutInfo,
+                final PageReference pageRef) {
+
+            super(previousUserTO, userTO, anyTypeClasses, formLayoutInfo, pageRef);
+            this.formTO = formTO;
+        }
+
+        @Override
+        protected Serializable onApplyInternal(final AnyWrapper<UserTO> modelObject) {
+            UserTO inner = modelObject.getInnerObject();
+
+            ProvisioningResult<UserTO> result;
+
+            UserPatch patch = AnyOperations.diff(inner, formTO.getUserTO(), false);
+
+            if (StringUtils.isNotBlank(inner.getPassword())) {
+                PasswordPatch passwordPatch = new PasswordPatch.Builder().
+                        value(inner.getPassword()).onSyncope(true).resources(inner.
+                        getResources()).
+                        build();
+                patch.setPassword(passwordPatch);
+            }
+            // update just if it is changed
+            if (patch.isEmpty()) {
+                result = new ProvisioningResult<>();
+                result.setEntity(inner);
+            } else {
+                result = userRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
+                UserRequestForm workFlowTO = restClient.getForms(result.getEntity().getKey()).get(0);
+                if (workFlowTO != null) {
+                    claimForm(workFlowTO.getTaskId());
+                }
+            }
+
+            return result;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalModal.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalModal.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalModal.java
new file mode 100644
index 0000000..0cae1c8
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalModal.java
@@ -0,0 +1,82 @@
+/*
+ * 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.approvals;
+
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.MultilevelPanel;
+import org.apache.syncope.client.console.rest.UserRequestRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.common.lib.to.UserRequestForm;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.syncope.client.console.panels.SubmitableModalPanel;
+import org.apache.syncope.client.console.panels.WizardModalPanel;
+
+public class ApprovalModal extends Panel implements SubmitableModalPanel, WizardModalPanel<UserRequestForm> {
+
+    private static final long serialVersionUID = -8847854414429745216L;
+
+    private final UserRequestRestClient restClient = new UserRequestRestClient();
+
+    private final BaseModal<?> modal;
+
+    private final UserRequestForm formTO;
+
+    private final PageReference pageRef;
+
+    public ApprovalModal(final BaseModal<?> modal, final PageReference pageRef, final UserRequestForm formTO) {
+        super(BaseModal.CONTENT_ID);
+        this.modal = modal;
+        this.formTO = formTO;
+        this.pageRef = pageRef;
+
+        MultilevelPanel mlp = new MultilevelPanel("approval");
+        mlp.setFirstLevel(new Approval(pageRef, formTO) {
+
+            private static final long serialVersionUID = -2195387360323687302L;
+
+            @Override
+            protected void viewDetails(final AjaxRequestTarget target) {
+                mlp.next(getString("approval.details"), new ApprovalDetails(pageRef, formTO), target);
+            }
+        });
+        add(mlp);
+    }
+
+    @Override
+    public void onSubmit(final AjaxRequestTarget target) {
+        this.restClient.submitForm(formTO);
+        this.modal.show(false);
+        this.modal.close(target);
+        SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+    }
+
+    @Override
+    public void onError(final AjaxRequestTarget target) {
+        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+    }
+
+    @Override
+    public UserRequestForm getItem() {
+        return this.formTO;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/Approvals.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/Approvals.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/Approvals.java
new file mode 100644
index 0000000..0dbc4db
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/Approvals.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.pages;
+
+import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.approvals.ApprovalDirectoryPanel;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class Approvals extends BasePage {
+
+    private static final long serialVersionUID = -1100228004207271271L;
+
+    public Approvals(final PageParameters parameters) {
+        super(parameters);
+
+        body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", Dashboard.class));
+
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        body.add(content);
+
+        content.add(new ApprovalDirectoryPanel("wfPanel", getPageReference()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/Flowable.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/Flowable.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/Flowable.java
new file mode 100644
index 0000000..562aad1
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/Flowable.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.pages;
+
+import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.annotations.ExtPage;
+import org.apache.syncope.client.console.panels.BpmnProcessDirectoryPanel;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.syncope.common.lib.types.FlowableEntitlement;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+@ExtPage(label = "Flowable", icon = "fa-briefcase",
+        listEntitlement = FlowableEntitlement.BPMN_PROCESS_LIST, priority = 200)
+public class Flowable extends BaseExtPage {
+
+    private static final long serialVersionUID = -8781434495150074529L;
+
+    public Flowable(final PageParameters parameters) {
+        super(parameters);
+
+        body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", Dashboard.class));
+
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        body.add(content);
+
+        WebMarkupContainer disabled = new WebMarkupContainer("disabled");
+        disabled.setOutputMarkupPlaceholderTag(true);
+        content.add(disabled);
+
+        WizardMgtPanel<BpmnProcessTO> bpmnProcessesPanel = new BpmnProcessDirectoryPanel.Builder(getPageReference()) {
+
+            private static final long serialVersionUID = -5960765294082359003L;
+
+        }.disableCheckBoxes().build("bpmnProcessesPanel");
+        bpmnProcessesPanel.setOutputMarkupPlaceholderTag(true);
+        MetaDataRoleAuthorizationStrategy.authorize(bpmnProcessesPanel, ENABLE, FlowableEntitlement.BPMN_PROCESS_LIST);
+
+        content.add(bpmnProcessesPanel);
+
+        if (SyncopeConsoleSession.get().getPlatformInfo().getUserWorkflowAdapter().contains("Flowable")) {
+            disabled.setVisible(false);
+        } else {
+            bpmnProcessesPanel.setVisible(false);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/FlowableModelerPopupPage.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/FlowableModelerPopupPage.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/FlowableModelerPopupPage.java
new file mode 100644
index 0000000..b55d2b5
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/pages/FlowableModelerPopupPage.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.pages;
+
+import org.apache.wicket.markup.html.WebPage;
+
+public class FlowableModelerPopupPage extends WebPage {
+
+    private static final long serialVersionUID = -7031206743629422898L;
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/BpmnProcessDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/BpmnProcessDirectoryPanel.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/BpmnProcessDirectoryPanel.java
new file mode 100644
index 0000000..b7914bb
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/BpmnProcessDirectoryPanel.java
@@ -0,0 +1,308 @@
+/*
+ * 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 de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.ws.rs.core.MediaType;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.pages.ModelerPopupPage;
+import org.apache.syncope.client.console.panels.BpmnProcessDirectoryPanel.BpmProcessDataProvider;
+import org.apache.syncope.client.console.rest.BpmnProcessRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.ImageModalPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.XMLEditorPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizardBuilder;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.syncope.common.lib.types.FlowableEntitlement;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+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.extensions.wizard.WizardModel;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.util.io.IOUtils;
+
+public class BpmnProcessDirectoryPanel extends DirectoryPanel<
+        BpmnProcessTO, BpmnProcessTO, BpmProcessDataProvider, BpmnProcessRestClient> {
+
+    private static final long serialVersionUID = 2705668831139984998L;
+
+    private static final String FLOWABLE_MODELER_CTX = "flowable-modeler";
+
+    private final BaseModal<String> utility;
+
+    protected BpmnProcessDirectoryPanel(final String id, final Builder builder) {
+        super(id, builder);
+
+        this.addNewItemPanelBuilder(new AjaxWizardBuilder<BpmnProcessTO>(new BpmnProcessTO(), pageRef) {
+
+            private static final long serialVersionUID = 1633859795677053912L;
+
+            @Override
+            protected WizardModel buildModelSteps(
+                    final BpmnProcessTO modelObject, final WizardModel wizardModel) {
+
+                return wizardModel;
+            }
+        }, false);
+        NewBpmnProcess newBpmnProcess = new NewBpmnProcess("newBpmnProcess", container, pageRef);
+        addInnerObject(newBpmnProcess);
+        AjaxLink<Void> newBpmnProcessLink = new AjaxLink<Void>("add") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                newBpmnProcess.toggle(target, true);
+            }
+        };
+        ((WebMarkupContainer) get("container:content")).addOrReplace(newBpmnProcessLink);
+
+        setShowResultPage(true);
+
+        modal.size(Modal.Size.Large);
+
+        utility = new BaseModal<>("outer");
+        addOuterObject(utility);
+        utility.size(Modal.Size.Large);
+        AjaxSubmitLink xmlEditorSubmit = utility.addSubmitButton();
+        MetaDataRoleAuthorizationStrategy.authorize(xmlEditorSubmit, RENDER, FlowableEntitlement.BPMN_PROCESS_SET);
+        utility.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {
+
+            private static final long serialVersionUID = 8804221891699487139L;
+
+            @Override
+            public void onClose(final AjaxRequestTarget target) {
+                utility.show(false);
+                utility.close(target);
+            }
+        });
+        initResultTable();
+    }
+
+    @Override
+    protected BpmProcessDataProvider dataProvider() {
+        return new BpmProcessDataProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_WORKFLOW_PAGINATOR_ROWS;
+    }
+
+    @Override
+    protected List<IColumn<BpmnProcessTO, String>> getColumns() {
+        List<IColumn<BpmnProcessTO, String>> columns = new ArrayList<>();
+
+        columns.add(new PropertyColumn<>(new ResourceModel("key"), "key"));
+        columns.add(new PropertyColumn<>(new ResourceModel("name"), "name", "name"));
+
+        return columns;
+    }
+
+    @Override
+    public ActionsPanel<BpmnProcessTO> getActions(final IModel<BpmnProcessTO> model) {
+        final ActionsPanel<BpmnProcessTO> panel = super.getActions(model);
+
+        panel.add(new ActionLink<BpmnProcessTO>() {
+
+            private static final long serialVersionUID = -184018732772021627L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final BpmnProcessTO ignore) {
+                final IModel<String> wfDefinition = new Model<>();
+                try {
+                    wfDefinition.setObject(IOUtils.toString(restClient.getDefinition(
+                            MediaType.APPLICATION_XML_TYPE, model.getObject().getKey())));
+                } catch (IOException e) {
+                    LOG.error("Could not get workflow definition", e);
+                }
+
+                utility.header(Model.of(model.getObject().getKey()));
+                utility.setContent(new XMLEditorPanel(utility, wfDefinition, false, pageRef) {
+
+                    private static final long serialVersionUID = -7688359318035249200L;
+
+                    @Override
+                    public void onSubmit(final AjaxRequestTarget target) {
+                        if (StringUtils.isNotBlank(wfDefinition.getObject())) {
+                            try {
+                                restClient.setDefinition(MediaType.APPLICATION_XML_TYPE,
+                                        model.getObject().getKey(), wfDefinition.getObject());
+                                SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+
+                                target.add(container);
+                                utility.show(false);
+                                utility.close(target);
+                            } catch (SyncopeClientException e) {
+                                SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                                        ? e.getClass().getName() : e.getMessage());
+                            }
+                            ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                        }
+                    }
+                });
+                utility.show(target);
+                target.add(utility);
+            }
+        }, ActionLink.ActionType.EDIT, FlowableEntitlement.BPMN_PROCESS_SET);
+
+        panel.add(new ActionLink<BpmnProcessTO>() {
+
+            private static final long serialVersionUID = 3109256773218160485L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final BpmnProcessTO ignore) {
+                modal.header(Model.of(model.getObject().getKey()));
+                modal.setContent(new ImageModalPanel<>(
+                        modal, restClient.getDiagram(model.getObject().getKey()), pageRef));
+                modal.show(target);
+                target.add(modal);
+            }
+        }, ActionLink.ActionType.VIEW, FlowableEntitlement.BPMN_PROCESS_GET);
+
+        panel.add(new ActionLink<BpmnProcessTO>() {
+
+            private static final long serialVersionUID = -184018732772021627L;
+
+            @Override
+            public Class<? extends Page> getPageClass() {
+                return ModelerPopupPage.class;
+            }
+
+            @Override
+            public PageParameters getPageParameters() {
+                PageParameters parameters = new PageParameters();
+                parameters.add(Constants.MODELER_CONTEXT, FLOWABLE_MODELER_CTX);
+                parameters.add(Constants.MODEL_ID_PARAM, model.getObject().getModelId());
+
+                return parameters;
+            }
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final BpmnProcessTO ignore) {
+                // do nothing
+            }
+        }, ActionLink.ActionType.EXTERNAL_EDITOR, FlowableEntitlement.BPMN_PROCESS_SET);
+
+        panel.add(new ActionLink<BpmnProcessTO>() {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            protected boolean statusCondition(final BpmnProcessTO modelObject) {
+                return !modelObject.isUserWorkflow();
+            }
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final BpmnProcessTO ignore) {
+                try {
+                    restClient.deleteDefinition(model.getObject().getKey());
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting BPMN definition {}", model.getObject().getName(), e);
+                    SyncopeConsoleSession.get().error(
+                            StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, FlowableEntitlement.BPMN_PROCESS_DELETE, true);
+
+        return panel;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBatches() {
+        return Collections.emptyList();
+    }
+
+    public abstract static class Builder
+            extends DirectoryPanel.Builder<BpmnProcessTO, BpmnProcessTO, BpmnProcessRestClient> {
+
+        private static final long serialVersionUID = 5088962796986706805L;
+
+        public Builder(final PageReference pageRef) {
+            super(new BpmnProcessRestClient(), pageRef);
+        }
+
+        @Override
+        protected WizardMgtPanel<BpmnProcessTO> newInstance(final String id, final boolean wizardInModal) {
+            return new BpmnProcessDirectoryPanel(id, this);
+        }
+    }
+
+    protected class BpmProcessDataProvider extends DirectoryDataProvider<BpmnProcessTO> {
+
+        private static final long serialVersionUID = 1764153405387687592L;
+
+        private final SortableDataProviderComparator<BpmnProcessTO> comparator;
+
+        private final BpmnProcessRestClient restClient = new BpmnProcessRestClient();
+
+        public BpmProcessDataProvider(final int paginatorRows) {
+            super(paginatorRows);
+            this.comparator = new SortableDataProviderComparator<>(this);
+            setSort("key", SortOrder.ASCENDING);
+        }
+
+        @Override
+        public Iterator<BpmnProcessTO> iterator(final long first, final long count) {
+            List<BpmnProcessTO> result = restClient.getDefinitions();
+            Collections.sort(result, comparator);
+            return result.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.getDefinitions().size();
+        }
+
+        @Override
+        public IModel<BpmnProcessTO> model(final BpmnProcessTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java
new file mode 100644
index 0000000..e28fb62
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java
@@ -0,0 +1,87 @@
+/*
+ * 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.io.Serializable;
+import javax.ws.rs.core.MediaType;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.pdfbox.util.Charsets;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.rest.BpmnProcessRestClient;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.Model;
+
+public class NewBpmnProcess extends TogglePanel<Serializable> {
+
+    private static final long serialVersionUID = -4886361549305302161L;
+
+    private final BpmnProcessRestClient restClient = new BpmnProcessRestClient();
+
+    private final Form<?> form;
+
+    public NewBpmnProcess(final String id, final WebMarkupContainer container, final PageReference pageRef) {
+        super(id, pageRef);
+
+        form = new Form<>("form");
+        addInnerObject(form);
+
+        final TextField<String> key = new TextField<>("key", new Model<>());
+        key.setRequired(true);
+        form.add(key);
+
+        form.add(new AjaxSubmitLink("submit", form) {
+
+            private static final long serialVersionUID = 4947613489823025052L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target) {
+                try {
+                    restClient.setDefinition(MediaType.APPLICATION_XML_TYPE, key.getModelObject(),
+                            IOUtils.toString(
+                                    getClass().getResourceAsStream("empty.bpmn20.xml"),
+                                    Charsets.UTF_8.name()).replaceAll("%KEY%", key.getModelObject()));
+
+                    key.getModel().setObject(null);
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    toggle(target, false);
+                    target.add(container);
+                } catch (Exception e) {
+                    LOG.error("While creating new BPMN process", e);
+                    SyncopeConsoleSession.get().error(
+                            StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+
+            @Override
+            protected void onError(final AjaxRequestTarget target) {
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/AbstractBpmnProcessResource.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/AbstractBpmnProcessResource.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/AbstractBpmnProcessResource.java
new file mode 100644
index 0000000..91a42e3
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/AbstractBpmnProcessResource.java
@@ -0,0 +1,51 @@
+/*
+ * 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.resources;
+
+import javax.ws.rs.NotFoundException;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.rest.BpmnProcessRestClient;
+import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.util.string.StringValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractBpmnProcessResource extends AbstractResource {
+
+    private static final long serialVersionUID = 5163553843196539019L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(AbstractBpmnProcessResource.class);
+
+    protected final BpmnProcessRestClient restClient = new BpmnProcessRestClient();
+
+    protected BpmnProcessTO getBpmnProcess(final Attributes attributes) {
+        StringValue modelId = attributes.getRequest().getQueryParameters().getParameterValue(Constants.MODEL_ID_PARAM);
+
+        BpmnProcessTO bpmnProcess = modelId == null || modelId.isNull()
+                ? null
+                : restClient.getDefinitions().stream().
+                        filter(object -> modelId.toString().equals(object.getModelId())).findAny().orElse(null);
+        if (bpmnProcess == null) {
+            throw new NotFoundException("BPMN process with modelId " + modelId);
+        }
+
+        return bpmnProcess;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/BpmnProcessGETResource.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/BpmnProcessGETResource.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/BpmnProcessGETResource.java
new file mode 100644
index 0000000..ba34502
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/BpmnProcessGETResource.java
@@ -0,0 +1,56 @@
+/*
+ * 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.resources;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.client.console.annotations.Resource;
+import org.apache.syncope.client.console.rest.BpmnProcessRestClient;
+import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.wicket.util.io.IOUtils;
+
+/**
+ * Mirror REST resource for obtaining BPMN process in JSON (used by Flowable Modeler).
+ */
+@Resource(key = "bpmnProcessGET", path = "/bpmnProcessGET")
+public class BpmnProcessGETResource extends AbstractBpmnProcessResource {
+
+    private static final long serialVersionUID = 4637304868056148970L;
+
+    @Override
+    protected ResourceResponse newResourceResponse(final Attributes attributes) {
+        final BpmnProcessTO toGet = getBpmnProcess(attributes);
+
+        ResourceResponse response = new ResourceResponse();
+        response.disableCaching();
+        response.setContentType(MediaType.APPLICATION_JSON);
+        response.setTextEncoding(StandardCharsets.UTF_8.name());
+        response.setWriteCallback(new WriteCallback() {
+
+            @Override
+            public void writeData(final Attributes attributes) throws IOException {
+                IOUtils.copy(new BpmnProcessRestClient().getDefinition(MediaType.APPLICATION_JSON_TYPE, toGet.getKey()),
+                        attributes.getResponse().getOutputStream());
+            }
+        });
+
+        return response;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/BpmnProcessPUTResource.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/BpmnProcessPUTResource.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/BpmnProcessPUTResource.java
new file mode 100644
index 0000000..d583396
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/resources/BpmnProcessPUTResource.java
@@ -0,0 +1,73 @@
+/*
+ * 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.resources;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.common.util.UrlUtils;
+import org.apache.syncope.client.console.annotations.Resource;
+import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.wicket.util.io.IOUtils;
+
+/**
+ * Mirror REST resource for setting BPMN process in JSON (used by Flowable Modeler).
+ */
+@Resource(key = "bpmnProcessPUT", path = "/bpmnProcessPUT")
+public class BpmnProcessPUTResource extends AbstractBpmnProcessResource {
+
+    private static final long serialVersionUID = 2964542005207297944L;
+
+    @Override
+    protected ResourceResponse newResourceResponse(final Attributes attributes) {
+        String definition = null;
+        try {
+            HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
+            String requestBody = IOUtils.toString(request.getInputStream());
+            String[] split = requestBody.split("&");
+            for (int i = 0; i < split.length && definition == null; i++) {
+                String keyValue = split[i];
+                if (keyValue.startsWith("json_xml=")) {
+                    definition = UrlUtils.urlDecode(keyValue.split("=")[1]);
+                }
+            }
+        } catch (IOException e) {
+            LOG.error("Could not extract BPMN process", e);
+        }
+
+        BpmnProcessTO toSet = getBpmnProcess(attributes);
+
+        if (definition == null || toSet == null) {
+            return new ResourceResponse().setStatusCode(Response.Status.BAD_REQUEST.getStatusCode()).
+                    setError(Response.Status.BAD_REQUEST.getStatusCode(),
+                            "Could not extract BPMN process' model id and / or definition");
+        }
+
+        try {
+            restClient.setDefinition(MediaType.APPLICATION_JSON_TYPE, toSet.getKey(), definition);
+            return new ResourceResponse().setStatusCode(Response.Status.NO_CONTENT.getStatusCode());
+        } catch (Exception e) {
+            LOG.error("While updating BPMN process", e);
+            return new ResourceResponse().setStatusCode(Response.Status.BAD_REQUEST.getStatusCode()).
+                    setError(Response.Status.BAD_REQUEST.getStatusCode(),
+                            "While updating BPMN process: " + e.getMessage());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/BpmnProcessRestClient.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/BpmnProcessRestClient.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/BpmnProcessRestClient.java
new file mode 100644
index 0000000..2b7c368
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/BpmnProcessRestClient.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.rest;
+
+import java.io.InputStream;
+import java.util.List;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.BpmnProcessService;
+
+public class BpmnProcessRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = 5049285686167071017L;
+
+    private BpmnProcessService getService(final MediaType mediaType) {
+        return SyncopeConsoleSession.get().getService(mediaType, BpmnProcessService.class);
+    }
+
+    public List<BpmnProcessTO> getDefinitions() {
+        return getService(BpmnProcessService.class).list();
+    }
+
+    public InputStream getDefinition(final MediaType mediaType, final String key) {
+        Response response = getService(mediaType).get(key);
+
+        return (InputStream) response.getEntity();
+    }
+
+    public byte[] getDiagram(final String key) {
+        BpmnProcessService service = getService(BpmnProcessService.class);
+        WebClient.client(service).accept(RESTHeaders.MEDIATYPE_IMAGE_PNG);
+        Response response = service.exportDiagram(key);
+
+        byte[] diagram;
+        try {
+            diagram = IOUtils.readBytesFromStream((InputStream) response.getEntity());
+        } catch (Exception e) {
+            LOG.error("Could not get workflow diagram", e);
+            diagram = new byte[0];
+        }
+        return diagram;
+    }
+
+    public void setDefinition(final MediaType mediaType, final String key, final String definition) {
+        getService(mediaType).set(key, definition);
+    }
+
+    public void deleteDefinition(final String key) {
+        getService(BpmnProcessService.class).delete(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java
new file mode 100644
index 0000000..555555b
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java
@@ -0,0 +1,54 @@
+/*
+ * 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.rest;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.UserRequestForm;
+import org.apache.syncope.common.rest.api.beans.UserRequestFormQuery;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
+import org.apache.syncope.common.rest.api.service.UserRequestService;
+
+public class UserRequestRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -4785231164900813921L;
+
+    public int countForms() {
+        return getService(UserRequestService.class).
+                getForms(new UserRequestFormQuery.Builder().page(1).size(1).build()).
+                getTotalCount();
+    }
+
+    public List<UserRequestForm> getForms(final int page, final int size, final SortParam<String> sort) {
+        return getService(UserRequestService.class).
+                getForms(new UserRequestFormQuery.Builder().page(page).size(size).orderBy(toOrderBy(sort)).build()).
+                getResult();
+    }
+
+    public List<UserRequestForm> getForms(final String userKey) {
+        return getService(UserRequestService.class).getForms(userKey);
+    }
+
+    public UserRequestForm claimForm(final String taskKey) {
+        return getService(UserRequestService.class).claimForm(taskKey);
+    }
+
+    public void submitForm(final UserRequestForm form) {
+        getService(UserRequestService.class).submitForm(form);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserWorkflowTaskRestClient.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserWorkflowTaskRestClient.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserWorkflowTaskRestClient.java
new file mode 100644
index 0000000..68d5856
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserWorkflowTaskRestClient.java
@@ -0,0 +1,34 @@
+/*
+ * 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.rest;
+
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.to.WorkflowTaskExecInput;
+import org.apache.syncope.common.rest.api.service.UserWorkflowTaskService;
+
+public class UserWorkflowTaskRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = 5765024947677482995L;
+
+    public UserTO executeNextTask(final UserTO userTO) {
+        WorkflowTaskExecInput input = new WorkflowTaskExecInput();
+        input.setUserKey(userTO.getKey());
+        return getService(UserWorkflowTaskService.class).executeNextTask(input);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/widgets/ApprovalsWidget.java
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/widgets/ApprovalsWidget.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/widgets/ApprovalsWidget.java
new file mode 100644
index 0000000..8f8329e
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/widgets/ApprovalsWidget.java
@@ -0,0 +1,130 @@
+/*
+ * 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.widgets;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesomeIconTypeBuilder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.annotations.ExtWidget;
+import org.apache.syncope.client.console.pages.Approvals;
+import org.apache.syncope.client.console.rest.UserRequestRestClient;
+import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior;
+import org.apache.syncope.common.lib.to.UserRequestForm;
+import org.apache.syncope.common.lib.types.FlowableEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.link.AbstractLink;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.util.ListModel;
+import org.apache.wicket.util.time.Duration;
+
+@ExtWidget(priority = 10)
+public class ApprovalsWidget extends ExtAlertWidget<UserRequestForm> {
+
+    private static final long serialVersionUID = 7667120094526529934L;
+
+    private final UserRequestRestClient restClient = new UserRequestRestClient();
+
+    private final List<UserRequestForm> lastApprovals = new ArrayList<>();
+
+    public ApprovalsWidget(final String id, final PageReference pageRef) {
+        super(id);
+        setOutputMarkupId(true);
+
+        latestAlertsList.add(new IndicatorAjaxTimerBehavior(Duration.seconds(30)) {
+
+            private static final long serialVersionUID = 7298597675929755960L;
+
+            @Override
+            protected void onTimer(final AjaxRequestTarget target) {
+                if (!latestAlerts.getObject().equals(lastApprovals)) {
+                    refreshLatestAlerts(target);
+                }
+            }
+        });
+    }
+
+    public final void refreshLatestAlerts(final AjaxRequestTarget target) {
+        latestAlerts.getObject().clear();
+        latestAlerts.getObject().addAll(lastApprovals);
+
+        int latestAlertSize = getLatestAlertsSize();
+        linkAlertsNumber.setDefaultModelObject(latestAlertSize);
+        target.add(linkAlertsNumber);
+
+        headerAlertsNumber.setDefaultModelObject(latestAlertSize);
+        target.add(headerAlertsNumber);
+
+        target.add(latestAlertsList);
+
+        lastApprovals.clear();
+        lastApprovals.addAll(latestAlerts.getObject());
+    }
+
+    @Override
+    protected int getLatestAlertsSize() {
+        return SyncopeConsoleSession.get().owns(FlowableEntitlement.WORKFLOW_FORM_LIST)
+                && SyncopeConsoleSession.get().owns(FlowableEntitlement.WORKFLOW_FORM_READ)
+                ? restClient.countForms()
+                : 0;
+    }
+
+    @Override
+    protected IModel<List<UserRequestForm>> getLatestAlerts() {
+        return new ListModel<UserRequestForm>() {
+
+            private static final long serialVersionUID = -2583290457773357445L;
+
+            @Override
+            public List<UserRequestForm> getObject() {
+                List<UserRequestForm> updatedApprovals;
+                if (SyncopeConsoleSession.get().owns(FlowableEntitlement.WORKFLOW_FORM_LIST)
+                        && SyncopeConsoleSession.get().owns(FlowableEntitlement.WORKFLOW_FORM_READ)) {
+
+                    updatedApprovals = restClient.getForms(1, MAX_SIZE, new SortParam<>("createTime", true));
+                } else {
+                    updatedApprovals = Collections.<UserRequestForm>emptyList();
+                }
+
+                return updatedApprovals;
+            }
+        };
+    }
+
+    @Override
+    protected AbstractLink getEventsLink(final String linkid) {
+        BookmarkablePageLink<Approvals> approvals = BookmarkablePageLinkBuilder.build(linkid, Approvals.class);
+        MetaDataRoleAuthorizationStrategy.authorize(approvals, WebPage.ENABLE, FlowableEntitlement.WORKFLOW_FORM_LIST);
+        return approvals;
+    }
+
+    @Override
+    protected Icon getIcon(final String iconid) {
+        return new Icon(iconid,
+                FontAwesomeIconTypeBuilder.on(FontAwesomeIconTypeBuilder.FontAwesomeGraphic.handshake_o).build());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/app-cfg.js
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/app-cfg.js b/ext/flowable/client-console/src/main/resources/app-cfg.js
new file mode 100644
index 0000000..15ec23a
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/app-cfg.js
@@ -0,0 +1,25 @@
+/* Copyright 2005-2015 Alfresco Software, Ltd.
+ * 
+ * Licensed 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.
+ */
+
+'use strict';
+
+var FLOWABLE = FLOWABLE || {};
+
+FLOWABLE.CONFIG = {
+	'onPremise' : true,
+    'contextRoot': window.location.toString().substr(0, window.location.toString().indexOf('/flowable-modeler')) + '/flowable-modeler',
+    'webContextRoot': window.location.toString().substr(0, window.location.toString().indexOf('/flowable-modeler') + '/flowable-modeler'),
+	'datesLocalization' : false
+};

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval.html
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval.html b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval.html
new file mode 100644
index 0000000..64805b9
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval.html
@@ -0,0 +1,31 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div wicket:id="propView">
+      <span wicket:id="value">[value]</span>
+    </div>
+
+    <div style="margin: 20px 0">
+      <a href="#" alt="user details" class="btn btn-primary btn-circle btn-lg" wicket:id="userDetails" wicket:message="title:userDetails">
+        <i class="glyphicon glyphicon-eye-open"></i>
+      </a>
+    </div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval.properties
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval.properties
new file mode 100644
index 0000000..450ff50
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval.properties
@@ -0,0 +1,18 @@
+# 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.
+userDetails=User details
+userForm=Edit User

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalDetails.html
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalDetails.html b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalDetails.html
new file mode 100644
index 0000000..1de2361
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalDetails.html
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <span wicket:id="wizard"/>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.html
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.html b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.html
new file mode 100644
index 0000000..5d5d129
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.html
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <span wicket:id="approval">[APPROVAL]</span>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.properties
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.properties
new file mode 100644
index 0000000..941c896
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.properties
@@ -0,0 +1,17 @@
+# 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.
+approval.details=Approval details

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_it.properties
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_it.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_it.properties
new file mode 100644
index 0000000..941c896
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_it.properties
@@ -0,0 +1,17 @@
+# 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.
+approval.details=Approval details

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_ja.properties
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_ja.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_ja.properties
new file mode 100644
index 0000000..84106b9
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_ja.properties
@@ -0,0 +1,17 @@
+# 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.
+approval.details=\u627f\u8a8d\u8a73\u7d30

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_pt_BR.properties
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_pt_BR.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_pt_BR.properties
new file mode 100644
index 0000000..941c896
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_pt_BR.properties
@@ -0,0 +1,17 @@
+# 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.
+approval.details=Approval details

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_ru.properties
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_ru.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_ru.properties
new file mode 100644
index 0000000..941c896
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal_ru.properties
@@ -0,0 +1,17 @@
+# 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.
+approval.details=Approval details

http://git-wip-us.apache.org/repos/asf/syncope/blob/66b3a04e/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval_it.properties
----------------------------------------------------------------------
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval_it.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval_it.properties
new file mode 100644
index 0000000..92c475d
--- /dev/null
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/approvals/Approval_it.properties
@@ -0,0 +1,18 @@
+# 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.
+userDetails=Dettagli utente
+userForm=Modifica utente