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 2017/07/13 08:53:38 UTC

[1/4] syncope git commit: Connector and Resource configuration versioning in Admin Console for SYNCOPE-1145

Repository: syncope
Updated Branches:
  refs/heads/2_0_X b683a7683 -> 5a07d7e91
  refs/heads/master 13f9e9b9b -> 8f7a7be70


http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.html
new file mode 100644
index 0000000..fb08214
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.html
@@ -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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:head>
+    <link rel="stylesheet" type="text/css" href="webjars/codemirror/${codemirror.version}/lib/codemirror.css"/>
+    <link rel="stylesheet" type="text/css" href="webjars/codemirror/${codemirror.version}/addon/merge/merge.css"/>
+
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/lib/codemirror.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/mode/javascript/javascript.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/display/autorefresh.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/search.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/searchcursor.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/edit/matchbrackets.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/edit/closebrackets.js"></script>
+    <script type="text/javascript" src="webjars/google-diff-match-patch/${googlediffmatchpath.version}/diff_match_patch.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/merge/merge.js"></script>
+
+    <style>
+      .w_content_3 {
+        padding: 0;
+        color: #333333;
+        font-family: Verdana,Tahoma,sans-serif;
+        font-size: 100%;
+        border: 1px solid #BBBBBB;
+        padding: 1%;
+      }
+
+      /* enlarge modal container for better viewing */
+      .wrapper div#outer.modal-lg {
+        max-width: 1400px;
+      }
+      .wrapper .modal-content .modal-body {
+        max-height: 80vh;
+      }
+      #jsonDiffEditorInfoDefForm {
+        height: 550px;
+      }
+      #jsonDiffEditorInfoDefForm .CodeMirror-merge,
+      #jsonDiffEditorInfoDefForm .CodeMirror-merge-pane,
+      #jsonDiffEditorInfoDefForm .CodeMirror {
+        height: 100%;
+      }
+    </style>
+  </wicket:head>
+  <wicket:panel>
+    <div style="padding: 1%;">
+      <textarea wicket:id="jsonEditorInfo1" id="consoleLayoutInfo" name="jsonEditorInfo1" style="display: none;">
+      </textarea>
+      <textarea wicket:id="jsonEditorInfo2" id="consoleLayoutInfo" name="jsonEditorInfo2" style="display: none;">
+      </textarea> 
+      <div class="w_content_3" id="jsonDiffEditorInfoDefForm">
+
+      </div>
+    </div>
+  </wicket:panel>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonEditorPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonEditorPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonEditorPanel.html
index e7b558f..82d232e 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonEditorPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonEditorPanel.html
@@ -41,12 +41,26 @@ under the License.
         border: 1px solid #BBBBBB;
         padding: 1%;
       }
+
+      /* enlarge modal container for better viewing */
+      .wrapper div#outer.modal-lg {
+        max-width: 1400px;
+      }
+      .wrapper .modal-content .modal-body {
+        max-height: 80vh;
+      }
+      #jsonEditorInfoDefForm .CodeMirror {
+        height: 100%;
+      }
+      #jsonEditorInfoDefForm {
+        height: 550px;
+      }
     </style>
   </wicket:head>
   <wicket:panel>
     <div style="padding: 1%;">
       <div class="w_content_3" id="jsonEditorInfoDefForm">
-        <textarea wicket:id="jsonEditorInfo" id="consoleLayoutInfo" name="jsonEditorInfo" style="width: 100%; height: 350px;">
+        <textarea wicket:id="jsonEditorInfo" id="consoleLayoutInfo" name="jsonEditorInfo" style="width: 100%; height: 550px;">
         </textarea>
       </div>
     </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 4aa3a1f..b0fc95c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -424,6 +424,7 @@ under the License.
     <ionicons.version>2.0.1</ionicons.version>
     <highlightjs.version>9.8.0</highlightjs.version>
     <codemirror.version>5.24.2</codemirror.version>
+    <googlediffmatchpath.version>20121119-1</googlediffmatchpath.version>
     <jsplumb.version>2.0.7</jsplumb.version>
     
     <wicket.version>7.7.0</wicket.version>
@@ -1387,6 +1388,11 @@ under the License.
       </dependency>
       <dependency>
         <groupId>org.webjars</groupId>
+        <artifactId>google-diff-match-patch</artifactId>
+        <version>${googlediffmatchpath.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.webjars</groupId>
         <artifactId>jsplumb</artifactId>
         <version>${jsplumb.version}</version>
       </dependency>


[4/4] syncope git commit: Connector and Resource configuration versioning in Admin Console for SYNCOPE-1145 - This closes #50

Posted by il...@apache.org.
Connector and Resource configuration versioning in Admin Console for SYNCOPE-1145 - This closes #50


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/8f7a7be7
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/8f7a7be7
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/8f7a7be7

Branch: refs/heads/master
Commit: 8f7a7be70af49967733eafa6a2412f7763009064
Parents: 13f9e9b
Author: Matteo Alessandroni <ma...@tirasa.net>
Authored: Tue Jul 11 12:40:26 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Jul 13 10:53:08 2017 +0200

----------------------------------------------------------------------
 client/console/pom.xml                          |   4 +
 .../client/console/commons/Constants.java       |   4 +
 .../ConnInstanceHistoryConfDirectoryPanel.java  | 234 ++++++++++++++++
 .../console/panels/HistoryConfDetails.java      | 265 +++++++++++++++++++
 .../client/console/panels/HistoryConfList.java  |  74 ++++++
 .../ResourceHistoryConfDirectoryPanel.java      | 237 +++++++++++++++++
 .../rest/ConnectorHistoryRestClient.java        |  50 ++++
 .../console/rest/ResourceHistoryRestClient.java |  50 ++++
 .../console/topology/TopologyTogglePanel.java   |  64 ++++-
 .../wicket/markup/html/form/ActionLink.java     |   3 +-
 .../wicket/markup/html/form/JsonDiffPanel.java  |  69 +++++
 .../console/panels/HistoryConfDetails.html      |  29 ++
 .../panels/HistoryConfDetails.properties        |  23 ++
 .../panels/HistoryConfDetails_it.properties     |  23 ++
 .../panels/HistoryConfDetails_pt_BR.properties  |  23 ++
 .../panels/HistoryConfDetails_ru.properties     |  23 ++
 .../client/console/panels/HistoryConfList.html  |  23 ++
 .../console/panels/HistoryConfList.properties   |  21 ++
 .../panels/HistoryConfList_it.properties        |  21 ++
 .../panels/HistoryConfList_pt_BR.properties     |  21 ++
 .../panels/HistoryConfList_ru.properties        |  21 ++
 .../console/topology/TopologyTogglePanel.html   |   6 +
 .../topology/TopologyTogglePanel.properties     |   2 +
 .../topology/TopologyTogglePanel_it.properties  |   2 +
 .../TopologyTogglePanel_pt_BR.properties        |   2 +
 .../topology/TopologyTogglePanel_ru.properties  |   2 +
 .../markup/html/form/ActionPanel.properties     |   4 +
 .../markup/html/form/ActionPanel_it.properties  |   4 +
 .../html/form/ActionPanel_pt_BR.properties      |   4 +
 .../markup/html/form/ActionPanel_ru.properties  |   4 +
 .../wicket/markup/html/form/JsonDiffPanel.html  |  72 +++++
 .../markup/html/form/JsonEditorPanel.html       |  16 +-
 pom.xml                                         |   6 +
 33 files changed, 1402 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/pom.xml
----------------------------------------------------------------------
diff --git a/client/console/pom.xml b/client/console/pom.xml
index f8ec4dc..68eb818 100644
--- a/client/console/pom.xml
+++ b/client/console/pom.xml
@@ -133,6 +133,10 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.webjars</groupId>
+      <artifactId>google-diff-match-patch</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars</groupId>
       <artifactId>jsplumb</artifactId>
     </dependency>
     <dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
index 24109d2..dc2d260 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
@@ -127,6 +127,10 @@ public final class Constants {
 
     public static final String PREF_PROPAGATION_TASKS_PAGINATOR_ROWS = "proagationtasks.paginator.rows";
 
+    public static final String PREF_CONNECTOR_HISTORY_CONF_PAGINATOR_ROWS = "connectorhistoryconf.paginator.rows";
+
+    public static final String PREF_RESOURCE_HISTORY_CONF_PAGINATOR_ROWS = "resourcehistoryconf.paginator.rows";
+
     public static final String PREF_REPORT_TASKS_PAGINATOR_ROWS = "report.paginator.rows";
 
     public static final String PREF_REPORTLET_TASKS_PAGINATOR_ROWS = "reportlet.paginator.rows";

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnInstanceHistoryConfDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnInstanceHistoryConfDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnInstanceHistoryConfDirectoryPanel.java
new file mode 100644
index 0000000..66f821d
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnInstanceHistoryConfDirectoryPanel.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.ArrayList;
+import java.util.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.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.ConnInstanceHistoryConfDirectoryPanel.CHConfProvider;
+import org.apache.syncope.client.console.rest.ConnectorHistoryRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
+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.ActionLink.ActionType;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.IEvent;
+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.StringResourceModel;
+
+/**
+ * List all connector configuration history instances for the selected connector.
+ */
+public abstract class ConnInstanceHistoryConfDirectoryPanel extends DirectoryPanel<
+        ConnInstanceHistoryConfTO, ConnInstanceHistoryConfTO, CHConfProvider, ConnectorHistoryRestClient>
+        implements ModalPanel {
+
+    private static final long serialVersionUID = 4984337552918213290L;
+
+    protected final BaseModal<?> baseModal;
+
+    private final MultilevelPanel multiLevelPanelRef;
+
+    private final String entityKey;
+
+    protected ConnInstanceHistoryConfDirectoryPanel(
+            final BaseModal<?> baseModal,
+            final MultilevelPanel multiLevelPanelRef,
+            final String entityKey,
+            final PageReference pageRef) {
+
+        super(MultilevelPanel.FIRST_LEVEL_ID, pageRef, false);
+
+        this.baseModal = baseModal;
+        this.multiLevelPanelRef = multiLevelPanelRef;
+        restClient = new ConnectorHistoryRestClient();
+        setShowResultPage(false);
+
+        this.entityKey = entityKey;
+        initResultTable();
+    }
+
+    @Override
+    protected List<IColumn<ConnInstanceHistoryConfTO, String>> getColumns() {
+        final List<IColumn<ConnInstanceHistoryConfTO, String>> columns = new ArrayList<>();
+
+        columns.add(new KeyPropertyColumn<ConnInstanceHistoryConfTO>(
+                new StringResourceModel("key", this), "key"));
+
+        columns.add(new PropertyColumn<ConnInstanceHistoryConfTO, String>(new StringResourceModel(
+                "creator", this), "creator", "creator"));
+
+        columns.add(new DatePropertyColumn<ConnInstanceHistoryConfTO>(
+                new StringResourceModel("creation", this), "creation", "creation"));
+
+        return columns;
+    }
+
+    @Override
+    public ActionsPanel<ConnInstanceHistoryConfTO> getActions(final IModel<ConnInstanceHistoryConfTO> model) {
+        final ActionsPanel<ConnInstanceHistoryConfTO> panel = super.getActions(model);
+        final ConnInstanceHistoryConfTO connHistoryConfTO = model.getObject();
+
+        // -- view
+        panel.add(new ActionLink<ConnInstanceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -6745431735457245600L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ConnInstanceHistoryConfTO modelObject) {
+
+                ConnInstanceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                viewConfiguration(modelObject, target);
+                target.add(modal);
+            }
+        }, ActionLink.ActionType.VIEW, StandardEntitlement.CONNECTOR_HISTORY_LIST);
+
+        // -- restore
+        panel.add(new ActionLink<ConnInstanceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -6745431735457245600L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ConnInstanceHistoryConfTO modelObject) {
+                try {
+                    restClient.restore(modelObject.getKey());
+                    ConnInstanceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While restoring {}", connHistoryConfTO.getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.RESTORE, StandardEntitlement.CONNECTOR_HISTORY_RESTORE);
+
+        // -- delete
+        panel.add(new ActionLink<ConnInstanceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -6745431735457245600L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ConnInstanceHistoryConfTO modelObject) {
+                try {
+                    restClient.delete(modelObject.getKey());
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                    ConnInstanceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting {}", connHistoryConfTO.getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, StandardEntitlement.CONNECTOR_HISTORY_DELETE, true);
+
+        return panel;
+    }
+
+    @Override
+    protected Collection<ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_CONNECTOR_HISTORY_CONF_PAGINATOR_ROWS;
+    }
+
+    protected abstract void viewConfiguration(ConnInstanceHistoryConfTO connHistoryTO,
+            final AjaxRequestTarget target);
+
+    @Override
+    protected void resultTableCustomChanges(
+            final AjaxDataTablePanel.Builder<ConnInstanceHistoryConfTO, String> resultTableBuilder) {
+        resultTableBuilder.setMultiLevelPanel(baseModal, multiLevelPanelRef);
+    }
+
+    @Override
+    protected CHConfProvider dataProvider() {
+        return new CHConfProvider(rows);
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        super.onEvent(event);
+        if (event.getPayload() instanceof ExitEvent && modal != null) {
+            final AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget();
+            baseModal.show(false);
+            baseModal.close(target);
+        }
+    }
+
+    protected class CHConfProvider extends DirectoryDataProvider<ConnInstanceHistoryConfTO> {
+
+        private static final long serialVersionUID = -4402560904215049574L;
+
+        private final SortableDataProviderComparator<ConnInstanceHistoryConfTO> comparator;
+
+        public CHConfProvider(final int paginatorRows) {
+            super(paginatorRows);
+
+            setSort("creation", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<ConnInstanceHistoryConfTO> iterator(final long first, final long count) {
+            final List<ConnInstanceHistoryConfTO> configurations = restClient.list(entityKey);
+
+            Collections.sort(configurations, getComparator());
+            return configurations.iterator();
+        }
+
+        public SortableDataProviderComparator<ConnInstanceHistoryConfTO> getComparator() {
+            return comparator;
+        }
+
+        @Override
+        public long size() {
+            return restClient.list(entityKey).size();
+        }
+
+        @Override
+        public IModel<ConnInstanceHistoryConfTO> model(final ConnInstanceHistoryConfTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfDetails.java
new file mode 100644
index 0000000..aeb088b
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfDetails.java
@@ -0,0 +1,265 @@
+/*
+ * 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 com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.client.console.rest.ResourceRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.JsonDiffPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.JsonEditorPanel;
+import org.apache.syncope.common.lib.to.AbstractHistoryConf;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.PropertyModel;
+
+public class HistoryConfDetails<T extends AbstractHistoryConf> extends MultilevelPanel.SecondLevel {
+
+    private static final long serialVersionUID = -7400543686272100483L;
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    private final T selectedHistoryConfTO;
+
+    private final List<T> availableHistoryConfTOs;
+
+    private AbstractModalPanel<String> jsonPanel;
+
+    public HistoryConfDetails(final BaseModal<?> baseModal, final T selectedHistoryConfTO,
+            final PageReference pageRef, final List<T> availableHistoryConfTOs) {
+        super();
+
+        // remove selected conf from list
+        CollectionUtils.filter(availableHistoryConfTOs, new Predicate<T>() {
+
+            @Override
+            public boolean evaluate(final T object) {
+                return !object.getKey().equals(selectedHistoryConfTO.getKey());
+            }
+        });
+        this.availableHistoryConfTOs = availableHistoryConfTOs;
+        this.selectedHistoryConfTO = selectedHistoryConfTO;
+
+        // add current conf to list
+        addCurrentInstanceConf();
+
+        Form<?> form = initDropdownDiffConfForm();
+        add(form);
+        if (availableHistoryConfTOs.isEmpty()) {
+            form.setVisible(false);
+        } else {
+            form.setVisible(true);
+        }
+
+        showConfigurationSinglePanel();
+    }
+
+    private void showConfigurationSinglePanel() {
+        Pair<String, String> info = getJSONInfo(selectedHistoryConfTO);
+
+        jsonPanel = new JsonEditorPanel(null, new PropertyModel<String>(info, "right"), true, null) {
+
+            private static final long serialVersionUID = -8927036362466990179L;
+
+            @Override
+            public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                modal.close(target);
+            }
+        };
+        jsonPanel.setOutputMarkupId(true);
+
+        addOrReplace(jsonPanel);
+    }
+
+    private void showConfigurationDiffPanel(final List<T> historyConfTOs) {
+        List<Pair<String, String>> infos = new ArrayList<>();
+        for (T historyConfTO : historyConfTOs) {
+            infos.add(getJSONInfo(historyConfTO));
+        }
+
+        jsonPanel = new JsonDiffPanel(null, new PropertyModel<String>(infos.get(0), "value"),
+                new PropertyModel<String>(infos.get(1), "value"), null) {
+
+            private static final long serialVersionUID = -8927036362466990179L;
+
+            @Override
+            public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                modal.close(target);
+            }
+        };
+
+        replace(jsonPanel);
+    }
+
+    private Pair<String, String> getJSONInfo(final T historyConfTO) {
+        Object conf = null; // selected configuration instance
+        String key = "";
+        if (historyConfTO instanceof ConnInstanceHistoryConfTO) {
+            ConnInstanceHistoryConfTO historyConf = ConnInstanceHistoryConfTO.class.cast(historyConfTO);
+            conf = historyConf.getConnInstanceTO();
+            key = historyConf.getKey();
+        } else if (historyConfTO instanceof ResourceHistoryConfTO) {
+            ResourceHistoryConfTO historyConf = ResourceHistoryConfTO.class.cast(historyConfTO);
+            conf = historyConf.getResourceTO();
+            key = historyConf.getKey();
+        }
+
+        String json = "";
+        try {
+            json = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(conf);
+        } catch (IOException ex) {
+            DirectoryPanel.LOG.error("Error converting objects to JSON", ex);
+        }
+
+        return Pair.of(key, json);
+    }
+
+    private Map<String, String> getDropdownNamesMap(final List<T> historyConfTOs) {
+        Map<String, String> historyConfMap = new HashMap<>();
+        if (selectedHistoryConfTO instanceof ConnInstanceHistoryConfTO) {
+            for (T historyConfValue : historyConfTOs) {
+                ConnInstanceHistoryConfTO historyConf = ConnInstanceHistoryConfTO.class.cast(historyConfValue);
+                historyConfMap.put(historyConf.getKey(),
+                        historyConf.getCreation() != null ? historyConf.getCreator() + " - " + SyncopeConsoleSession.
+                        get().getDateFormat().format(
+                                historyConf.getCreation()) + " - " + historyConf.getKey() : getString("current"));
+            }
+        } else if (selectedHistoryConfTO instanceof ResourceHistoryConfTO) {
+            for (T historyConfValue : historyConfTOs) {
+                ResourceHistoryConfTO historyConf = ResourceHistoryConfTO.class.cast(historyConfValue);
+                historyConfMap.put(historyConf.getKey(),
+                        historyConf.getCreation() != null ? historyConf.getCreator() + " - " + SyncopeConsoleSession.
+                        get().getDateFormat().format(
+                                historyConf.getCreation()) + " - " + historyConf.getKey() : getString("current"));
+            }
+        }
+        return historyConfMap;
+    }
+
+    private Form initDropdownDiffConfForm() {
+        final Form<T> form = new Form<>("form");
+        form.setModel(new CompoundPropertyModel<>(selectedHistoryConfTO));
+        form.setOutputMarkupId(true);
+
+        final Map<String, String> namesMap = getDropdownNamesMap(availableHistoryConfTOs);
+        List<String> keys = new ArrayList<>(namesMap.keySet());
+
+        final AjaxDropDownChoicePanel<String> dropdownElem = new AjaxDropDownChoicePanel<>(
+                "compareDropdown",
+                getString("compare"),
+                new PropertyModel<String>(selectedHistoryConfTO, "key"),
+                false);
+        dropdownElem.setChoices(keys);
+        dropdownElem.setChoiceRenderer(new IChoiceRenderer<String>() {
+
+            private static final long serialVersionUID = -6265603675261014912L;
+
+            @Override
+            public Object getDisplayValue(final String value) {
+                return namesMap.get(value) == null ? value : namesMap.get(value);
+            }
+
+            @Override
+            public String getIdValue(final String value, final int i) {
+                return value;
+            }
+
+            @Override
+            public String getObject(
+                    final String id, final IModel<? extends List<? extends String>> choices) {
+                return id;
+            }
+        });
+        dropdownElem.setNullValid(true);
+        dropdownElem.getField().add(new AjaxFormComponentUpdatingBehavior("onchange") {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                List<T> elemsToCompare = new ArrayList<>();
+                elemsToCompare.add(selectedHistoryConfTO);
+
+                final String selectedKey = dropdownElem.getModelObject();
+                if (selectedKey != null) {
+                    if (!selectedKey.isEmpty()) {
+                        T confToCompare = IterableUtils.find(availableHistoryConfTOs, new Predicate<T>() {
+
+                            @Override
+                            public boolean evaluate(final T object) {
+                                return object.getKey().equals(selectedKey);
+                            }
+                        });
+                        elemsToCompare.add(confToCompare);
+                        showConfigurationDiffPanel(elemsToCompare);
+                    } else {
+                        showConfigurationSinglePanel();
+                    }
+                }
+                target.add(jsonPanel);
+            }
+        });
+        form.add(dropdownElem);
+
+        return form;
+    }
+
+    @SuppressWarnings("unchecked")
+    private void addCurrentInstanceConf() {
+        T conf = null;
+
+        if (selectedHistoryConfTO instanceof ConnInstanceHistoryConfTO) {
+            ConnInstanceTO current = new ConnectorRestClient().read(
+                    ConnInstanceHistoryConfTO.class.cast(selectedHistoryConfTO).getConnInstanceTO().getKey());
+            conf = (T) new ConnInstanceHistoryConfTO();
+            ((ConnInstanceHistoryConfTO) conf).setConnInstanceTO(current);
+        } else if (selectedHistoryConfTO instanceof ResourceHistoryConfTO) {
+            ResourceTO currentRes = new ResourceRestClient().read(
+                    ResourceHistoryConfTO.class.cast(selectedHistoryConfTO).getResourceTO().getKey());
+            conf = (T) new ResourceHistoryConfTO();
+            ((ResourceHistoryConfTO) conf).setResourceTO(currentRes);
+        }
+
+        if (conf != null) {
+            conf.setCreator(selectedHistoryConfTO.getCreator());
+            conf.setKey("current");
+            availableHistoryConfTOs.add(conf);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfList.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfList.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfList.java
new file mode 100644
index 0000000..aa6880d
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfList.java
@@ -0,0 +1,74 @@
+/*
+ * 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 org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class HistoryConfList<T extends Serializable> extends Panel implements ModalPanel {
+
+    private static final long serialVersionUID = 2482507052037665907L;
+
+    public <T extends AnyTO> HistoryConfList(
+            final BaseModal<?> baseModal,
+            final String entityKey,
+            final PageReference pageReference,
+            final AbstractBaseBean modelObj) {
+
+        super(BaseModal.CONTENT_ID);
+
+        final MultilevelPanel mlp = new MultilevelPanel("history");
+
+        mlp.setFirstLevel(modelObj instanceof ConnInstanceTO
+                ? new ConnInstanceHistoryConfDirectoryPanel(baseModal, mlp, entityKey, pageReference) {
+
+            private static final long serialVersionUID = 1422189028000709100L;
+
+            @Override
+            protected void viewConfiguration(final ConnInstanceHistoryConfTO historyTO,
+                    final AjaxRequestTarget target) {
+                mlp.next(
+                        new StringResourceModel("history.diff.view", this).getObject(),
+                        new HistoryConfDetails<>(modal, historyTO, pageReference, restClient.list(entityKey)), target);
+            }
+        } : new ResourceHistoryConfDirectoryPanel(baseModal, mlp, entityKey, pageReference) {
+
+            private static final long serialVersionUID = 1422189028000709100L;
+
+            @Override
+            protected void viewConfiguration(final ResourceHistoryConfTO historyTO,
+                    final AjaxRequestTarget target) {
+                mlp.next(
+                        new StringResourceModel("history.diff.view", this).getObject(),
+                        new HistoryConfDetails<>(modal, historyTO, pageReference, restClient.list(entityKey)), target);
+            }
+        });
+
+        add(mlp);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceHistoryConfDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceHistoryConfDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceHistoryConfDirectoryPanel.java
new file mode 100644
index 0000000..ab95676
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceHistoryConfDirectoryPanel.java
@@ -0,0 +1,237 @@
+/*
+ * 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 static org.apache.syncope.client.console.panels.DirectoryPanel.LOG;
+
+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.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.ResourceHistoryConfDirectoryPanel.RHConfProvider;
+import org.apache.syncope.client.console.rest.ResourceHistoryRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
+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.ActionLink.ActionType;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.IEvent;
+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.StringResourceModel;
+
+/**
+ * List all resource configuration history instances for the selected resource.
+ */
+public abstract class ResourceHistoryConfDirectoryPanel extends DirectoryPanel<
+        ResourceHistoryConfTO, ResourceHistoryConfTO, RHConfProvider, ResourceHistoryRestClient>
+        implements ModalPanel {
+
+    private static final long serialVersionUID = 7636531280703026376L;
+
+    protected final BaseModal<?> baseModal;
+
+    private final MultilevelPanel multiLevelPanelRef;
+
+    private final String entityKey;
+
+    protected ResourceHistoryConfDirectoryPanel(
+            final BaseModal<?> baseModal,
+            final MultilevelPanel multiLevelPanelRef,
+            final String entityKey,
+            final PageReference pageRef) {
+
+        super(MultilevelPanel.FIRST_LEVEL_ID, pageRef, false);
+
+        this.baseModal = baseModal;
+        this.multiLevelPanelRef = multiLevelPanelRef;
+        restClient = new ResourceHistoryRestClient();
+        setShowResultPage(false);
+
+        this.entityKey = entityKey;
+        initResultTable();
+    }
+
+    @Override
+    protected List<IColumn<ResourceHistoryConfTO, String>> getColumns() {
+        final List<IColumn<ResourceHistoryConfTO, String>> columns = new ArrayList<>();
+
+        columns.add(new KeyPropertyColumn<ResourceHistoryConfTO>(
+                new StringResourceModel("key", this), "key"));
+
+        columns.add(new PropertyColumn<ResourceHistoryConfTO, String>(new StringResourceModel(
+                "creator", this), "creator", "creator"));
+
+        columns.add(new DatePropertyColumn<ResourceHistoryConfTO>(
+                new StringResourceModel("creation", this), "creation", "creation"));
+
+        return columns;
+    }
+
+    @Override
+    public ActionsPanel<ResourceHistoryConfTO> getActions(final IModel<ResourceHistoryConfTO> model) {
+        final ActionsPanel<ResourceHistoryConfTO> panel = super.getActions(model);
+        final ResourceHistoryConfTO resHistoryConfTO = model.getObject();
+
+        // -- view
+        panel.add(new ActionLink<ResourceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -3369924994540304232L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ResourceHistoryConfTO modelObject) {
+
+                ResourceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                viewConfiguration(modelObject, target);
+                target.add(modal);
+            }
+        }, ActionLink.ActionType.VIEW, StandardEntitlement.RESOURCE_HISTORY_LIST);
+
+        // -- restore
+        panel.add(new ActionLink<ResourceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -3369924994540304232L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ResourceHistoryConfTO modelObject) {
+                try {
+                    restClient.restore(modelObject.getKey());
+                    ResourceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While restoring {}", resHistoryConfTO.getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.RESTORE, StandardEntitlement.RESOURCE_HISTORY_RESTORE);
+
+        // -- delete
+        panel.add(new ActionLink<ResourceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -3369924994540304232L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ResourceHistoryConfTO modelObject) {
+                try {
+                    restClient.delete(modelObject.getKey());
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                    ResourceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting {}", resHistoryConfTO.getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, StandardEntitlement.RESOURCE_HISTORY_DELETE, true);
+
+        return panel;
+    }
+
+    @Override
+    protected Collection<ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_RESOURCE_HISTORY_CONF_PAGINATOR_ROWS;
+    }
+
+    protected abstract void viewConfiguration(ResourceHistoryConfTO resHistoryTO,
+            final AjaxRequestTarget target);
+
+    @Override
+    protected void resultTableCustomChanges(
+            final AjaxDataTablePanel.Builder<ResourceHistoryConfTO, String> resultTableBuilder) {
+        resultTableBuilder.setMultiLevelPanel(baseModal, multiLevelPanelRef);
+    }
+
+    @Override
+    protected RHConfProvider dataProvider() {
+        return new RHConfProvider(rows);
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        super.onEvent(event);
+        if (event.getPayload() instanceof WizardMgtPanel.ExitEvent && modal != null) {
+            final AjaxRequestTarget target = WizardMgtPanel.ExitEvent.class.cast(event.getPayload()).getTarget();
+            baseModal.show(false);
+            baseModal.close(target);
+        }
+    }
+
+    protected class RHConfProvider extends DirectoryDataProvider<ResourceHistoryConfTO> {
+
+        private static final long serialVersionUID = -5244315453787001412L;
+
+        private final SortableDataProviderComparator<ResourceHistoryConfTO> comparator;
+
+        public RHConfProvider(final int paginatorRows) {
+            super(paginatorRows);
+
+            setSort("creation", SortOrder.ASCENDING); // sort by 'creation' property
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<ResourceHistoryConfTO> iterator(final long first, final long count) {
+            final List<ResourceHistoryConfTO> configurations = restClient.list(entityKey);
+
+            Collections.sort(configurations, getComparator());
+            return configurations.iterator();
+        }
+
+        public SortableDataProviderComparator<ResourceHistoryConfTO> getComparator() {
+            return comparator;
+        }
+
+        @Override
+        public long size() {
+            return restClient.list(entityKey).size();
+        }
+
+        @Override
+        public IModel<ResourceHistoryConfTO> model(final ResourceHistoryConfTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorHistoryRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorHistoryRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorHistoryRestClient.java
new file mode 100644
index 0000000..9178569
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorHistoryRestClient.java
@@ -0,0 +1,50 @@
+/*
+ * 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.Collections;
+import java.util.List;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.rest.api.service.ConnectorHistoryService;
+
+/**
+ * Console client for invoking Rest Connector configuration history services.
+ */
+public class ConnectorHistoryRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -1917949374689773018L;
+
+    public List<ConnInstanceHistoryConfTO> list(final String key) {
+        List<ConnInstanceHistoryConfTO> connHistoryConfs = Collections.<ConnInstanceHistoryConfTO>emptyList();
+        try {
+            connHistoryConfs = getService(ConnectorHistoryService.class).list(key);
+        } catch (Exception e) {
+            LOG.error("While reading connector history configuration instances", e);
+        }
+        return connHistoryConfs;
+    }
+
+    public void delete(final String key) {
+        getService(ConnectorHistoryService.class).delete(key);
+    }
+
+    public void restore(final String key) {
+        getService(ConnectorHistoryService.class).restore(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/java/org/apache/syncope/client/console/rest/ResourceHistoryRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ResourceHistoryRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ResourceHistoryRestClient.java
new file mode 100644
index 0000000..9efb783
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ResourceHistoryRestClient.java
@@ -0,0 +1,50 @@
+/*
+ * 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.Collections;
+import java.util.List;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.syncope.common.rest.api.service.ResourceHistoryService;
+
+/**
+ * Console client for invoking Rest Resource configuration history services.
+ */
+public class ResourceHistoryRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -5722829010510310887L;
+
+    public List<ResourceHistoryConfTO> list(final String key) {
+        List<ResourceHistoryConfTO> resHistoryConfs = Collections.<ResourceHistoryConfTO>emptyList();
+        try {
+            resHistoryConfs = getService(ResourceHistoryService.class).list(key);
+        } catch (Exception e) {
+            LOG.error("While reading resource history configuration instances", e);
+        }
+        return resHistoryConfs;
+    }
+
+    public void delete(final String key) {
+        getService(ResourceHistoryService.class).delete(key);
+    }
+
+    public void restore(final String key) {
+        getService(ResourceHistoryService.class).restore(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java b/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
index 520bf1a..b02e9ca 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
@@ -25,6 +25,7 @@ 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.pages.BasePage;
+import org.apache.syncope.client.console.panels.HistoryConfList;
 import org.apache.syncope.client.console.panels.ConnObjects;
 import org.apache.syncope.client.console.wizards.resources.ConnectorWizardBuilder;
 import org.apache.syncope.client.console.wizards.resources.ResourceWizardBuilder;
@@ -75,6 +76,8 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
 
     protected final BaseModal<Serializable> provisionModal;
 
+    private final BaseModal<Serializable> historyModal;
+
     public TopologyTogglePanel(final String id, final PageReference pageRef) {
         super(id, "topologyTogglePanel", pageRef);
 
@@ -103,6 +106,10 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
         provisionModal.addSubmitButton();
         addOuterObject(provisionModal);
 
+        historyModal = new BaseModal<>("outer");
+        historyModal.size(Modal.Size.Large);
+        addOuterObject(historyModal);
+
         container = new WebMarkupContainer("container");
         container.setOutputMarkupPlaceholderTag(true);
         addInnerObject(container);
@@ -325,6 +332,32 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
         MetaDataRoleAuthorizationStrategy.authorize(edit, RENDER, StandardEntitlement.CONNECTOR_UPDATE);
         fragment.add(edit);
 
+        AjaxLink<String> history = new IndicatingAjaxLink<String>("history") {
+
+            private static final long serialVersionUID = -1876519166660008562L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                String connKey = String.class.cast(node.getKey());
+                final ConnInstanceTO modelObject = connectorRestClient.read(String.class.cast(node.getKey()));
+
+                target.add(historyModal.setContent(new HistoryConfList<>(historyModal, connKey, pageRef, modelObject)));
+
+                historyModal.header(
+                        new Model<>(MessageFormat.format(getString("connector.menu.history"), node.getDisplayName())));
+
+                historyModal.show(true);
+            }
+
+            @Override
+            public String getAjaxIndicatorMarkupId() {
+                return Constants.VEIL_INDICATOR_MARKUP_ID;
+            }
+
+        };
+        MetaDataRoleAuthorizationStrategy.authorize(history, RENDER, StandardEntitlement.CONNECTOR_HISTORY_LIST);
+        fragment.add(history);
+
         return fragment;
     }
 
@@ -369,8 +402,8 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
 
                 modal.header(new Model<>(MessageFormat.format(getString("resource.edit"), node.getKey())));
 
-                MetaDataRoleAuthorizationStrategy.
-                        authorize(modal.getForm(), RENDER, StandardEntitlement.RESOURCE_UPDATE);
+                MetaDataRoleAuthorizationStrategy.authorize(
+                        modal.getForm(), RENDER, StandardEntitlement.RESOURCE_UPDATE);
 
                 modal.show(true);
             }
@@ -522,6 +555,33 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
         MetaDataRoleAuthorizationStrategy.authorize(push, RENDER, StandardEntitlement.TASK_LIST);
         fragment.add(push);
 
+        AjaxLink<String> history = new IndicatingAjaxLink<String>("history") {
+
+            private static final long serialVersionUID = -1876519166660008562L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                String resourceKey = String.class.cast(node.getKey());
+                final ResourceTO modelObject = resourceRestClient.read(String.class.cast(node.getKey()));
+
+                target.add(historyModal.setContent(
+                        new HistoryConfList<>(historyModal, resourceKey, pageRef, modelObject)));
+
+                historyModal.header(
+                        new Model<>(MessageFormat.format(getString("resource.menu.history"), node.getDisplayName())));
+
+                historyModal.show(true);
+            }
+
+            @Override
+            public String getAjaxIndicatorMarkupId() {
+                return Constants.VEIL_INDICATOR_MARKUP_ID;
+            }
+
+        };
+        MetaDataRoleAuthorizationStrategy.authorize(history, RENDER, StandardEntitlement.RESOURCE_HISTORY_LIST);
+        fragment.add(history);
+
         return fragment;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
index c90ef37..3de74f8 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
@@ -75,9 +75,10 @@ public abstract class ActionLink<T extends Serializable> implements Serializable
         EXPORT_PDF("read"),
         EXPORT_RTF("read"),
         EXPORT_XML("read"),
+        RESTORE("import"),
         SUSPEND("update"),
         REACTIVATE("update"),
-        RELOAD("reload"),
+        RELOAD("import"),
         CHANGE_VIEW("changeView"),
         UNLINK("update"),
         LINK("update"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.java
new file mode 100644
index 0000000..8c5b80e
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.wicket.markup.html.form;
+
+import org.apache.syncope.client.console.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.OnLoadHeaderItem;
+import org.apache.wicket.markup.html.form.TextArea;
+import org.apache.wicket.model.IModel;
+
+public class JsonDiffPanel extends AbstractModalPanel<String> {
+
+    private static final long serialVersionUID = -5110368813584745668L;
+
+    private final IModel<String> first;
+
+    private final IModel<String> second;
+
+    public JsonDiffPanel(
+            final BaseModal<String> modal,
+            final IModel<String> first,
+            final IModel<String> second,
+            final PageReference pageRef) {
+
+        super(modal, pageRef);
+        this.second = second;
+        this.first = first;
+        TextArea<String> jsonEditorInfoDefArea1 = new TextArea<>("jsonEditorInfo1", this.first);
+        TextArea<String> jsonEditorInfoDefArea2 = new TextArea<>("jsonEditorInfo2", this.second);
+        jsonEditorInfoDefArea1.setMarkupId("jsonEditorInfo1").setOutputMarkupPlaceholderTag(true);
+        jsonEditorInfoDefArea2.setMarkupId("jsonEditorInfo2").setOutputMarkupPlaceholderTag(true);
+        add(jsonEditorInfoDefArea1);
+        add(jsonEditorInfoDefArea2);
+    }
+
+    @Override
+    public void renderHead(final IHeaderResponse response) {
+        super.renderHead(response);
+        response.render(OnLoadHeaderItem.forScript(
+                "CodeMirror.MergeView(document.getElementById('jsonDiffEditorInfoDefForm'), {"
+                + "  value: document.getElementById('jsonEditorInfo1').innerHTML, "
+                + "  orig: document.getElementById('jsonEditorInfo2').innerHTML, "
+                + "  lineNumbers: true, "
+                + "  mode: \"application/json\","
+                + "  highlightDifferences: true,"
+                + "  showDifferences: true,"
+                + "  readOnly: true,"
+                + "  revertButtons: false"
+                + "});"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.html
new file mode 100644
index 0000000..da89934
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.html
@@ -0,0 +1,29 @@
+<!--
+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>
+
+    <form wicket:id="form">
+      <span wicket:id="compareDropdown"></span>
+    </form>
+
+    <div wicket:id="content"></div>
+
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.properties
new file mode 100644
index 0000000..0abe803
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.properties
@@ -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.
+creation=Creation date
+creator=Creator
+entityKey=Local ID
+history.view=History view
+resource=Resource
+compare=Compare with
+current=Current

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_it.properties
new file mode 100644
index 0000000..bac0d9c
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_it.properties
@@ -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.
+creation=Data di creazione
+creator=Creatore
+entityKey=ID locale
+history.view=Configurazione
+resource=Risorsa
+compare=Confronta con
+current=Corrente

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_pt_BR.properties
new file mode 100644
index 0000000..192a559
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_pt_BR.properties
@@ -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.
+creation=Cria\u00e7\u00e3o
+creator=O Criador
+entityKey=ID local
+history.view=Configura\u00e7\u00e3o
+resource=Recurso
+compare=Compare com
+current=Atual

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_ru.properties
new file mode 100644
index 0000000..7b17b31
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_ru.properties
@@ -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.
+creation=\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435
+creator=\u0442\u0432\u043e\u0440\u0435\u0446
+entityKey=\u041c\u0435\u0441\u0442\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440
+history.view=\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f
+resource=\u0420\u0435\u0441\u0443\u0440\u0441
+compare=\u0421\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u0441
+current=\u0422\u0435\u043a\u0443\u0449\u0438\u0439

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.html
new file mode 100644
index 0000000..1cae5af
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.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="history">[HISTORY]</span>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.properties
new file mode 100644
index 0000000..5546931
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.properties
@@ -0,0 +1,21 @@
+# 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.
+creation=Creation date
+creator=Creator
+entityKey=Local ID
+history.diff.view=Configuration
+resource=Resource

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_it.properties
new file mode 100644
index 0000000..88c8bb3
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_it.properties
@@ -0,0 +1,21 @@
+# 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.
+creation=Data di creazione
+creator=Creatore
+entityKey=ID locale
+history.diff.view=Configurazione
+resource=Risorsa

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_pt_BR.properties
new file mode 100644
index 0000000..813728d
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_pt_BR.properties
@@ -0,0 +1,21 @@
+# 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.
+creation=Cria\u00e7\u00e3o
+creator=O Criador
+entityKey=ID local
+history.diff.view=Configura\u00e7\u00e3o
+resource=Recurso

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_ru.properties
new file mode 100644
index 0000000..4f278d7
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_ru.properties
@@ -0,0 +1,21 @@
+# 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.
+creation=\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435
+creator=\u0442\u0432\u043e\u0440\u0435\u0446
+entityKey=\u041c\u0435\u0441\u0442\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440
+history.diff.view=\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f
+resource=\u0420\u0435\u0441\u0443\u0440\u0441

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html
index 5abef88..c596813 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html
@@ -37,6 +37,9 @@ under the License.
         <wicket:enclosure child="edit">
           <li><a href="#" wicket:id="edit"><i class="fa fa-pencil"></i><wicket:message key="connector.menu.edit"/></a></li>
         </wicket:enclosure>
+        <wicket:enclosure child="history">
+          <li><a href="#" wicket:id="history"><i class="fa fa-history"></i><wicket:message key="connector.menu.history"/></a></li>
+        </wicket:enclosure>
         <wicket:enclosure child="delete">
           <li><a href="#" wicket:id="delete"><i class="fa fa-minus"></i><wicket:message key="connector.menu.remove"/></a></li>
         </wicket:enclosure>
@@ -66,6 +69,9 @@ under the License.
         <wicket:enclosure child="status">
           <li><a href="#" wicket:id="status"><i class="fa fa-list-ul"></i><wicket:message key="resource.menu.provisioning.status"/></a></li>
         </wicket:enclosure>
+        <wicket:enclosure child="history">
+          <li><a href="#" wicket:id="history"><i class="fa fa-history"></i><wicket:message key="resource.menu.history"/></a></li>
+        </wicket:enclosure>
         <wicket:enclosure child="delete">
           <li><a href="#" wicket:id="delete"><i class="fa fa-minus"></i><wicket:message key="resource.menu.remove"/></a></li>
         </wicket:enclosure>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties
index 43df570..08c0e53 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties
@@ -19,6 +19,7 @@ connector.edit=Edit connector {0}
 connector.menu.add=Add new connector
 connector.menu.remove=Remove connector
 connector.menu.edit=Edit connector
+connector.menu.history=Configuration history
 
 resource.new=New resource
 resource.edit=Edit resource {0}
@@ -27,6 +28,7 @@ resource.menu.remove=Remove resource
 resource.menu.edit=Edit resource
 resource.menu.provision=Edit provision rules
 resource.menu.explore=Explore resource
+resource.menu.history=Configuration history
 
 task.custom.list=Custom tasks
 task.propagation.list=Propagation tasks {0}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties
index 90ed5ec..1493852 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties
@@ -19,6 +19,7 @@ connector.edit=Modifica connettore {0}
 connector.menu.add=Aggiungi nuovo connettore
 connector.menu.remove=Rimuovi connettore
 connector.menu.edit=Modifica connettore
+connector.menu.history=Storico delle configurazioni
 
 resource.new=Nuova risorsa
 resource.edit=Modifica risorsa {0}
@@ -27,6 +28,7 @@ resource.menu.remove=Rimuovi risorsa
 resource.menu.edit=Modifica risorsa
 resource.menu.provision=Modifica regole di provisioning
 resource.menu.explore=Esplora risorsa
+resource.menu.history=Storico delle configurazioni
 
 task.custom.list=Task personalizzati
 task.propagation.list=Task di propagazione {0}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties
index 6f2520e..d6f9756 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties
@@ -19,6 +19,7 @@ connector.edit=Alterar conector {0}
 connector.menu.add=Adicionar novo conector
 connector.menu.remove=Retire conector
 connector.menu.edit=Alterar conector
+connector.menu.history=Hist\u00f3rico de configura\u00e7\u00e3o
 
 resource.new=Novo recurso
 resource.edit=Alterar recurso {0}
@@ -27,6 +28,7 @@ resource.menu.remove=Retire recurso
 resource.menu.edit=Alterar recurso
 resource.menu.provision=Alterar regras de provision
 resource.menu.explore=Explorar recurso
+resource.menu.history=Hist\u00f3rico de configura\u00e7\u00e3o
 
 task.custom.list=Custom tasks
 task.propagation.list=Propagation tasks {0}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties
index 997f304..1106c65 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties
@@ -20,6 +20,7 @@ connector.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043e\u04
 connector.menu.add=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440
 connector.menu.remove=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440
 connector.menu.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440
+connector.menu.history=\u0418\u0441\u0442\u043e\u0440\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438
 
 resource.new=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441
 resource.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441 {0}
@@ -28,6 +29,7 @@ resource.menu.remove=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0440\u0435\u04
 resource.menu.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441
 resource.menu.provision=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f
 resource.menu.explore=\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u0440\u0435\u0441\u0443\u0440\u0441\u0430
+resource.menu.history=\u0418\u0441\u0442\u043e\u0440\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438
 
 task.custom.list=\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438
 task.propagation.list=\u0417\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 {0}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
index 3eff230..eaf9bda 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
@@ -146,6 +146,10 @@ export_xml.class=fa fa-file-excel-o
 export_xml.title=export as xml
 export_xml.alt=export as xml icon
 
+restore.class=fa fa-arrow-circle-down
+restore.title=restore
+restore.alt=restore icon
+
 suspend.class=glyphicon glyphicon-ban-circle
 suspend.title=suspend
 suspend.alt=suspend icon

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_it.properties
index e23ad28..0351275 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_it.properties
@@ -146,6 +146,10 @@ export_xml.class=fa fa-file-excel-o
 export_xml.title=export as xml
 export_xml.alt=export as xml icon
 
+restore.class=fa fa-arrow-circle-down
+restore.title=restore
+restore.alt=restore icon
+
 suspend.class=glyphicon glyphicon-ban-circle
 suspend.title=suspend
 suspend.alt=suspend icon

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_pt_BR.properties
index e23ad28..0351275 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_pt_BR.properties
@@ -146,6 +146,10 @@ export_xml.class=fa fa-file-excel-o
 export_xml.title=export as xml
 export_xml.alt=export as xml icon
 
+restore.class=fa fa-arrow-circle-down
+restore.title=restore
+restore.alt=restore icon
+
 suspend.class=glyphicon glyphicon-ban-circle
 suspend.title=suspend
 suspend.alt=suspend icon

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_ru.properties
index e23ad28..0351275 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_ru.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_ru.properties
@@ -146,6 +146,10 @@ export_xml.class=fa fa-file-excel-o
 export_xml.title=export as xml
 export_xml.alt=export as xml icon
 
+restore.class=fa fa-arrow-circle-down
+restore.title=restore
+restore.alt=restore icon
+
 suspend.class=glyphicon glyphicon-ban-circle
 suspend.title=suspend
 suspend.alt=suspend icon


[3/4] syncope git commit: Connector and Resource configuration versioning in Admin Console for SYNCOPE-1145 - This closes #50

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.html
new file mode 100644
index 0000000..fb08214
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.html
@@ -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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:head>
+    <link rel="stylesheet" type="text/css" href="webjars/codemirror/${codemirror.version}/lib/codemirror.css"/>
+    <link rel="stylesheet" type="text/css" href="webjars/codemirror/${codemirror.version}/addon/merge/merge.css"/>
+
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/lib/codemirror.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/mode/javascript/javascript.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/display/autorefresh.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/search.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/searchcursor.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/edit/matchbrackets.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/edit/closebrackets.js"></script>
+    <script type="text/javascript" src="webjars/google-diff-match-patch/${googlediffmatchpath.version}/diff_match_patch.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/merge/merge.js"></script>
+
+    <style>
+      .w_content_3 {
+        padding: 0;
+        color: #333333;
+        font-family: Verdana,Tahoma,sans-serif;
+        font-size: 100%;
+        border: 1px solid #BBBBBB;
+        padding: 1%;
+      }
+
+      /* enlarge modal container for better viewing */
+      .wrapper div#outer.modal-lg {
+        max-width: 1400px;
+      }
+      .wrapper .modal-content .modal-body {
+        max-height: 80vh;
+      }
+      #jsonDiffEditorInfoDefForm {
+        height: 550px;
+      }
+      #jsonDiffEditorInfoDefForm .CodeMirror-merge,
+      #jsonDiffEditorInfoDefForm .CodeMirror-merge-pane,
+      #jsonDiffEditorInfoDefForm .CodeMirror {
+        height: 100%;
+      }
+    </style>
+  </wicket:head>
+  <wicket:panel>
+    <div style="padding: 1%;">
+      <textarea wicket:id="jsonEditorInfo1" id="consoleLayoutInfo" name="jsonEditorInfo1" style="display: none;">
+      </textarea>
+      <textarea wicket:id="jsonEditorInfo2" id="consoleLayoutInfo" name="jsonEditorInfo2" style="display: none;">
+      </textarea> 
+      <div class="w_content_3" id="jsonDiffEditorInfoDefForm">
+
+      </div>
+    </div>
+  </wicket:panel>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonEditorPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonEditorPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonEditorPanel.html
index e7b558f..82d232e 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonEditorPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/JsonEditorPanel.html
@@ -41,12 +41,26 @@ under the License.
         border: 1px solid #BBBBBB;
         padding: 1%;
       }
+
+      /* enlarge modal container for better viewing */
+      .wrapper div#outer.modal-lg {
+        max-width: 1400px;
+      }
+      .wrapper .modal-content .modal-body {
+        max-height: 80vh;
+      }
+      #jsonEditorInfoDefForm .CodeMirror {
+        height: 100%;
+      }
+      #jsonEditorInfoDefForm {
+        height: 550px;
+      }
     </style>
   </wicket:head>
   <wicket:panel>
     <div style="padding: 1%;">
       <div class="w_content_3" id="jsonEditorInfoDefForm">
-        <textarea wicket:id="jsonEditorInfo" id="consoleLayoutInfo" name="jsonEditorInfo" style="width: 100%; height: 350px;">
+        <textarea wicket:id="jsonEditorInfo" id="consoleLayoutInfo" name="jsonEditorInfo" style="width: 100%; height: 550px;">
         </textarea>
       </div>
     </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8f7a7be7/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index fe0cd3f..ae48981 100644
--- a/pom.xml
+++ b/pom.xml
@@ -422,6 +422,7 @@ under the License.
     <ionicons.version>2.0.1</ionicons.version>
     <highlightjs.version>9.8.0</highlightjs.version>
     <codemirror.version>5.24.2</codemirror.version>
+    <googlediffmatchpath.version>20121119-1</googlediffmatchpath.version>
     <jsplumb.version>2.0.7</jsplumb.version>
     
     <wicket.version>7.7.0</wicket.version>
@@ -1332,6 +1333,11 @@ under the License.
       </dependency>
       <dependency>
         <groupId>org.webjars</groupId>
+        <artifactId>google-diff-match-patch</artifactId>
+        <version>${googlediffmatchpath.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.webjars</groupId>
         <artifactId>jsplumb</artifactId>
         <version>${jsplumb.version}</version>
       </dependency>


[2/4] syncope git commit: Connector and Resource configuration versioning in Admin Console for SYNCOPE-1145

Posted by il...@apache.org.
Connector and Resource configuration versioning in Admin Console for SYNCOPE-1145


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/5a07d7e9
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/5a07d7e9
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/5a07d7e9

Branch: refs/heads/2_0_X
Commit: 5a07d7e91d87b3fd730a1ddfa23b4ab715615b7d
Parents: b683a76
Author: Matteo Alessandroni <ma...@tirasa.net>
Authored: Tue Jul 11 12:40:26 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Jul 13 10:52:43 2017 +0200

----------------------------------------------------------------------
 client/console/pom.xml                          |   4 +
 .../client/console/commons/Constants.java       |   4 +
 .../ConnInstanceHistoryConfDirectoryPanel.java  | 234 ++++++++++++++++
 .../console/panels/HistoryConfDetails.java      | 265 +++++++++++++++++++
 .../client/console/panels/HistoryConfList.java  |  74 ++++++
 .../ResourceHistoryConfDirectoryPanel.java      | 237 +++++++++++++++++
 .../rest/ConnectorHistoryRestClient.java        |  50 ++++
 .../console/rest/ResourceHistoryRestClient.java |  50 ++++
 .../console/topology/TopologyTogglePanel.java   |  64 ++++-
 .../wicket/markup/html/form/ActionLink.java     |   3 +-
 .../wicket/markup/html/form/JsonDiffPanel.java  |  69 +++++
 .../console/panels/HistoryConfDetails.html      |  29 ++
 .../panels/HistoryConfDetails.properties        |  23 ++
 .../panels/HistoryConfDetails_it.properties     |  23 ++
 .../panels/HistoryConfDetails_pt_BR.properties  |  23 ++
 .../panels/HistoryConfDetails_ru.properties     |  23 ++
 .../client/console/panels/HistoryConfList.html  |  23 ++
 .../console/panels/HistoryConfList.properties   |  21 ++
 .../panels/HistoryConfList_it.properties        |  21 ++
 .../panels/HistoryConfList_pt_BR.properties     |  21 ++
 .../panels/HistoryConfList_ru.properties        |  21 ++
 .../console/topology/TopologyTogglePanel.html   |   6 +
 .../topology/TopologyTogglePanel.properties     |   2 +
 .../topology/TopologyTogglePanel_it.properties  |   2 +
 .../TopologyTogglePanel_pt_BR.properties        |   2 +
 .../topology/TopologyTogglePanel_ru.properties  |   2 +
 .../markup/html/form/ActionPanel.properties     |   4 +
 .../markup/html/form/ActionPanel_it.properties  |   4 +
 .../html/form/ActionPanel_pt_BR.properties      |   4 +
 .../markup/html/form/ActionPanel_ru.properties  |   4 +
 .../wicket/markup/html/form/JsonDiffPanel.html  |  72 +++++
 .../markup/html/form/JsonEditorPanel.html       |  16 +-
 pom.xml                                         |   6 +
 33 files changed, 1402 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/pom.xml
----------------------------------------------------------------------
diff --git a/client/console/pom.xml b/client/console/pom.xml
index c3e4b7b..228790d 100644
--- a/client/console/pom.xml
+++ b/client/console/pom.xml
@@ -133,6 +133,10 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.webjars</groupId>
+      <artifactId>google-diff-match-patch</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars</groupId>
       <artifactId>jsplumb</artifactId>
     </dependency>
     <dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
index 24109d2..dc2d260 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
@@ -127,6 +127,10 @@ public final class Constants {
 
     public static final String PREF_PROPAGATION_TASKS_PAGINATOR_ROWS = "proagationtasks.paginator.rows";
 
+    public static final String PREF_CONNECTOR_HISTORY_CONF_PAGINATOR_ROWS = "connectorhistoryconf.paginator.rows";
+
+    public static final String PREF_RESOURCE_HISTORY_CONF_PAGINATOR_ROWS = "resourcehistoryconf.paginator.rows";
+
     public static final String PREF_REPORT_TASKS_PAGINATOR_ROWS = "report.paginator.rows";
 
     public static final String PREF_REPORTLET_TASKS_PAGINATOR_ROWS = "reportlet.paginator.rows";

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnInstanceHistoryConfDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnInstanceHistoryConfDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnInstanceHistoryConfDirectoryPanel.java
new file mode 100644
index 0000000..66f821d
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnInstanceHistoryConfDirectoryPanel.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.ArrayList;
+import java.util.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.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.ConnInstanceHistoryConfDirectoryPanel.CHConfProvider;
+import org.apache.syncope.client.console.rest.ConnectorHistoryRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
+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.ActionLink.ActionType;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.IEvent;
+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.StringResourceModel;
+
+/**
+ * List all connector configuration history instances for the selected connector.
+ */
+public abstract class ConnInstanceHistoryConfDirectoryPanel extends DirectoryPanel<
+        ConnInstanceHistoryConfTO, ConnInstanceHistoryConfTO, CHConfProvider, ConnectorHistoryRestClient>
+        implements ModalPanel {
+
+    private static final long serialVersionUID = 4984337552918213290L;
+
+    protected final BaseModal<?> baseModal;
+
+    private final MultilevelPanel multiLevelPanelRef;
+
+    private final String entityKey;
+
+    protected ConnInstanceHistoryConfDirectoryPanel(
+            final BaseModal<?> baseModal,
+            final MultilevelPanel multiLevelPanelRef,
+            final String entityKey,
+            final PageReference pageRef) {
+
+        super(MultilevelPanel.FIRST_LEVEL_ID, pageRef, false);
+
+        this.baseModal = baseModal;
+        this.multiLevelPanelRef = multiLevelPanelRef;
+        restClient = new ConnectorHistoryRestClient();
+        setShowResultPage(false);
+
+        this.entityKey = entityKey;
+        initResultTable();
+    }
+
+    @Override
+    protected List<IColumn<ConnInstanceHistoryConfTO, String>> getColumns() {
+        final List<IColumn<ConnInstanceHistoryConfTO, String>> columns = new ArrayList<>();
+
+        columns.add(new KeyPropertyColumn<ConnInstanceHistoryConfTO>(
+                new StringResourceModel("key", this), "key"));
+
+        columns.add(new PropertyColumn<ConnInstanceHistoryConfTO, String>(new StringResourceModel(
+                "creator", this), "creator", "creator"));
+
+        columns.add(new DatePropertyColumn<ConnInstanceHistoryConfTO>(
+                new StringResourceModel("creation", this), "creation", "creation"));
+
+        return columns;
+    }
+
+    @Override
+    public ActionsPanel<ConnInstanceHistoryConfTO> getActions(final IModel<ConnInstanceHistoryConfTO> model) {
+        final ActionsPanel<ConnInstanceHistoryConfTO> panel = super.getActions(model);
+        final ConnInstanceHistoryConfTO connHistoryConfTO = model.getObject();
+
+        // -- view
+        panel.add(new ActionLink<ConnInstanceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -6745431735457245600L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ConnInstanceHistoryConfTO modelObject) {
+
+                ConnInstanceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                viewConfiguration(modelObject, target);
+                target.add(modal);
+            }
+        }, ActionLink.ActionType.VIEW, StandardEntitlement.CONNECTOR_HISTORY_LIST);
+
+        // -- restore
+        panel.add(new ActionLink<ConnInstanceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -6745431735457245600L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ConnInstanceHistoryConfTO modelObject) {
+                try {
+                    restClient.restore(modelObject.getKey());
+                    ConnInstanceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While restoring {}", connHistoryConfTO.getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.RESTORE, StandardEntitlement.CONNECTOR_HISTORY_RESTORE);
+
+        // -- delete
+        panel.add(new ActionLink<ConnInstanceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -6745431735457245600L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ConnInstanceHistoryConfTO modelObject) {
+                try {
+                    restClient.delete(modelObject.getKey());
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                    ConnInstanceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting {}", connHistoryConfTO.getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, StandardEntitlement.CONNECTOR_HISTORY_DELETE, true);
+
+        return panel;
+    }
+
+    @Override
+    protected Collection<ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_CONNECTOR_HISTORY_CONF_PAGINATOR_ROWS;
+    }
+
+    protected abstract void viewConfiguration(ConnInstanceHistoryConfTO connHistoryTO,
+            final AjaxRequestTarget target);
+
+    @Override
+    protected void resultTableCustomChanges(
+            final AjaxDataTablePanel.Builder<ConnInstanceHistoryConfTO, String> resultTableBuilder) {
+        resultTableBuilder.setMultiLevelPanel(baseModal, multiLevelPanelRef);
+    }
+
+    @Override
+    protected CHConfProvider dataProvider() {
+        return new CHConfProvider(rows);
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        super.onEvent(event);
+        if (event.getPayload() instanceof ExitEvent && modal != null) {
+            final AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget();
+            baseModal.show(false);
+            baseModal.close(target);
+        }
+    }
+
+    protected class CHConfProvider extends DirectoryDataProvider<ConnInstanceHistoryConfTO> {
+
+        private static final long serialVersionUID = -4402560904215049574L;
+
+        private final SortableDataProviderComparator<ConnInstanceHistoryConfTO> comparator;
+
+        public CHConfProvider(final int paginatorRows) {
+            super(paginatorRows);
+
+            setSort("creation", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<ConnInstanceHistoryConfTO> iterator(final long first, final long count) {
+            final List<ConnInstanceHistoryConfTO> configurations = restClient.list(entityKey);
+
+            Collections.sort(configurations, getComparator());
+            return configurations.iterator();
+        }
+
+        public SortableDataProviderComparator<ConnInstanceHistoryConfTO> getComparator() {
+            return comparator;
+        }
+
+        @Override
+        public long size() {
+            return restClient.list(entityKey).size();
+        }
+
+        @Override
+        public IModel<ConnInstanceHistoryConfTO> model(final ConnInstanceHistoryConfTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfDetails.java
new file mode 100644
index 0000000..aeb088b
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfDetails.java
@@ -0,0 +1,265 @@
+/*
+ * 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 com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.client.console.rest.ResourceRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.JsonDiffPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.JsonEditorPanel;
+import org.apache.syncope.common.lib.to.AbstractHistoryConf;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.PropertyModel;
+
+public class HistoryConfDetails<T extends AbstractHistoryConf> extends MultilevelPanel.SecondLevel {
+
+    private static final long serialVersionUID = -7400543686272100483L;
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    private final T selectedHistoryConfTO;
+
+    private final List<T> availableHistoryConfTOs;
+
+    private AbstractModalPanel<String> jsonPanel;
+
+    public HistoryConfDetails(final BaseModal<?> baseModal, final T selectedHistoryConfTO,
+            final PageReference pageRef, final List<T> availableHistoryConfTOs) {
+        super();
+
+        // remove selected conf from list
+        CollectionUtils.filter(availableHistoryConfTOs, new Predicate<T>() {
+
+            @Override
+            public boolean evaluate(final T object) {
+                return !object.getKey().equals(selectedHistoryConfTO.getKey());
+            }
+        });
+        this.availableHistoryConfTOs = availableHistoryConfTOs;
+        this.selectedHistoryConfTO = selectedHistoryConfTO;
+
+        // add current conf to list
+        addCurrentInstanceConf();
+
+        Form<?> form = initDropdownDiffConfForm();
+        add(form);
+        if (availableHistoryConfTOs.isEmpty()) {
+            form.setVisible(false);
+        } else {
+            form.setVisible(true);
+        }
+
+        showConfigurationSinglePanel();
+    }
+
+    private void showConfigurationSinglePanel() {
+        Pair<String, String> info = getJSONInfo(selectedHistoryConfTO);
+
+        jsonPanel = new JsonEditorPanel(null, new PropertyModel<String>(info, "right"), true, null) {
+
+            private static final long serialVersionUID = -8927036362466990179L;
+
+            @Override
+            public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                modal.close(target);
+            }
+        };
+        jsonPanel.setOutputMarkupId(true);
+
+        addOrReplace(jsonPanel);
+    }
+
+    private void showConfigurationDiffPanel(final List<T> historyConfTOs) {
+        List<Pair<String, String>> infos = new ArrayList<>();
+        for (T historyConfTO : historyConfTOs) {
+            infos.add(getJSONInfo(historyConfTO));
+        }
+
+        jsonPanel = new JsonDiffPanel(null, new PropertyModel<String>(infos.get(0), "value"),
+                new PropertyModel<String>(infos.get(1), "value"), null) {
+
+            private static final long serialVersionUID = -8927036362466990179L;
+
+            @Override
+            public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                modal.close(target);
+            }
+        };
+
+        replace(jsonPanel);
+    }
+
+    private Pair<String, String> getJSONInfo(final T historyConfTO) {
+        Object conf = null; // selected configuration instance
+        String key = "";
+        if (historyConfTO instanceof ConnInstanceHistoryConfTO) {
+            ConnInstanceHistoryConfTO historyConf = ConnInstanceHistoryConfTO.class.cast(historyConfTO);
+            conf = historyConf.getConnInstanceTO();
+            key = historyConf.getKey();
+        } else if (historyConfTO instanceof ResourceHistoryConfTO) {
+            ResourceHistoryConfTO historyConf = ResourceHistoryConfTO.class.cast(historyConfTO);
+            conf = historyConf.getResourceTO();
+            key = historyConf.getKey();
+        }
+
+        String json = "";
+        try {
+            json = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(conf);
+        } catch (IOException ex) {
+            DirectoryPanel.LOG.error("Error converting objects to JSON", ex);
+        }
+
+        return Pair.of(key, json);
+    }
+
+    private Map<String, String> getDropdownNamesMap(final List<T> historyConfTOs) {
+        Map<String, String> historyConfMap = new HashMap<>();
+        if (selectedHistoryConfTO instanceof ConnInstanceHistoryConfTO) {
+            for (T historyConfValue : historyConfTOs) {
+                ConnInstanceHistoryConfTO historyConf = ConnInstanceHistoryConfTO.class.cast(historyConfValue);
+                historyConfMap.put(historyConf.getKey(),
+                        historyConf.getCreation() != null ? historyConf.getCreator() + " - " + SyncopeConsoleSession.
+                        get().getDateFormat().format(
+                                historyConf.getCreation()) + " - " + historyConf.getKey() : getString("current"));
+            }
+        } else if (selectedHistoryConfTO instanceof ResourceHistoryConfTO) {
+            for (T historyConfValue : historyConfTOs) {
+                ResourceHistoryConfTO historyConf = ResourceHistoryConfTO.class.cast(historyConfValue);
+                historyConfMap.put(historyConf.getKey(),
+                        historyConf.getCreation() != null ? historyConf.getCreator() + " - " + SyncopeConsoleSession.
+                        get().getDateFormat().format(
+                                historyConf.getCreation()) + " - " + historyConf.getKey() : getString("current"));
+            }
+        }
+        return historyConfMap;
+    }
+
+    private Form initDropdownDiffConfForm() {
+        final Form<T> form = new Form<>("form");
+        form.setModel(new CompoundPropertyModel<>(selectedHistoryConfTO));
+        form.setOutputMarkupId(true);
+
+        final Map<String, String> namesMap = getDropdownNamesMap(availableHistoryConfTOs);
+        List<String> keys = new ArrayList<>(namesMap.keySet());
+
+        final AjaxDropDownChoicePanel<String> dropdownElem = new AjaxDropDownChoicePanel<>(
+                "compareDropdown",
+                getString("compare"),
+                new PropertyModel<String>(selectedHistoryConfTO, "key"),
+                false);
+        dropdownElem.setChoices(keys);
+        dropdownElem.setChoiceRenderer(new IChoiceRenderer<String>() {
+
+            private static final long serialVersionUID = -6265603675261014912L;
+
+            @Override
+            public Object getDisplayValue(final String value) {
+                return namesMap.get(value) == null ? value : namesMap.get(value);
+            }
+
+            @Override
+            public String getIdValue(final String value, final int i) {
+                return value;
+            }
+
+            @Override
+            public String getObject(
+                    final String id, final IModel<? extends List<? extends String>> choices) {
+                return id;
+            }
+        });
+        dropdownElem.setNullValid(true);
+        dropdownElem.getField().add(new AjaxFormComponentUpdatingBehavior("onchange") {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                List<T> elemsToCompare = new ArrayList<>();
+                elemsToCompare.add(selectedHistoryConfTO);
+
+                final String selectedKey = dropdownElem.getModelObject();
+                if (selectedKey != null) {
+                    if (!selectedKey.isEmpty()) {
+                        T confToCompare = IterableUtils.find(availableHistoryConfTOs, new Predicate<T>() {
+
+                            @Override
+                            public boolean evaluate(final T object) {
+                                return object.getKey().equals(selectedKey);
+                            }
+                        });
+                        elemsToCompare.add(confToCompare);
+                        showConfigurationDiffPanel(elemsToCompare);
+                    } else {
+                        showConfigurationSinglePanel();
+                    }
+                }
+                target.add(jsonPanel);
+            }
+        });
+        form.add(dropdownElem);
+
+        return form;
+    }
+
+    @SuppressWarnings("unchecked")
+    private void addCurrentInstanceConf() {
+        T conf = null;
+
+        if (selectedHistoryConfTO instanceof ConnInstanceHistoryConfTO) {
+            ConnInstanceTO current = new ConnectorRestClient().read(
+                    ConnInstanceHistoryConfTO.class.cast(selectedHistoryConfTO).getConnInstanceTO().getKey());
+            conf = (T) new ConnInstanceHistoryConfTO();
+            ((ConnInstanceHistoryConfTO) conf).setConnInstanceTO(current);
+        } else if (selectedHistoryConfTO instanceof ResourceHistoryConfTO) {
+            ResourceTO currentRes = new ResourceRestClient().read(
+                    ResourceHistoryConfTO.class.cast(selectedHistoryConfTO).getResourceTO().getKey());
+            conf = (T) new ResourceHistoryConfTO();
+            ((ResourceHistoryConfTO) conf).setResourceTO(currentRes);
+        }
+
+        if (conf != null) {
+            conf.setCreator(selectedHistoryConfTO.getCreator());
+            conf.setKey("current");
+            availableHistoryConfTOs.add(conf);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfList.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfList.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfList.java
new file mode 100644
index 0000000..aa6880d
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/HistoryConfList.java
@@ -0,0 +1,74 @@
+/*
+ * 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 org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class HistoryConfList<T extends Serializable> extends Panel implements ModalPanel {
+
+    private static final long serialVersionUID = 2482507052037665907L;
+
+    public <T extends AnyTO> HistoryConfList(
+            final BaseModal<?> baseModal,
+            final String entityKey,
+            final PageReference pageReference,
+            final AbstractBaseBean modelObj) {
+
+        super(BaseModal.CONTENT_ID);
+
+        final MultilevelPanel mlp = new MultilevelPanel("history");
+
+        mlp.setFirstLevel(modelObj instanceof ConnInstanceTO
+                ? new ConnInstanceHistoryConfDirectoryPanel(baseModal, mlp, entityKey, pageReference) {
+
+            private static final long serialVersionUID = 1422189028000709100L;
+
+            @Override
+            protected void viewConfiguration(final ConnInstanceHistoryConfTO historyTO,
+                    final AjaxRequestTarget target) {
+                mlp.next(
+                        new StringResourceModel("history.diff.view", this).getObject(),
+                        new HistoryConfDetails<>(modal, historyTO, pageReference, restClient.list(entityKey)), target);
+            }
+        } : new ResourceHistoryConfDirectoryPanel(baseModal, mlp, entityKey, pageReference) {
+
+            private static final long serialVersionUID = 1422189028000709100L;
+
+            @Override
+            protected void viewConfiguration(final ResourceHistoryConfTO historyTO,
+                    final AjaxRequestTarget target) {
+                mlp.next(
+                        new StringResourceModel("history.diff.view", this).getObject(),
+                        new HistoryConfDetails<>(modal, historyTO, pageReference, restClient.list(entityKey)), target);
+            }
+        });
+
+        add(mlp);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceHistoryConfDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceHistoryConfDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceHistoryConfDirectoryPanel.java
new file mode 100644
index 0000000..ab95676
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceHistoryConfDirectoryPanel.java
@@ -0,0 +1,237 @@
+/*
+ * 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 static org.apache.syncope.client.console.panels.DirectoryPanel.LOG;
+
+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.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.ResourceHistoryConfDirectoryPanel.RHConfProvider;
+import org.apache.syncope.client.console.rest.ResourceHistoryRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
+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.ActionLink.ActionType;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.IEvent;
+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.StringResourceModel;
+
+/**
+ * List all resource configuration history instances for the selected resource.
+ */
+public abstract class ResourceHistoryConfDirectoryPanel extends DirectoryPanel<
+        ResourceHistoryConfTO, ResourceHistoryConfTO, RHConfProvider, ResourceHistoryRestClient>
+        implements ModalPanel {
+
+    private static final long serialVersionUID = 7636531280703026376L;
+
+    protected final BaseModal<?> baseModal;
+
+    private final MultilevelPanel multiLevelPanelRef;
+
+    private final String entityKey;
+
+    protected ResourceHistoryConfDirectoryPanel(
+            final BaseModal<?> baseModal,
+            final MultilevelPanel multiLevelPanelRef,
+            final String entityKey,
+            final PageReference pageRef) {
+
+        super(MultilevelPanel.FIRST_LEVEL_ID, pageRef, false);
+
+        this.baseModal = baseModal;
+        this.multiLevelPanelRef = multiLevelPanelRef;
+        restClient = new ResourceHistoryRestClient();
+        setShowResultPage(false);
+
+        this.entityKey = entityKey;
+        initResultTable();
+    }
+
+    @Override
+    protected List<IColumn<ResourceHistoryConfTO, String>> getColumns() {
+        final List<IColumn<ResourceHistoryConfTO, String>> columns = new ArrayList<>();
+
+        columns.add(new KeyPropertyColumn<ResourceHistoryConfTO>(
+                new StringResourceModel("key", this), "key"));
+
+        columns.add(new PropertyColumn<ResourceHistoryConfTO, String>(new StringResourceModel(
+                "creator", this), "creator", "creator"));
+
+        columns.add(new DatePropertyColumn<ResourceHistoryConfTO>(
+                new StringResourceModel("creation", this), "creation", "creation"));
+
+        return columns;
+    }
+
+    @Override
+    public ActionsPanel<ResourceHistoryConfTO> getActions(final IModel<ResourceHistoryConfTO> model) {
+        final ActionsPanel<ResourceHistoryConfTO> panel = super.getActions(model);
+        final ResourceHistoryConfTO resHistoryConfTO = model.getObject();
+
+        // -- view
+        panel.add(new ActionLink<ResourceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -3369924994540304232L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ResourceHistoryConfTO modelObject) {
+
+                ResourceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                viewConfiguration(modelObject, target);
+                target.add(modal);
+            }
+        }, ActionLink.ActionType.VIEW, StandardEntitlement.RESOURCE_HISTORY_LIST);
+
+        // -- restore
+        panel.add(new ActionLink<ResourceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -3369924994540304232L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ResourceHistoryConfTO modelObject) {
+                try {
+                    restClient.restore(modelObject.getKey());
+                    ResourceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While restoring {}", resHistoryConfTO.getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.RESTORE, StandardEntitlement.RESOURCE_HISTORY_RESTORE);
+
+        // -- delete
+        panel.add(new ActionLink<ResourceHistoryConfTO>() {
+
+            private static final long serialVersionUID = -3369924994540304232L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ResourceHistoryConfTO modelObject) {
+                try {
+                    restClient.delete(modelObject.getKey());
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                    ResourceHistoryConfDirectoryPanel.this.getTogglePanel().close(target);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting {}", resHistoryConfTO.getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, StandardEntitlement.RESOURCE_HISTORY_DELETE, true);
+
+        return panel;
+    }
+
+    @Override
+    protected Collection<ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_RESOURCE_HISTORY_CONF_PAGINATOR_ROWS;
+    }
+
+    protected abstract void viewConfiguration(ResourceHistoryConfTO resHistoryTO,
+            final AjaxRequestTarget target);
+
+    @Override
+    protected void resultTableCustomChanges(
+            final AjaxDataTablePanel.Builder<ResourceHistoryConfTO, String> resultTableBuilder) {
+        resultTableBuilder.setMultiLevelPanel(baseModal, multiLevelPanelRef);
+    }
+
+    @Override
+    protected RHConfProvider dataProvider() {
+        return new RHConfProvider(rows);
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        super.onEvent(event);
+        if (event.getPayload() instanceof WizardMgtPanel.ExitEvent && modal != null) {
+            final AjaxRequestTarget target = WizardMgtPanel.ExitEvent.class.cast(event.getPayload()).getTarget();
+            baseModal.show(false);
+            baseModal.close(target);
+        }
+    }
+
+    protected class RHConfProvider extends DirectoryDataProvider<ResourceHistoryConfTO> {
+
+        private static final long serialVersionUID = -5244315453787001412L;
+
+        private final SortableDataProviderComparator<ResourceHistoryConfTO> comparator;
+
+        public RHConfProvider(final int paginatorRows) {
+            super(paginatorRows);
+
+            setSort("creation", SortOrder.ASCENDING); // sort by 'creation' property
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<ResourceHistoryConfTO> iterator(final long first, final long count) {
+            final List<ResourceHistoryConfTO> configurations = restClient.list(entityKey);
+
+            Collections.sort(configurations, getComparator());
+            return configurations.iterator();
+        }
+
+        public SortableDataProviderComparator<ResourceHistoryConfTO> getComparator() {
+            return comparator;
+        }
+
+        @Override
+        public long size() {
+            return restClient.list(entityKey).size();
+        }
+
+        @Override
+        public IModel<ResourceHistoryConfTO> model(final ResourceHistoryConfTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorHistoryRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorHistoryRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorHistoryRestClient.java
new file mode 100644
index 0000000..9178569
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorHistoryRestClient.java
@@ -0,0 +1,50 @@
+/*
+ * 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.Collections;
+import java.util.List;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.rest.api.service.ConnectorHistoryService;
+
+/**
+ * Console client for invoking Rest Connector configuration history services.
+ */
+public class ConnectorHistoryRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -1917949374689773018L;
+
+    public List<ConnInstanceHistoryConfTO> list(final String key) {
+        List<ConnInstanceHistoryConfTO> connHistoryConfs = Collections.<ConnInstanceHistoryConfTO>emptyList();
+        try {
+            connHistoryConfs = getService(ConnectorHistoryService.class).list(key);
+        } catch (Exception e) {
+            LOG.error("While reading connector history configuration instances", e);
+        }
+        return connHistoryConfs;
+    }
+
+    public void delete(final String key) {
+        getService(ConnectorHistoryService.class).delete(key);
+    }
+
+    public void restore(final String key) {
+        getService(ConnectorHistoryService.class).restore(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/java/org/apache/syncope/client/console/rest/ResourceHistoryRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ResourceHistoryRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ResourceHistoryRestClient.java
new file mode 100644
index 0000000..9efb783
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ResourceHistoryRestClient.java
@@ -0,0 +1,50 @@
+/*
+ * 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.Collections;
+import java.util.List;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.syncope.common.rest.api.service.ResourceHistoryService;
+
+/**
+ * Console client for invoking Rest Resource configuration history services.
+ */
+public class ResourceHistoryRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -5722829010510310887L;
+
+    public List<ResourceHistoryConfTO> list(final String key) {
+        List<ResourceHistoryConfTO> resHistoryConfs = Collections.<ResourceHistoryConfTO>emptyList();
+        try {
+            resHistoryConfs = getService(ResourceHistoryService.class).list(key);
+        } catch (Exception e) {
+            LOG.error("While reading resource history configuration instances", e);
+        }
+        return resHistoryConfs;
+    }
+
+    public void delete(final String key) {
+        getService(ResourceHistoryService.class).delete(key);
+    }
+
+    public void restore(final String key) {
+        getService(ResourceHistoryService.class).restore(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java b/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
index 520bf1a..b02e9ca 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
@@ -25,6 +25,7 @@ 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.pages.BasePage;
+import org.apache.syncope.client.console.panels.HistoryConfList;
 import org.apache.syncope.client.console.panels.ConnObjects;
 import org.apache.syncope.client.console.wizards.resources.ConnectorWizardBuilder;
 import org.apache.syncope.client.console.wizards.resources.ResourceWizardBuilder;
@@ -75,6 +76,8 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
 
     protected final BaseModal<Serializable> provisionModal;
 
+    private final BaseModal<Serializable> historyModal;
+
     public TopologyTogglePanel(final String id, final PageReference pageRef) {
         super(id, "topologyTogglePanel", pageRef);
 
@@ -103,6 +106,10 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
         provisionModal.addSubmitButton();
         addOuterObject(provisionModal);
 
+        historyModal = new BaseModal<>("outer");
+        historyModal.size(Modal.Size.Large);
+        addOuterObject(historyModal);
+
         container = new WebMarkupContainer("container");
         container.setOutputMarkupPlaceholderTag(true);
         addInnerObject(container);
@@ -325,6 +332,32 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
         MetaDataRoleAuthorizationStrategy.authorize(edit, RENDER, StandardEntitlement.CONNECTOR_UPDATE);
         fragment.add(edit);
 
+        AjaxLink<String> history = new IndicatingAjaxLink<String>("history") {
+
+            private static final long serialVersionUID = -1876519166660008562L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                String connKey = String.class.cast(node.getKey());
+                final ConnInstanceTO modelObject = connectorRestClient.read(String.class.cast(node.getKey()));
+
+                target.add(historyModal.setContent(new HistoryConfList<>(historyModal, connKey, pageRef, modelObject)));
+
+                historyModal.header(
+                        new Model<>(MessageFormat.format(getString("connector.menu.history"), node.getDisplayName())));
+
+                historyModal.show(true);
+            }
+
+            @Override
+            public String getAjaxIndicatorMarkupId() {
+                return Constants.VEIL_INDICATOR_MARKUP_ID;
+            }
+
+        };
+        MetaDataRoleAuthorizationStrategy.authorize(history, RENDER, StandardEntitlement.CONNECTOR_HISTORY_LIST);
+        fragment.add(history);
+
         return fragment;
     }
 
@@ -369,8 +402,8 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
 
                 modal.header(new Model<>(MessageFormat.format(getString("resource.edit"), node.getKey())));
 
-                MetaDataRoleAuthorizationStrategy.
-                        authorize(modal.getForm(), RENDER, StandardEntitlement.RESOURCE_UPDATE);
+                MetaDataRoleAuthorizationStrategy.authorize(
+                        modal.getForm(), RENDER, StandardEntitlement.RESOURCE_UPDATE);
 
                 modal.show(true);
             }
@@ -522,6 +555,33 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
         MetaDataRoleAuthorizationStrategy.authorize(push, RENDER, StandardEntitlement.TASK_LIST);
         fragment.add(push);
 
+        AjaxLink<String> history = new IndicatingAjaxLink<String>("history") {
+
+            private static final long serialVersionUID = -1876519166660008562L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                String resourceKey = String.class.cast(node.getKey());
+                final ResourceTO modelObject = resourceRestClient.read(String.class.cast(node.getKey()));
+
+                target.add(historyModal.setContent(
+                        new HistoryConfList<>(historyModal, resourceKey, pageRef, modelObject)));
+
+                historyModal.header(
+                        new Model<>(MessageFormat.format(getString("resource.menu.history"), node.getDisplayName())));
+
+                historyModal.show(true);
+            }
+
+            @Override
+            public String getAjaxIndicatorMarkupId() {
+                return Constants.VEIL_INDICATOR_MARKUP_ID;
+            }
+
+        };
+        MetaDataRoleAuthorizationStrategy.authorize(history, RENDER, StandardEntitlement.RESOURCE_HISTORY_LIST);
+        fragment.add(history);
+
         return fragment;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
index c90ef37..3de74f8 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
@@ -75,9 +75,10 @@ public abstract class ActionLink<T extends Serializable> implements Serializable
         EXPORT_PDF("read"),
         EXPORT_RTF("read"),
         EXPORT_XML("read"),
+        RESTORE("import"),
         SUSPEND("update"),
         REACTIVATE("update"),
-        RELOAD("reload"),
+        RELOAD("import"),
         CHANGE_VIEW("changeView"),
         UNLINK("update"),
         LINK("update"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.java
new file mode 100644
index 0000000..8c5b80e
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/JsonDiffPanel.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.wicket.markup.html.form;
+
+import org.apache.syncope.client.console.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.OnLoadHeaderItem;
+import org.apache.wicket.markup.html.form.TextArea;
+import org.apache.wicket.model.IModel;
+
+public class JsonDiffPanel extends AbstractModalPanel<String> {
+
+    private static final long serialVersionUID = -5110368813584745668L;
+
+    private final IModel<String> first;
+
+    private final IModel<String> second;
+
+    public JsonDiffPanel(
+            final BaseModal<String> modal,
+            final IModel<String> first,
+            final IModel<String> second,
+            final PageReference pageRef) {
+
+        super(modal, pageRef);
+        this.second = second;
+        this.first = first;
+        TextArea<String> jsonEditorInfoDefArea1 = new TextArea<>("jsonEditorInfo1", this.first);
+        TextArea<String> jsonEditorInfoDefArea2 = new TextArea<>("jsonEditorInfo2", this.second);
+        jsonEditorInfoDefArea1.setMarkupId("jsonEditorInfo1").setOutputMarkupPlaceholderTag(true);
+        jsonEditorInfoDefArea2.setMarkupId("jsonEditorInfo2").setOutputMarkupPlaceholderTag(true);
+        add(jsonEditorInfoDefArea1);
+        add(jsonEditorInfoDefArea2);
+    }
+
+    @Override
+    public void renderHead(final IHeaderResponse response) {
+        super.renderHead(response);
+        response.render(OnLoadHeaderItem.forScript(
+                "CodeMirror.MergeView(document.getElementById('jsonDiffEditorInfoDefForm'), {"
+                + "  value: document.getElementById('jsonEditorInfo1').innerHTML, "
+                + "  orig: document.getElementById('jsonEditorInfo2').innerHTML, "
+                + "  lineNumbers: true, "
+                + "  mode: \"application/json\","
+                + "  highlightDifferences: true,"
+                + "  showDifferences: true,"
+                + "  readOnly: true,"
+                + "  revertButtons: false"
+                + "});"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.html
new file mode 100644
index 0000000..da89934
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.html
@@ -0,0 +1,29 @@
+<!--
+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>
+
+    <form wicket:id="form">
+      <span wicket:id="compareDropdown"></span>
+    </form>
+
+    <div wicket:id="content"></div>
+
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.properties
new file mode 100644
index 0000000..0abe803
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails.properties
@@ -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.
+creation=Creation date
+creator=Creator
+entityKey=Local ID
+history.view=History view
+resource=Resource
+compare=Compare with
+current=Current

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_it.properties
new file mode 100644
index 0000000..bac0d9c
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_it.properties
@@ -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.
+creation=Data di creazione
+creator=Creatore
+entityKey=ID locale
+history.view=Configurazione
+resource=Risorsa
+compare=Confronta con
+current=Corrente

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_pt_BR.properties
new file mode 100644
index 0000000..192a559
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_pt_BR.properties
@@ -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.
+creation=Cria\u00e7\u00e3o
+creator=O Criador
+entityKey=ID local
+history.view=Configura\u00e7\u00e3o
+resource=Recurso
+compare=Compare com
+current=Atual

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_ru.properties
new file mode 100644
index 0000000..7b17b31
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfDetails_ru.properties
@@ -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.
+creation=\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435
+creator=\u0442\u0432\u043e\u0440\u0435\u0446
+entityKey=\u041c\u0435\u0441\u0442\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440
+history.view=\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f
+resource=\u0420\u0435\u0441\u0443\u0440\u0441
+compare=\u0421\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u0441
+current=\u0422\u0435\u043a\u0443\u0449\u0438\u0439

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.html
new file mode 100644
index 0000000..1cae5af
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.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="history">[HISTORY]</span>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.properties
new file mode 100644
index 0000000..5546931
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList.properties
@@ -0,0 +1,21 @@
+# 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.
+creation=Creation date
+creator=Creator
+entityKey=Local ID
+history.diff.view=Configuration
+resource=Resource

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_it.properties
new file mode 100644
index 0000000..88c8bb3
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_it.properties
@@ -0,0 +1,21 @@
+# 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.
+creation=Data di creazione
+creator=Creatore
+entityKey=ID locale
+history.diff.view=Configurazione
+resource=Risorsa

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_pt_BR.properties
new file mode 100644
index 0000000..813728d
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_pt_BR.properties
@@ -0,0 +1,21 @@
+# 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.
+creation=Cria\u00e7\u00e3o
+creator=O Criador
+entityKey=ID local
+history.diff.view=Configura\u00e7\u00e3o
+resource=Recurso

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_ru.properties
new file mode 100644
index 0000000..4f278d7
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/HistoryConfList_ru.properties
@@ -0,0 +1,21 @@
+# 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.
+creation=\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435
+creator=\u0442\u0432\u043e\u0440\u0435\u0446
+entityKey=\u041c\u0435\u0441\u0442\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440
+history.diff.view=\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f
+resource=\u0420\u0435\u0441\u0443\u0440\u0441

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html
index 5abef88..c596813 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html
@@ -37,6 +37,9 @@ under the License.
         <wicket:enclosure child="edit">
           <li><a href="#" wicket:id="edit"><i class="fa fa-pencil"></i><wicket:message key="connector.menu.edit"/></a></li>
         </wicket:enclosure>
+        <wicket:enclosure child="history">
+          <li><a href="#" wicket:id="history"><i class="fa fa-history"></i><wicket:message key="connector.menu.history"/></a></li>
+        </wicket:enclosure>
         <wicket:enclosure child="delete">
           <li><a href="#" wicket:id="delete"><i class="fa fa-minus"></i><wicket:message key="connector.menu.remove"/></a></li>
         </wicket:enclosure>
@@ -66,6 +69,9 @@ under the License.
         <wicket:enclosure child="status">
           <li><a href="#" wicket:id="status"><i class="fa fa-list-ul"></i><wicket:message key="resource.menu.provisioning.status"/></a></li>
         </wicket:enclosure>
+        <wicket:enclosure child="history">
+          <li><a href="#" wicket:id="history"><i class="fa fa-history"></i><wicket:message key="resource.menu.history"/></a></li>
+        </wicket:enclosure>
         <wicket:enclosure child="delete">
           <li><a href="#" wicket:id="delete"><i class="fa fa-minus"></i><wicket:message key="resource.menu.remove"/></a></li>
         </wicket:enclosure>

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties
index 43df570..08c0e53 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties
@@ -19,6 +19,7 @@ connector.edit=Edit connector {0}
 connector.menu.add=Add new connector
 connector.menu.remove=Remove connector
 connector.menu.edit=Edit connector
+connector.menu.history=Configuration history
 
 resource.new=New resource
 resource.edit=Edit resource {0}
@@ -27,6 +28,7 @@ resource.menu.remove=Remove resource
 resource.menu.edit=Edit resource
 resource.menu.provision=Edit provision rules
 resource.menu.explore=Explore resource
+resource.menu.history=Configuration history
 
 task.custom.list=Custom tasks
 task.propagation.list=Propagation tasks {0}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties
index 90ed5ec..1493852 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties
@@ -19,6 +19,7 @@ connector.edit=Modifica connettore {0}
 connector.menu.add=Aggiungi nuovo connettore
 connector.menu.remove=Rimuovi connettore
 connector.menu.edit=Modifica connettore
+connector.menu.history=Storico delle configurazioni
 
 resource.new=Nuova risorsa
 resource.edit=Modifica risorsa {0}
@@ -27,6 +28,7 @@ resource.menu.remove=Rimuovi risorsa
 resource.menu.edit=Modifica risorsa
 resource.menu.provision=Modifica regole di provisioning
 resource.menu.explore=Esplora risorsa
+resource.menu.history=Storico delle configurazioni
 
 task.custom.list=Task personalizzati
 task.propagation.list=Task di propagazione {0}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties
index 6f2520e..d6f9756 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties
@@ -19,6 +19,7 @@ connector.edit=Alterar conector {0}
 connector.menu.add=Adicionar novo conector
 connector.menu.remove=Retire conector
 connector.menu.edit=Alterar conector
+connector.menu.history=Hist\u00f3rico de configura\u00e7\u00e3o
 
 resource.new=Novo recurso
 resource.edit=Alterar recurso {0}
@@ -27,6 +28,7 @@ resource.menu.remove=Retire recurso
 resource.menu.edit=Alterar recurso
 resource.menu.provision=Alterar regras de provision
 resource.menu.explore=Explorar recurso
+resource.menu.history=Hist\u00f3rico de configura\u00e7\u00e3o
 
 task.custom.list=Custom tasks
 task.propagation.list=Propagation tasks {0}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties
index 997f304..1106c65 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties
@@ -20,6 +20,7 @@ connector.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043e\u04
 connector.menu.add=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440
 connector.menu.remove=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440
 connector.menu.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440
+connector.menu.history=\u0418\u0441\u0442\u043e\u0440\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438
 
 resource.new=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441
 resource.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441 {0}
@@ -28,6 +29,7 @@ resource.menu.remove=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0440\u0435\u04
 resource.menu.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441
 resource.menu.provision=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f
 resource.menu.explore=\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u0440\u0435\u0441\u0443\u0440\u0441\u0430
+resource.menu.history=\u0418\u0441\u0442\u043e\u0440\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438
 
 task.custom.list=\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438
 task.propagation.list=\u0417\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 {0}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
index 3eff230..eaf9bda 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
@@ -146,6 +146,10 @@ export_xml.class=fa fa-file-excel-o
 export_xml.title=export as xml
 export_xml.alt=export as xml icon
 
+restore.class=fa fa-arrow-circle-down
+restore.title=restore
+restore.alt=restore icon
+
 suspend.class=glyphicon glyphicon-ban-circle
 suspend.title=suspend
 suspend.alt=suspend icon

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_it.properties
index e23ad28..0351275 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_it.properties
@@ -146,6 +146,10 @@ export_xml.class=fa fa-file-excel-o
 export_xml.title=export as xml
 export_xml.alt=export as xml icon
 
+restore.class=fa fa-arrow-circle-down
+restore.title=restore
+restore.alt=restore icon
+
 suspend.class=glyphicon glyphicon-ban-circle
 suspend.title=suspend
 suspend.alt=suspend icon

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_pt_BR.properties
index e23ad28..0351275 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_pt_BR.properties
@@ -146,6 +146,10 @@ export_xml.class=fa fa-file-excel-o
 export_xml.title=export as xml
 export_xml.alt=export as xml icon
 
+restore.class=fa fa-arrow-circle-down
+restore.title=restore
+restore.alt=restore icon
+
 suspend.class=glyphicon glyphicon-ban-circle
 suspend.title=suspend
 suspend.alt=suspend icon

http://git-wip-us.apache.org/repos/asf/syncope/blob/5a07d7e9/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_ru.properties
index e23ad28..0351275 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_ru.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel_ru.properties
@@ -146,6 +146,10 @@ export_xml.class=fa fa-file-excel-o
 export_xml.title=export as xml
 export_xml.alt=export as xml icon
 
+restore.class=fa fa-arrow-circle-down
+restore.title=restore
+restore.alt=restore icon
+
 suspend.class=glyphicon glyphicon-ban-circle
 suspend.title=suspend
 suspend.alt=suspend icon