You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by fm...@apache.org on 2013/11/05 09:22:32 UTC

svn commit: r1538902 [1/4] - in /syncope/branches/1_1_X: client/src/main/java/org/apache/syncope/client/services/proxy/ client/src/test/java/org/apache/syncope/client/test/ common/src/main/java/org/apache/syncope/common/services/ common/src/main/java/o...

Author: fmartelli
Date: Tue Nov  5 08:22:31 2013
New Revision: 1538902

URL: http://svn.apache.org/r1538902
Log:
Centralized notification and auditing message generation. Missing some improvement onto the console .... see issues SYNCOPE-422 & SYNCOPE-423

Added:
    syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/to/EventCategoryTO.java
    syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/util/LoggerEventUtils.java
    syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/EventSelectionPanel.java
    syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/LoggerCategoryPanel.java
    syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/SelectedEventsPanel.java
    syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.java
      - copied, changed from r1538202, syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/wicket/extensions/markup/html/repeater/data/table/CheckBoxPanel.java
    syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/wicket/extensions/markup/html/repeater/data/table/CollectionPropertyColumn.java
      - copied, changed from r1538202, syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/wicket/extensions/markup/html/repeater/data/table/TokenColumn.java
    syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/panels/AuditEventsPanel.html
      - copied, changed from r1536731, syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/Reports$AuditCategoryPanel.html
    syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/panels/EventSelectionPanel.html
      - copied, changed from r1536731, syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/Reports$AuditCategoryPanel.html
    syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/panels/LoggerCategoryPanel.html
      - copied, changed from r1536731, syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/panels/AbstractSearchPanel.html
    syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/panels/SelectedEventsPanel.html
      - copied, changed from r1536731, syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/Reports$AuditCategoryPanel.html
    syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.html
      - copied, changed from r1538202, syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/wicket/extensions/markup/html/repeater/data/table/CheckBoxPanel.html
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractTransactionalController.java
      - copied, changed from r1524721, syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/UnresolvedReferenceException.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/services/ServiceHandler.java
Removed:
    syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/Reports$AuditCategoryPanel.html
Modified:
    syncope/branches/1_1_X/client/src/main/java/org/apache/syncope/client/services/proxy/LoggerServiceProxy.java
    syncope/branches/1_1_X/client/src/test/java/org/apache/syncope/client/test/JSONTest.java
    syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/services/LoggerService.java
    syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/types/AuditElements.java
    syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/types/AuditLoggerName.java
    syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/util/CollectionWrapper.java
    syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/Configuration.java
    syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/NotificationModalPage.java
    syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/Reports.java
    syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/rest/LoggerRestClient.java
    syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/NotificationModalPage.html
    syncope/branches/1_1_X/console/src/main/resources/org/apache/syncope/console/pages/Reports.html
    syncope/branches/1_1_X/console/src/test/java/org/apache/syncope/console/ConfigurationTestITCase.java
    syncope/branches/1_1_X/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/audit/AuditManager.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/notification/NotificationJob.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/persistence/dao/impl/AttributableSearchDAOImpl.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/quartz/AbstractTaskJob.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/AuthenticationController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/ConfigurationController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/ConnInstanceController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/DerivedSchemaController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/LoggerController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/NotificationController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/PolicyController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/ReportController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/SchemaController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/TaskController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/UserRequestController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/VirtualSchemaController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/WorkflowController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/NotificationDataBinder.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/SchemaDataBinder.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/security/SyncopeAuthenticationProvider.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/services/LoggerServiceImpl.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPMembershipSyncActions.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/workflow/WorkflowUserSuspender.java
    syncope/branches/1_1_X/core/src/main/resources/restContext.xml
    syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/notification/NotificationTest.java
    syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/rest/LoggerTestITCase.java
    syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
    syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
    syncope/branches/1_1_X/core/src/test/resources/content.xml

Modified: syncope/branches/1_1_X/client/src/main/java/org/apache/syncope/client/services/proxy/LoggerServiceProxy.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/client/src/main/java/org/apache/syncope/client/services/proxy/LoggerServiceProxy.java?rev=1538902&r1=1538901&r2=1538902&view=diff
==============================================================================
--- syncope/branches/1_1_X/client/src/main/java/org/apache/syncope/client/services/proxy/LoggerServiceProxy.java (original)
+++ syncope/branches/1_1_X/client/src/main/java/org/apache/syncope/client/services/proxy/LoggerServiceProxy.java Tue Nov  5 08:22:31 2013
@@ -25,6 +25,7 @@ import javax.ws.rs.BadRequestException;
 import javax.ws.rs.NotFoundException;
 
 import org.apache.syncope.common.services.LoggerService;
+import org.apache.syncope.common.to.EventCategoryTO;
 import org.apache.syncope.common.to.LoggerTO;
 import org.apache.syncope.common.types.AuditLoggerName;
 import org.apache.syncope.common.types.LoggerType;
@@ -104,4 +105,9 @@ public class LoggerServiceProxy extends 
         }
 
     }
+
+    @Override
+    public List<EventCategoryTO> events() {
+        return Arrays.asList(getRestTemplate().getForObject(baseUrl + "logger/events", EventCategoryTO[].class));
+    }
 }

Modified: syncope/branches/1_1_X/client/src/test/java/org/apache/syncope/client/test/JSONTest.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/client/src/test/java/org/apache/syncope/client/test/JSONTest.java?rev=1538902&r1=1538901&r2=1538902&view=diff
==============================================================================
--- syncope/branches/1_1_X/client/src/test/java/org/apache/syncope/client/test/JSONTest.java (original)
+++ syncope/branches/1_1_X/client/src/test/java/org/apache/syncope/client/test/JSONTest.java Tue Nov  5 08:22:31 2013
@@ -121,8 +121,12 @@ public class JSONTest {
 
     @Test
     public void testAuditLoggerName() throws IOException {
-        AuditLoggerName auditLoggerName = new AuditLoggerName(AuditElements.Category.report,
-                AuditElements.ReportSubCategory.create, AuditElements.Result.failure);
+        AuditLoggerName auditLoggerName = new AuditLoggerName(
+                AuditElements.EventCategoryType.REST,
+                "ReportController",
+                null,
+                "create",
+                AuditElements.Result.FAILURE);
 
         ObjectMapper mapper = new ObjectMapper();
 

Modified: syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/services/LoggerService.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/services/LoggerService.java?rev=1538902&r1=1538901&r2=1538902&view=diff
==============================================================================
--- syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/services/LoggerService.java (original)
+++ syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/services/LoggerService.java Tue Nov  5 08:22:31 2013
@@ -25,6 +25,7 @@ import javax.ws.rs.GET;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
+import org.apache.syncope.common.to.EventCategoryTO;
 
 import org.apache.syncope.common.to.LoggerTO;
 import org.apache.syncope.common.types.LoggerType;
@@ -65,4 +66,6 @@ public interface LoggerService {
     @Path("{name}/level")
     void update(@PathParam("type") LoggerType type, @PathParam("name") String name, LoggerTO logger);
 
+    @GET
+    List<EventCategoryTO> events();
 }

Added: syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/to/EventCategoryTO.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/to/EventCategoryTO.java?rev=1538902&view=auto
==============================================================================
--- syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/to/EventCategoryTO.java (added)
+++ syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/to/EventCategoryTO.java Tue Nov  5 08:22:31 2013
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.syncope.common.to;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.AbstractBaseBean;
+import org.apache.syncope.common.types.AuditElements;
+
+@XmlRootElement(name = "user")
+@XmlType
+public class EventCategoryTO extends AbstractBaseBean {
+
+    private static final long serialVersionUID = -4340060002701633401L;
+
+    private AuditElements.EventCategoryType type;
+
+    private String category;
+
+    private String subcategory;
+
+    private List<String> events;
+
+    /**
+     * Constructor for Type.REST event category.
+     */
+    public EventCategoryTO() {
+        this.type = AuditElements.EventCategoryType.REST;
+    }
+
+    /**
+     * Constructor for the given Type event category.
+     */
+    public EventCategoryTO(final AuditElements.EventCategoryType type) {
+        this.type = type;
+    }
+
+    public AuditElements.EventCategoryType getType() {
+        return type;
+    }
+
+    public void setType(final AuditElements.EventCategoryType type) {
+        this.type = type;
+    }
+
+    public String getCategory() {
+        return category;
+    }
+
+    public void setCategory(final String category) {
+        this.category = category;
+    }
+
+    public String getSubcategory() {
+        return subcategory;
+    }
+
+    public void setSubcategory(final String subcategory) {
+        this.subcategory = subcategory;
+    }
+
+    public List<String> getEvents() {
+        if (events == null) {
+            events = new ArrayList<String>();
+        }
+        return events;
+    }
+
+    public void setEvents(final List<String> events) {
+        this.events = events;
+    }
+}

Modified: syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/types/AuditElements.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/types/AuditElements.java?rev=1538902&r1=1538901&r2=1538902&view=diff
==============================================================================
--- syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/types/AuditElements.java (original)
+++ syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/types/AuditElements.java Tue Nov  5 08:22:31 2013
@@ -18,249 +18,42 @@
  */
 package org.apache.syncope.common.types;
 
-import java.util.EnumSet;
+import java.io.Serializable;
 import javax.xml.bind.annotation.XmlEnum;
+import org.apache.commons.lang.StringUtils;
 
-public final class AuditElements {
+public final class AuditElements implements Serializable {
+
+    private static final long serialVersionUID = -4385059255522273254L;
 
     private AuditElements() {
     }
 
     @XmlEnum
-    public enum Category {
-
-        authentication(AuthenticationSubCategory.class),
-        configuration(ConfigurationSubCategory.class),
-        connector(ConnectorSubCategory.class),
-        logger(LoggerSubCategory.class),
-        notification(NotificationSubCategory.class),
-        policy(PolicySubCategory.class),
-        report(ReportSubCategory.class),
-        resource(ResourceSubCategory.class),
-        role(RoleSubCategory.class),
-        schema(SchemaSubCategory.class),
-        task(TaskSubCategory.class),
-        user(UserSubCategory.class),
-        userRequest(UserRequestSubCategory.class),
-        workflow(WorkflowSubCategory.class);
+    public enum EventCategoryType {
 
-        private Class<? extends Enum<?>> subCategory;
+        REST(StringUtils.EMPTY),
+        TASK(StringUtils.EMPTY),
+        PROPAGATION("PropagationTask"),
+        SYNCHRONIZATION("SyncTask");
 
-        Category(final Class<? extends Enum<?>> subCategory) {
-            this.subCategory = subCategory;
-        }
+        private final String value;
 
-        public Class<? extends Enum> getSubCategory() {
-            return subCategory;
+        EventCategoryType(final String value) {
+            this.value = value;
         }
 
-        @SuppressWarnings("unchecked")
-        public EnumSet<? extends Enum<?>> getSubCategoryElements() {
-            return EnumSet.allOf(getSubCategory());
+        @Override
+        public String toString() {
+            return value;
         }
     }
 
     @XmlEnum
     public enum Result {
 
-        success,
-        failure
-
-    }
-
-    @XmlEnum
-    public enum AuthenticationSubCategory {
-
-        login,
-        getEntitlements
-
-    }
-
-    @XmlEnum
-    public enum ConfigurationSubCategory {
-
-        list,
-        create,
-        read,
-        update,
-        delete,
-        getMailTemplates,
-        getValidators,
-        dbExport
-
-    }
-
-    @XmlEnum
-    public enum ConnectorSubCategory {
-
-        list,
-        create,
-        read,
-        update,
-        delete,
-        getBundles,
-        getSchemaNames,
-        getSupportedObjectClasses,
-        getConfigurationProperties,
-        check,
-        readConnectorBean,
-        reload
-
-    }
-
-    @XmlEnum
-    public enum LoggerSubCategory {
-
-        list,
-        setLevel,
-        delete
-
-    }
-
-    @XmlEnum
-    public enum NotificationSubCategory {
-
-        list,
-        create,
-        read,
-        update,
-        delete,
-        send,
-        retry
-
-    }
-
-    @XmlEnum
-    public enum PolicySubCategory {
-
-        list,
-        create,
-        read,
-        update,
-        delete,
-        getCorrelationRuleClasses
-
-    }
-
-    @XmlEnum
-    public enum ReportSubCategory {
-
-        list,
-        create,
-        read,
-        readExecution,
-        update,
-        delete,
-        deleteExecution,
-        getReportletConfClasses,
-        execute,
-        exportExecutionResult
-
-    }
-
-    @XmlEnum
-    public enum ResourceSubCategory {
-
-        list,
-        create,
-        read,
-        update,
-        delete,
-        getObject,
-        getRoleResourcesMapping,
-        getPropagationActionsClasses
-
-    }
-
-    @XmlEnum
-    public enum RoleSubCategory {
-
-        list,
-        create,
-        read,
-        selfRead,
-        update,
-        delete,
-        parent,
-        children
-
-    }
-
-    @XmlEnum
-    public enum SchemaSubCategory {
-
-        list,
-        create,
-        read,
-        update,
-        delete,
-        listDerived,
-        createDerived,
-        readDerived,
-        updateDerived,
-        deleteDerived,
-        listVirtual,
-        createVirtual,
-        readVirtual,
-        updateVirtual,
-        deleteVirtual
-
-    }
-
-    @XmlEnum
-    public enum TaskSubCategory {
-
-        list,
-        create,
-        read,
-        update,
-        delete,
-        getJobClasses,
-        getSyncActionsClasses,
-        readExecution,
-        execute,
-        report,
-        deleteExecution
-
-    }
-
-    @XmlEnum
-    public enum UserSubCategory {
-
-        list,
-        create,
-        read,
-        update,
-        delete,
-        verifyPassword,
-        search,
-        setStatus,
-        executeWorkflow,
-        getForms,
-        getFormForUser,
-        claimForm,
-        submitForm
-
-    }
-
-    @XmlEnum
-    public enum UserRequestSubCategory {
-
-        list,
-        create,
-        read,
-        update,
-        delete,
-        isCreateAllowed
-
-    }
-
-    @XmlEnum
-    public enum WorkflowSubCategory {
-
-        getDefinition,
-        updateDefinition,
-        getDefinedTasks
+        SUCCESS,
+        FAILURE
 
     }
 }

Modified: syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/types/AuditLoggerName.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/types/AuditLoggerName.java?rev=1538902&r1=1538901&r2=1538902&view=diff
==============================================================================
--- syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/types/AuditLoggerName.java (original)
+++ syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/types/AuditLoggerName.java Tue Nov  5 08:22:31 2013
@@ -19,17 +19,21 @@
 package org.apache.syncope.common.types;
 
 import java.text.ParseException;
+import java.util.Map;
 
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.syncope.common.AbstractBaseBean;
-import org.apache.syncope.common.types.AuditElements.Category;
+import org.apache.syncope.common.to.EventCategoryTO;
 import org.apache.syncope.common.types.AuditElements.Result;
+import org.apache.syncope.common.util.LoggerEventUtils;
 import org.codehaus.jackson.annotate.JsonCreator;
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.codehaus.jackson.annotate.JsonTypeInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @XmlType
 @XmlRootElement
@@ -37,32 +41,51 @@ public class AuditLoggerName extends Abs
 
     private static final long serialVersionUID = -647989486671786839L;
 
-    private final Category category;
+    /**
+     * Logger.
+     */
+    private static Logger LOG = LoggerFactory.getLogger(AuditLoggerName.class);
+
+    private final AuditElements.EventCategoryType type;
+
+    private final String category;
+
+    private final String subcategory;
 
     @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
-    private final Enum<?> subcategory;
+    private final String event;
 
     private final Result result;
 
     @JsonCreator
-    public AuditLoggerName(@JsonProperty("category") final Category category,
-            @JsonProperty("subcategory") final Enum<?> subcategory, @JsonProperty("result") final Result result)
+    public AuditLoggerName(
+            @JsonProperty("type") final AuditElements.EventCategoryType type,
+            @JsonProperty("category") final String category,
+            @JsonProperty("subcategory") final String subcategory,
+            @JsonProperty("event") final String event,
+            @JsonProperty("result") final Result result)
             throws IllegalArgumentException {
 
-        if (category == null || subcategory == null || result == null) {
+        if (type == null || result == null) {
             throw new IllegalArgumentException("Null values not permitted");
         }
 
-        if (!category.getSubCategoryElements().contains(subcategory)) {
-            throw new IllegalArgumentException(category.name() + " does not contain " + subcategory.name());
-        }
-
+        this.type = type;
         this.category = category;
         this.subcategory = subcategory;
+        this.event = event;
         this.result = result;
     }
 
-    public Category getCategory() {
+    public AuditElements.EventCategoryType getType() {
+        return type;
+    }
+
+    public String getEvent() {
+        return event;
+    }
+
+    public String getCategory() {
         return category;
     }
 
@@ -70,15 +93,14 @@ public class AuditLoggerName extends Abs
         return result;
     }
 
-    public Enum<?> getSubcategory() {
+    public String getSubcategory() {
         return subcategory;
     }
 
     public String toLoggerName() {
-        return new StringBuilder().append(SyncopeLoggerType.AUDIT.getPrefix()).append('.').
-                append(category.name()).append('.').
-                append(subcategory.name()).append('.').
-                append(result.name()).toString();
+        return new StringBuilder().append(
+                SyncopeLoggerType.AUDIT.getPrefix()).append('.').append(
+                LoggerEventUtils.buildEvent(type, category, subcategory, event, result)).toString();
     }
 
     @SuppressWarnings("unchecked")
@@ -93,15 +115,16 @@ public class AuditLoggerName extends Abs
             throw new ParseException("Audit logger name must start with " + SyncopeLoggerType.AUDIT.getPrefix(), 0);
         }
 
-        String[] splitted = loggerName.split("\\.");
-        if (splitted == null || splitted.length < 5) {
-            throw new ParseException("Unparsable logger name", 0);
-        }
+        final Map.Entry<EventCategoryTO, Result> eventCategory = LoggerEventUtils.parseEventCategory(
+                loggerName.replaceAll(SyncopeLoggerType.AUDIT.getPrefix() + ".", ""));
 
-        Category category = Category.valueOf(splitted[2]);
-        Enum<?> subcategory = Enum.valueOf(category.getSubCategory(), splitted[3]);
-        Result result = Result.valueOf(splitted[4]);
+        LOG.debug("From logger name {} to event category {}", loggerName, eventCategory);
 
-        return new AuditLoggerName(category, subcategory, result);
+        return new AuditLoggerName(
+                eventCategory.getKey().getType(),
+                eventCategory.getKey().getCategory(),
+                eventCategory.getKey().getSubcategory(),
+                eventCategory.getKey().getEvents().iterator().next(),
+                eventCategory.getValue());
     }
 }

Modified: syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/util/CollectionWrapper.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/util/CollectionWrapper.java?rev=1538902&r1=1538901&r2=1538902&view=diff
==============================================================================
--- syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/util/CollectionWrapper.java (original)
+++ syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/util/CollectionWrapper.java Tue Nov  5 08:22:31 2013
@@ -33,10 +33,17 @@ import org.apache.syncope.common.to.Sync
 import org.apache.syncope.common.to.ValidatorTO;
 import org.apache.syncope.common.types.AuditLoggerName;
 import org.apache.syncope.common.types.SyncopeLoggerLevel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.web.servlet.ModelAndView;
 
 public final class CollectionWrapper {
 
+    /**
+     * Logger.
+     */
+    private final static Logger LOG = LoggerFactory.getLogger(CollectionWrapper.class);
+
     private CollectionWrapper() {
         // empty constructor for static utility class
     }
@@ -100,7 +107,7 @@ public final class CollectionWrapper {
             try {
                 respons.add(AuditLoggerName.fromLoggerName(l.getName()));
             } catch (Exception e) {
-                //TODO log event
+                LOG.error("Error wrapping logger", e);
             }
         }
         return respons;

Added: syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/util/LoggerEventUtils.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/util/LoggerEventUtils.java?rev=1538902&view=auto
==============================================================================
--- syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/util/LoggerEventUtils.java (added)
+++ syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/util/LoggerEventUtils.java Tue Nov  5 08:22:31 2013
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2013 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.syncope.common.util;
+
+import java.util.AbstractMap;
+import java.util.Map;
+import org.apache.commons.lang.StringUtils;
+import org.apache.syncope.common.to.EventCategoryTO;
+import org.apache.syncope.common.types.AuditElements;
+import org.apache.syncope.common.types.AuditElements.EventCategoryType;
+import org.apache.syncope.common.types.AuditElements.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LoggerEventUtils {
+
+    /**
+     * Logger.
+     */
+    private static Logger LOG = LoggerFactory.getLogger(LoggerEventUtils.class);
+
+    public static Map.Entry<EventCategoryTO, Result> parseEventCategory(final String event) {
+        final EventCategoryTO eventCategoryTO = new EventCategoryTO();
+
+        Result condition = null;
+
+        if (StringUtils.isNotEmpty(event)) {
+            LOG.debug("Parse event {}", event);
+
+            final String[] elements = event.substring(1, event.length() - 1).split("\\]:\\[");
+
+            LOG.debug("Found {} elements", elements.length);
+
+            LOG.debug("Type {}", elements[0]);
+
+            if (EventCategoryType.PROPAGATION.toString().equals(elements[0])) {
+                eventCategoryTO.setType(EventCategoryType.PROPAGATION);
+            } else if (EventCategoryType.SYNCHRONIZATION.toString().equals(elements[0])) {
+                eventCategoryTO.setType(EventCategoryType.SYNCHRONIZATION);
+            } else {
+                eventCategoryTO.setType(EventCategoryType.valueOf(elements[0]));
+            }
+
+            LOG.debug("Category {}", elements[1]);
+            eventCategoryTO.setCategory(StringUtils.isNotEmpty(elements[1]) ? elements[1] : null);
+
+            LOG.debug("Sub-category {}", elements[2]);
+            eventCategoryTO.setSubcategory(StringUtils.isNotEmpty(elements[2]) ? elements[2] : null);
+
+            if (elements.length > 3 && StringUtils.isNotEmpty(elements[3])) {
+                LOG.debug("Event {}", elements[3]);
+                eventCategoryTO.getEvents().add(elements[3]);
+            }
+
+            if (elements.length > 4) {
+                LOG.debug("Result condition {}", elements[4]);
+                condition = Result.valueOf(elements[4].toUpperCase());
+            }
+        }
+
+        return new AbstractMap.SimpleEntry< EventCategoryTO, Result>(eventCategoryTO, condition);
+    }
+
+    /**
+     * Build event string with the following syntax [type]:[category]:[subcategory]:[event]:[maybe result value cond].
+     *
+     * @param type event type.
+     * @param category event category.
+     * @param subcategory event subcategory.
+     * @param event event.
+     * @param resultValueCondition result value condition.
+     * @return event string.
+     */
+    public static String buildEvent(
+            final AuditElements.EventCategoryType type,
+            final String category,
+            final String subcategory,
+            final String event,
+            final AuditElements.Result resultValueCondition) {
+
+        final StringBuilder eventBuilder = new StringBuilder();
+
+        eventBuilder.append('[');
+        if (type != null) {
+            if (StringUtils.isNotBlank(type.toString())) {
+                eventBuilder.append(type.toString());
+            } else {
+                eventBuilder.append(type.name());
+            }
+        }
+        eventBuilder.append(']');
+
+        eventBuilder.append(":");
+
+        eventBuilder.append('[');
+        if (StringUtils.isNotBlank(category)) {
+            eventBuilder.append(category);
+        }
+        eventBuilder.append(']');
+
+        eventBuilder.append(":");
+
+        eventBuilder.append('[');
+        if (StringUtils.isNotBlank(subcategory)) {
+            eventBuilder.append(subcategory);
+        }
+        eventBuilder.append(']');
+
+        eventBuilder.append(":");
+
+        eventBuilder.append('[');
+        if (StringUtils.isNotBlank(event)) {
+            eventBuilder.append(event);
+        }
+        eventBuilder.append(']');
+
+        if (resultValueCondition != null) {
+            eventBuilder.append(":");
+
+            eventBuilder.append('[');
+            eventBuilder.append(resultValueCondition);
+            eventBuilder.append(']');
+        }
+
+        return eventBuilder.toString();
+    }
+}

Modified: syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/Configuration.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/Configuration.java?rev=1538902&r1=1538901&r2=1538902&view=diff
==============================================================================
--- syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/Configuration.java (original)
+++ syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/Configuration.java Tue Nov  5 08:22:31 2013
@@ -43,6 +43,7 @@ import org.apache.syncope.console.rest.L
 import org.apache.syncope.console.rest.NotificationRestClient;
 import org.apache.syncope.console.rest.WorkflowRestClient;
 import org.apache.syncope.console.wicket.ajax.markup.html.ClearIndicatingAjaxButton;
+import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.CollectionPropertyColumn;
 import org.apache.syncope.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.console.wicket.markup.html.form.ActionLinksPanel;
 import org.apache.wicket.Page;
@@ -119,7 +120,7 @@ public class Configuration extends BaseP
 
     private static final int NOTIFICATION_WIN_HEIGHT = 500;
 
-    private static final int NOTIFICATION_WIN_WIDTH = 900;
+    private static final int NOTIFICATION_WIN_WIDTH = 1100;
 
     private WebMarkupContainer confContainer;
 
@@ -188,7 +189,8 @@ public class Configuration extends BaseP
         add(workflowDefContainer);
 
         // Logger stuff
-        PropertyListView coreLoggerList = new LoggerPropertyList(null, "corelogger", loggerRestClient.listLogs());
+        PropertyListView<LoggerTO> coreLoggerList =
+                new LoggerPropertyList(null, "corelogger", loggerRestClient.listLogs());
         WebMarkupContainer coreLoggerContainer = new WebMarkupContainer("coreLoggerContainer");
         coreLoggerContainer.add(coreLoggerList);
         coreLoggerContainer.setOutputMarkupId(true);
@@ -198,8 +200,8 @@ public class Configuration extends BaseP
         add(coreLoggerContainer);
 
         ConsoleLoggerController consoleLoggerController = new ConsoleLoggerController();
-        PropertyListView consoleLoggerList = new LoggerPropertyList(consoleLoggerController, "consolelogger",
-                consoleLoggerController.getLoggers());
+        PropertyListView<LoggerTO> consoleLoggerList =
+                new LoggerPropertyList(consoleLoggerController, "consolelogger", consoleLoggerController.getLoggers());
         WebMarkupContainer consoleLoggerContainer = new WebMarkupContainer("consoleLoggerContainer");
         consoleLoggerContainer.add(consoleLoggerList);
         consoleLoggerContainer.setOutputMarkupId(true);
@@ -212,11 +214,9 @@ public class Configuration extends BaseP
     private void setupSyncopeConf() {
         confPaginatorRows = prefMan.getPaginatorRows(getRequest(), Constants.PREF_CONFIGURATION_PAGINATOR_ROWS);
 
-        List<IColumn> confColumns = new ArrayList<IColumn>();
-
-        confColumns.add(new PropertyColumn(new ResourceModel("key"), "key", "key"));
-
-        confColumns.add(new PropertyColumn(new ResourceModel("value"), "value", "value"));
+        final List<IColumn<ConfigurationTO, String>> confColumns = new ArrayList<IColumn<ConfigurationTO, String>>();
+        confColumns.add(new PropertyColumn<ConfigurationTO, String>(new ResourceModel("key"), "key", "key"));
+        confColumns.add(new PropertyColumn<ConfigurationTO, String>(new ResourceModel("value"), "value", "value"));
 
         confColumns.add(new AbstractColumn<ConfigurationTO, String>(new ResourceModel("actions", "")) {
 
@@ -282,8 +282,9 @@ public class Configuration extends BaseP
             }
         });
 
-        final AjaxFallbackDefaultDataTable confTable = new AjaxFallbackDefaultDataTable("syncopeconf", confColumns,
-                new SyncopeConfProvider(), confPaginatorRows);
+        final AjaxFallbackDefaultDataTable<ConfigurationTO, String> confTable =
+                new AjaxFallbackDefaultDataTable<ConfigurationTO, String>(
+                "syncopeconf", confColumns, new SyncopeConfProvider(), confPaginatorRows);
 
         confContainer = new WebMarkupContainer("confContainer");
         confContainer.add(confTable);
@@ -380,12 +381,17 @@ public class Configuration extends BaseP
     private void setupNotification() {
         notificationPaginatorRows = prefMan.getPaginatorRows(getRequest(), Constants.PREF_NOTIFICATION_PAGINATOR_ROWS);
 
-        List<IColumn> notificationCols = new ArrayList<IColumn>();
-        notificationCols.add(new PropertyColumn(new ResourceModel("id"), "id", "id"));
-        notificationCols.add(new PropertyColumn(new ResourceModel("events"), "events", "events"));
-        notificationCols.add(new PropertyColumn(new ResourceModel("subject"), "subject", "subject"));
-        notificationCols.add(new PropertyColumn(new ResourceModel("template"), "template", "template"));
-        notificationCols.add(new PropertyColumn(new ResourceModel("traceLevel"), "traceLevel", "traceLevel"));
+        final List<IColumn<NotificationTO, String>> notificationCols = new ArrayList<IColumn<NotificationTO, String>>();
+        notificationCols.add(new PropertyColumn<NotificationTO, String>(
+                new ResourceModel("id"), "id", "id"));
+        notificationCols.add(new CollectionPropertyColumn<NotificationTO>(
+                new ResourceModel("events"), "events", "events"));
+        notificationCols.add(new PropertyColumn<NotificationTO, String>(
+                new ResourceModel("subject"), "subject", "subject"));
+        notificationCols.add(new PropertyColumn<NotificationTO, String>(
+                new ResourceModel("template"), "template", "template"));
+        notificationCols.add(new PropertyColumn<NotificationTO, String>(
+                new ResourceModel("traceLevel"), "traceLevel", "traceLevel"));
 
         notificationCols.add(new AbstractColumn<NotificationTO, String>(new ResourceModel("actions", "")) {
 
@@ -451,8 +457,9 @@ public class Configuration extends BaseP
             }
         });
 
-        final AjaxFallbackDefaultDataTable notificationTable = new AjaxFallbackDefaultDataTable("notificationTable",
-                notificationCols, new NotificationProvider(), notificationPaginatorRows);
+        final AjaxFallbackDefaultDataTable<NotificationTO, String> notificationTable =
+                new AjaxFallbackDefaultDataTable<NotificationTO, String>(
+                "notificationTable", notificationCols, new NotificationProvider(), notificationPaginatorRows);
 
         notificationContainer = new WebMarkupContainer("notificationContainer");
         notificationContainer.add(notificationTable);

Modified: syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/NotificationModalPage.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/NotificationModalPage.java?rev=1538902&r1=1538901&r2=1538902&view=diff
==============================================================================
--- syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/NotificationModalPage.java (original)
+++ syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/NotificationModalPage.java Tue Nov  5 08:22:31 2013
@@ -23,17 +23,19 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+import org.apache.syncope.common.to.EventCategoryTO;
 import org.apache.syncope.common.to.NotificationTO;
 import org.apache.syncope.common.types.AttributableType;
 import org.apache.syncope.common.types.IntMappingType;
 import org.apache.syncope.common.types.TraceLevel;
 import org.apache.syncope.common.validation.SyncopeClientCompositeErrorException;
 import org.apache.syncope.console.commons.Constants;
+import org.apache.syncope.console.pages.panels.LoggerCategoryPanel;
 import org.apache.syncope.console.pages.panels.UserSearchPanel;
+import org.apache.syncope.console.rest.LoggerRestClient;
 import org.apache.syncope.console.rest.NotificationRestClient;
 import org.apache.syncope.console.wicket.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
-import org.apache.syncope.console.wicket.markup.html.form.AjaxPalettePanel;
 import org.apache.syncope.console.wicket.markup.html.form.AjaxTextFieldPanel;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -48,7 +50,6 @@ import org.apache.wicket.model.CompoundP
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.PropertyModel;
 import org.apache.wicket.model.ResourceModel;
-import org.apache.wicket.model.util.ListModel;
 import org.apache.wicket.spring.injection.annot.SpringBean;
 import org.apache.wicket.validation.validator.EmailAddressValidator;
 
@@ -59,11 +60,14 @@ class NotificationModalPage extends Base
     @SpringBean
     private NotificationRestClient restClient;
 
+    @SpringBean
+    private LoggerRestClient loggerRestClient;
+
     public NotificationModalPage(final PageReference pageRef, final ModalWindow window,
             final NotificationTO notificationTO, final boolean createFlag) {
 
-        Form form = new Form(FORM, new CompoundPropertyModel(notificationTO));
-        form.setModel(new CompoundPropertyModel(notificationTO));
+        final Form<NotificationTO> form =
+                new Form<NotificationTO>(FORM, new CompoundPropertyModel<NotificationTO>(notificationTO));
 
         final AjaxTextFieldPanel sender = new AjaxTextFieldPanel("sender", getString("sender"),
                 new PropertyModel<String>(notificationTO, "sender"));
@@ -94,16 +98,16 @@ class NotificationModalPage extends Base
         aboutContainer.setOutputMarkupId(true);
 
         form.add(aboutContainer);
-        
+
         final AjaxCheckBoxPanel checkAbout =
                 new AjaxCheckBoxPanel("checkAbout", "checkAbout",
                 new Model<Boolean>(notificationTO.getAbout() == null));
         aboutContainer.add(checkAbout);
-        
+
         final UserSearchPanel about = new UserSearchPanel.Builder("about").nodeCond(notificationTO.getAbout()).build();
         aboutContainer.add(about);
         about.setEnabled(!checkAbout.getModelObject());
-        
+
         checkAbout.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
 
             private static final long serialVersionUID = -1107858522700306810L;
@@ -143,9 +147,23 @@ class NotificationModalPage extends Base
             }
         });
 
-        final AjaxPalettePanel events = new AjaxPalettePanel("events", new PropertyModel(notificationTO, "events"),
-                new ListModel<String>(restClient.getEvents()));
-        form.add(events);
+        form.add(new LoggerCategoryPanel(
+                "eventSelection",
+                loggerRestClient.listEvents(),
+                new PropertyModel<List<String>>(notificationTO, "events")) {
+
+            private static final long serialVersionUID = 6429053774964787735L;
+
+            @Override
+            protected String[] getListRoles() {
+                return new String[] {};
+            }
+
+            @Override
+            protected String[] getChangeRoles() {
+                return new String[] {};
+            }
+        });
 
         final WebMarkupContainer recipientsContainer = new WebMarkupContainer("recipientsContainer");
         recipientsContainer.setOutputMarkupId(true);
@@ -153,7 +171,7 @@ class NotificationModalPage extends Base
         form.add(recipientsContainer);
 
         final AjaxCheckBoxPanel selfAsRecipient = new AjaxCheckBoxPanel("selfAsRecipient",
-                getString("selfAsRecipient"), new PropertyModel(notificationTO, "selfAsRecipient"));
+                getString("selfAsRecipient"), new PropertyModel<Boolean>(notificationTO, "selfAsRecipient"));
         form.add(selfAsRecipient);
 
         if (createFlag) {
@@ -292,4 +310,10 @@ class NotificationModalPage extends Base
 
         return result;
     }
+
+    private EventCategoryTO getEventCategoryTO(final List<String> events) {
+        final EventCategoryTO res = new EventCategoryTO();
+
+        return res;
+    }
 }

Modified: syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/Reports.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/Reports.java?rev=1538902&r1=1538901&r2=1538902&view=diff
==============================================================================
--- syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/Reports.java (original)
+++ syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/Reports.java Tue Nov  5 08:22:31 2013
@@ -24,17 +24,17 @@ import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import org.apache.syncope.common.to.EventCategoryTO;
 import org.apache.syncope.common.to.ReportTO;
-import org.apache.syncope.common.types.AuditElements.Category;
 import org.apache.syncope.common.types.AuditElements.Result;
 import org.apache.syncope.common.types.AuditLoggerName;
+import org.apache.syncope.common.util.LoggerEventUtils;
 import org.apache.syncope.common.validation.SyncopeClientCompositeErrorException;
 import org.apache.syncope.console.commons.Constants;
 import org.apache.syncope.console.commons.PreferenceManager;
 import org.apache.syncope.console.commons.SortableDataProviderComparator;
-import org.apache.syncope.console.markup.html.list.AltListView;
-import org.apache.syncope.console.pages.panels.JQueryUITabbedPanel;
+import org.apache.syncope.console.pages.panels.LoggerCategoryPanel;
+import org.apache.syncope.console.pages.panels.SelectedEventsPanel;
 import org.apache.syncope.console.rest.LoggerRestClient;
 import org.apache.syncope.console.wicket.ajax.markup.html.ClearIndicatingAjaxLink;
 import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
@@ -44,36 +44,28 @@ import org.apache.syncope.console.wicket
 import org.apache.wicket.Component;
 import org.apache.wicket.Page;
 import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
 import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.IEvent;
 import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
 import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
 import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
-import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
-import org.apache.wicket.extensions.markup.html.tabs.ITab;
 import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.form.Check;
-import org.apache.wicket.markup.html.form.CheckGroup;
-import org.apache.wicket.markup.html.form.CheckGroupSelector;
 import org.apache.wicket.markup.html.form.DropDownChoice;
 import org.apache.wicket.markup.html.form.Form;
-import org.apache.wicket.markup.html.list.ListItem;
-import org.apache.wicket.markup.html.list.ListView;
-import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.AbstractReadOnlyModel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.PropertyModel;
 import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.util.ListModel;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.spring.injection.annot.SpringBean;
-import org.springframework.util.StringUtils;
+import org.springframework.util.CollectionUtils;
 
 /**
  * Auditing and Reporting.
@@ -222,8 +214,8 @@ public class Reports extends BasePage {
             }
         });
 
-        final AjaxFallbackDefaultDataTable reportTable = new AjaxFallbackDefaultDataTable("reportTable", columns,
-                new ReportProvider(), paginatorRows);
+        final AjaxFallbackDefaultDataTable reportTable =
+                new AjaxFallbackDefaultDataTable("reportTable", columns, new ReportProvider(), paginatorRows);
 
         reportContainer.add(reportTable);
         reportContainer.setOutputMarkupId(true);
@@ -286,27 +278,90 @@ public class Reports extends BasePage {
         auditContainer.setOutputMarkupId(true);
         add(auditContainer);
 
-        MetaDataRoleAuthorizationStrategy.authorize(auditContainer, RENDER, xmlRolesReader.getAllAllowedRoles("Audit",
-                "list"));
+        MetaDataRoleAuthorizationStrategy.authorize(
+                auditContainer, RENDER, xmlRolesReader.getAllAllowedRoles("Audit", "list"));
 
-        Form form = new Form("auditForm");
+        final Form form = new Form("auditForm");
         auditContainer.add(form);
 
-        List<ITab> tabs = new ArrayList<ITab>();
+        final List<String> events = new ArrayList<String>();
 
-        for (final Category category : Category.values()) {
-            tabs.add(new AbstractTab(new Model<String>(StringUtils.capitalize(category.name()))) {
+        final List<AuditLoggerName> audits = loggerRestClient.listAudits();
+        for (AuditLoggerName audit : audits) {
+            events.add(LoggerEventUtils.buildEvent(
+                    audit.getType(),
+                    audit.getCategory(),
+                    audit.getSubcategory(),
+                    audit.getEvent(),
+                    audit.getResult()));
+        }
 
-                private static final long serialVersionUID = -5861786415855103549L;
+        final ListModel<String> model = new ListModel<String>(new ArrayList<String>(events));
 
-                @Override
-                public WebMarkupContainer getPanel(final String panelId) {
-                    return new AuditCategoryPanel(panelId, category);
-                }
-            });
-        }
+        form.add(new LoggerCategoryPanel("events", loggerRestClient.listEvents(), model) {
+
+            private static final long serialVersionUID = 6113164334533550277L;
+
+            @Override
+            protected String[] getListRoles() {
+                return new String[] {
+                    xmlRolesReader.getAllAllowedRoles("Audit", "list")
+                };
+            }
+
+            @Override
+            protected String[] getChangeRoles() {
+                return new String[] {
+                    xmlRolesReader.getAllAllowedRoles("Audit", "enable"),
+                    xmlRolesReader.getAllAllowedRoles("Audit", "disable")
+                };
+            }
+
+            @Override
+            public void onEventAction(final IEvent<?> event) {
+                if (event.getPayload() instanceof SelectedEventsPanel.EventSelectionChanged) {
 
-        form.add(new JQueryUITabbedPanel("categoriesTabs", tabs));
+                    final SelectedEventsPanel.EventSelectionChanged eventSelectionChanged =
+                            (SelectedEventsPanel.EventSelectionChanged) event.getPayload();
+
+                    for (String toBeRemoved : eventSelectionChanged.getToBeRemoved()) {
+                        if (events.contains(toBeRemoved)) {
+                            final Map.Entry<EventCategoryTO, Result> eventCategory =
+                                    LoggerEventUtils.parseEventCategory(toBeRemoved);
+
+                            final AuditLoggerName auditLoggerName = new AuditLoggerName(
+                                    eventCategory.getKey().getType(),
+                                    eventCategory.getKey().getCategory(),
+                                    eventCategory.getKey().getSubcategory(),
+                                    CollectionUtils.isEmpty(eventCategory.getKey().getEvents())
+                                    ? null : eventCategory.getKey().getEvents().iterator().next(),
+                                    eventCategory.getValue());
+
+                            loggerRestClient.disableAudit(auditLoggerName);
+                            events.remove(toBeRemoved);
+                        }
+                    }
+
+                    for (String toBeAdded : eventSelectionChanged.getToBeAdded()) {
+                        if (!events.contains(toBeAdded)) {
+                            final Map.Entry<EventCategoryTO, Result> eventCategory =
+                                    LoggerEventUtils.parseEventCategory(toBeAdded);
+
+                            final AuditLoggerName auditLoggerName = new AuditLoggerName(
+                                    eventCategory.getKey().getType(),
+                                    eventCategory.getKey().getCategory(),
+                                    eventCategory.getKey().getSubcategory(),
+                                    CollectionUtils.isEmpty(eventCategory.getKey().getEvents())
+                                    ? null : eventCategory.getKey().getEvents().iterator().next(),
+                                    eventCategory.getValue());
+
+                            loggerRestClient.enableAudit(auditLoggerName);
+                            events.add(toBeAdded);
+                        }
+                    }
+                }
+            }
+        });
     }
 
     private class ReportProvider extends SortableDataProvider<ReportTO, String> {
@@ -323,11 +378,8 @@ public class Reports extends BasePage {
 
         @Override
         public Iterator<ReportTO> iterator(final long first, final long count) {
-
-            List<ReportTO> list = reportRestClient.list(((int) first / paginatorRows) + 1, paginatorRows);
-
+            final List<ReportTO> list = reportRestClient.list(((int) first / paginatorRows) + 1, paginatorRows);
             Collections.sort(list, comparator);
-
             return list.iterator();
         }
 
@@ -350,152 +402,4 @@ public class Reports extends BasePage {
             };
         }
     }
-
-    private class AuditsByCategoryModel implements IModel<List<AuditLoggerName>> {
-
-        private static final long serialVersionUID = 605983084097505724L;
-
-        private final Category category;
-
-        private final Result result;
-
-        public AuditsByCategoryModel(final Category category, final Result result) {
-            this.category = category;
-            this.result = result;
-        }
-
-        @Override
-        public List<AuditLoggerName> getObject() {
-            Map<Category, Set<AuditLoggerName>> audits = loggerRestClient.listAuditsByCategory();
-
-            List<AuditLoggerName> object = new ArrayList<AuditLoggerName>();
-            for (Enum<?> subcategory : category.getSubCategoryElements()) {
-                AuditLoggerName auditLoggerName = new AuditLoggerName(category, subcategory, result);
-                if (audits.containsKey(category) && audits.get(category).contains(auditLoggerName)) {
-                    object.add(auditLoggerName);
-                }
-            }
-
-            return object;
-        }
-
-        @Override
-        public void setObject(final List<AuditLoggerName> object) {
-            for (Enum<?> subcategory : category.getSubCategoryElements()) {
-                AuditLoggerName auditLoggerName = new AuditLoggerName(category, subcategory, result);
-
-                if (object.contains(auditLoggerName)) {
-                    loggerRestClient.enableAudit(auditLoggerName);
-                } else {
-                    loggerRestClient.disableAudit(auditLoggerName);
-                }
-            }
-        }
-
-        @Override
-        public void detach() {
-            // Not needed.
-        }
-    }
-
-    private class AuditCategoryPanel extends Panel {
-
-        private static final long serialVersionUID = 1076251735476895253L;
-
-        public AuditCategoryPanel(final String id, final Category category) {
-            super(id);
-            setOutputMarkupId(true);
-
-            final CheckGroup<AuditLoggerName> successGroup = new CheckGroup<AuditLoggerName>("successGroup",
-                    new AuditsByCategoryModel(category, Result.success));
-            successGroup.add(new AjaxFormChoiceComponentUpdatingBehavior() {
-
-                private static final long serialVersionUID = -151291731388673682L;
-
-                @Override
-                protected void onUpdate(final AjaxRequestTarget target) {
-                    // Empty method: here only to let Model.setObject() be invoked.
-                }
-            });
-            add(successGroup);
-            authorizeComponent(successGroup);
-
-            final CheckGroupSelector successSelector = new CheckGroupSelector("successSelector", successGroup);
-            add(successSelector);
-            authorizeComponent(successSelector);
-
-            final CheckGroup<AuditLoggerName> failureGroup = new CheckGroup<AuditLoggerName>("failureGroup",
-                    new AuditsByCategoryModel(category, Result.failure));
-            failureGroup.add(new AjaxFormChoiceComponentUpdatingBehavior() {
-
-                private static final long serialVersionUID = -151291731388673682L;
-
-                @Override
-                protected void onUpdate(final AjaxRequestTarget target) {
-                    // Empty method: here only to let Model.setObject() be invoked.
-                }
-            });
-            add(failureGroup);
-            authorizeComponent(failureGroup);
-
-            final CheckGroupSelector failureSelector = new CheckGroupSelector("failureSelector", failureGroup);
-            add(failureSelector);
-            authorizeComponent(failureSelector);
-
-            ListView<Enum<?>> categoryView =
-                    new AltListView<Enum<?>>("categoryView", new ArrayList(category.getSubCategoryElements())) {
-
-                private static final long serialVersionUID = 4949588177564901031L;
-
-                @Override
-                protected void populateItem(final ListItem<Enum<?>> item) {
-                    final Enum<?> subcategory = item.getModelObject();
-
-                    item.add(new Label("subcategory", subcategory.name()));
-                }
-            };
-            add(categoryView);
-
-            ListView<Enum<?>> successView =
-                    new AltListView<Enum<?>>("successView", new ArrayList(category.getSubCategoryElements())) {
-
-                private static final long serialVersionUID = 4949588177564901031L;
-
-                @Override
-                protected void populateItem(final ListItem<Enum<?>> item) {
-                    final Enum<?> subcategory = item.getModelObject();
-
-                    final Check<AuditLoggerName> successCheck = new Check<AuditLoggerName>("successCheck",
-                            new Model<AuditLoggerName>(
-                            new AuditLoggerName(category, subcategory, Result.success)), successGroup);
-                    item.add(successCheck);
-                }
-            };
-            successGroup.add(successView);
-
-            ListView<Enum<?>> failureView =
-                    new AltListView<Enum<?>>("failureView", new ArrayList(category.getSubCategoryElements())) {
-
-                private static final long serialVersionUID = 4949588177564901031L;
-
-                @Override
-                protected void populateItem(final ListItem<Enum<?>> item) {
-                    final Enum<?> subcategory = item.getModelObject();
-
-                    final Check<AuditLoggerName> failureCheck = new Check<AuditLoggerName>("failureCheck",
-                            new Model<AuditLoggerName>(
-                            new AuditLoggerName(category, subcategory, Result.failure)), failureGroup);
-                    item.add(failureCheck);
-                }
-            };
-            failureGroup.add(failureView);
-        }
-    }
-
-    private void authorizeComponent(final Component component) {
-        MetaDataRoleAuthorizationStrategy.authorize(component, RENDER,
-                xmlRolesReader.getAllAllowedRoles("Audit", "enable"));
-        MetaDataRoleAuthorizationStrategy.authorize(component, RENDER,
-                xmlRolesReader.getAllAllowedRoles("Audit", "disable"));
-    }
 }

Added: syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/EventSelectionPanel.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/EventSelectionPanel.java?rev=1538902&view=auto
==============================================================================
--- syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/EventSelectionPanel.java (added)
+++ syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/EventSelectionPanel.java Tue Nov  5 08:22:31 2013
@@ -0,0 +1,233 @@
+/*
+ * 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.console.pages.panels;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang.StringUtils;
+import org.apache.syncope.common.to.EventCategoryTO;
+import org.apache.syncope.common.types.AuditElements;
+import org.apache.syncope.common.util.LoggerEventUtils;
+import org.apache.syncope.console.markup.html.list.AltListView;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Check;
+import org.apache.wicket.markup.html.form.CheckGroup;
+import org.apache.wicket.markup.html.form.CheckGroupSelector;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class EventSelectionPanel extends Panel {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(EventSelectionPanel.class);
+
+    private static final long serialVersionUID = 752233163798301002L;
+
+    private Set<String> selected = new HashSet<String>();
+
+    public EventSelectionPanel(
+            final String id, final EventCategoryTO eventCategoryTO, final IModel<List<String>> model) {
+        super(id);
+        setOutputMarkupId(true);
+
+        final List<String> events = getEvents(eventCategoryTO);
+
+        // needed to avoid model reset: model have to be managed into SelectedEventsPanel
+        selected.addAll(model.getObject());
+
+        final CheckGroup<String> successGroup = new CheckGroup<String>(
+                "successGroup",
+                selected);
+
+        successGroup.add(new AjaxFormChoiceComponentUpdatingBehavior() {
+
+            private static final long serialVersionUID = -151291731388673682L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+
+                final Set<String> toBeRemoved = new HashSet<String>();
+                final Set<String> toBeAdded = new HashSet<String>();
+
+                for (String event : getEvents(eventCategoryTO)) {
+                    final String eventString = LoggerEventUtils.buildEvent(
+                            eventCategoryTO.getType(),
+                            eventCategoryTO.getCategory(),
+                            eventCategoryTO.getSubcategory(),
+                            event,
+                            AuditElements.Result.SUCCESS);
+
+                    if (successGroup.getModelObject().contains(eventString)) {
+                        toBeAdded.add(eventString);
+                    } else {
+                        toBeRemoved.add(eventString);
+                    }
+                }
+
+                send(EventSelectionPanel.this.getPage(), Broadcast.BREADTH,
+                        new SelectedEventsPanel.EventSelectionChanged(target, toBeAdded, toBeRemoved));
+            }
+        });
+
+        successGroup.setVisible(!events.isEmpty());
+        add(successGroup);
+
+        add(new Label("successLabel", new ResourceModel("Success", "Success"))).setVisible(!events.isEmpty());
+
+        final CheckGroupSelector successSelector = new CheckGroupSelector("successSelector", successGroup);
+        successSelector.setVisible(!events.isEmpty());
+        add(successSelector);
+
+        final ListView<String> categoryView = new AltListView<String>("categoryView", events) {
+
+            private static final long serialVersionUID = 4949588177564901031L;
+
+            @Override
+            protected void populateItem(final ListItem<String> item) {
+                final String subcategory = item.getModelObject();
+
+                item.add(new Label("subcategory", new ResourceModel(subcategory, subcategory)));
+            }
+        };
+        add(categoryView);
+
+        final ListView<String> successView = new AltListView<String>("successView", events) {
+
+            private static final long serialVersionUID = 4949588177564901031L;
+
+            @Override
+            protected void populateItem(final ListItem<String> item) {
+                final String event = item.getModelObject();
+
+                final Check<String> successCheck = new Check<String>("successCheck",
+                        new Model<String>(LoggerEventUtils.buildEvent(
+                        eventCategoryTO.getType(),
+                        eventCategoryTO.getCategory(),
+                        eventCategoryTO.getSubcategory(),
+                        event,
+                        AuditElements.Result.SUCCESS)),
+                        successGroup);
+                item.add(successCheck);
+            }
+        };
+        successGroup.add(successView);
+
+        final CheckGroup<String> failureGroup = new CheckGroup<String>(
+                "failureGroup",
+                selected);
+
+        failureGroup.add(new AjaxFormChoiceComponentUpdatingBehavior() {
+
+            private static final long serialVersionUID = -151291731388673682L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+
+                final Set<String> toBeRemoved = new HashSet<String>();
+                final Set<String> toBeAdded = new HashSet<String>();
+
+                for (String event : getEvents(eventCategoryTO)) {
+                    final String eventString = LoggerEventUtils.buildEvent(
+                            eventCategoryTO.getType(),
+                            eventCategoryTO.getCategory(),
+                            eventCategoryTO.getSubcategory(),
+                            event,
+                            AuditElements.Result.FAILURE);
+
+                    if (failureGroup.getModelObject().contains(eventString)) {
+                        toBeAdded.add(eventString);
+                    } else {
+                        toBeRemoved.add(eventString);
+                    }
+                }
+
+                send(EventSelectionPanel.this.getPage(), Broadcast.BREADTH,
+                        new SelectedEventsPanel.EventSelectionChanged(target, toBeAdded, toBeRemoved));
+            }
+        });
+
+        failureGroup.setVisible(!events.isEmpty());
+        add(failureGroup);
+
+        add(new Label("failureLabel", new ResourceModel("Failure", "Failure"))).setVisible(!events.isEmpty());
+
+        final CheckGroupSelector failureSelector = new CheckGroupSelector("failureSelector", failureGroup);
+        failureSelector.setVisible(!events.isEmpty());
+        add(failureSelector);
+
+        final ListView<String> failureView = new AltListView<String>("failureView", events) {
+
+            private static final long serialVersionUID = 4949588177564901031L;
+
+            @Override
+            protected void populateItem(final ListItem<String> item) {
+                final String event = item.getModelObject();
+
+                final Check<String> failureCheck = new Check<String>("failureCheck",
+                        new Model<String>(LoggerEventUtils.buildEvent(
+                        eventCategoryTO.getType(),
+                        eventCategoryTO.getCategory(),
+                        eventCategoryTO.getSubcategory(),
+                        event,
+                        AuditElements.Result.FAILURE)),
+                        failureGroup);
+                item.add(failureCheck);
+            }
+        };
+        failureGroup.add(failureView);
+    }
+
+    private List<String> getEvents(final EventCategoryTO eventCategoryTO) {
+        final List<String> res;
+
+        res = eventCategoryTO.getEvents();
+
+        if (res.isEmpty()) {
+            if ((AuditElements.EventCategoryType.PROPAGATION == eventCategoryTO.getType()
+                    || AuditElements.EventCategoryType.SYNCHRONIZATION == eventCategoryTO.getType())
+                    && StringUtils.isEmpty(eventCategoryTO.getCategory())) {
+                res.add(eventCategoryTO.getType().toString());
+            } else if (AuditElements.EventCategoryType.TASK == eventCategoryTO.getType()
+                    && StringUtils.isNotEmpty(eventCategoryTO.getCategory())) {
+                res.add(eventCategoryTO.getCategory());
+            }
+        } else {
+            Collections.sort(res);
+        }
+
+        return res;
+    }
+
+    @Override
+    public abstract void onEvent(final IEvent<?> event);
+}

Added: syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/LoggerCategoryPanel.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/LoggerCategoryPanel.java?rev=1538902&view=auto
==============================================================================
--- syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/LoggerCategoryPanel.java (added)
+++ syncope/branches/1_1_X/console/src/main/java/org/apache/syncope/console/pages/panels/LoggerCategoryPanel.java Tue Nov  5 08:22:31 2013
@@ -0,0 +1,316 @@
+/*
+ * 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.console.pages.panels;
+
+import static org.apache.wicket.Component.RENDER;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang.StringUtils;
+import org.apache.syncope.common.to.EventCategoryTO;
+import org.apache.syncope.common.types.AuditElements.EventCategoryType;
+import org.apache.syncope.console.commons.Constants;
+import org.apache.syncope.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class LoggerCategoryPanel extends Panel {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(LoggerCategoryPanel.class);
+
+    private static final long serialVersionUID = 6429053774964787734L;
+
+    private final List<EventCategoryTO> eventCategoryTOs;
+
+    private final EventCategoryTO eventCategoryTO = new EventCategoryTO();
+
+    private final WebMarkupContainer categoryContainer;
+
+    private final WebMarkupContainer eventsContainer;
+
+    private final SelectedEventsPanel selectedEventsPanel;
+
+    private final AjaxDropDownChoicePanel<EventCategoryType> type;
+
+    private final AjaxDropDownChoicePanel<String> category;
+
+    private final AjaxDropDownChoicePanel<String> subcategory;
+
+    private final IModel<List<String>> model;
+
+    public LoggerCategoryPanel(
+            final String id,
+            final List<EventCategoryTO> eventCategoryTOs,
+            final IModel<List<String>> model) {
+        super(id);
+
+        this.model = model;
+        selectedEventsPanel = new SelectedEventsPanel("selectedEventsPanel", model);
+        add(selectedEventsPanel);
+
+        this.eventCategoryTOs = eventCategoryTOs;
+
+        categoryContainer = new WebMarkupContainer("categoryContainer");
+        categoryContainer.setOutputMarkupId(true);
+        add(categoryContainer);
+
+        eventsContainer = new WebMarkupContainer("eventsContainer");
+        eventsContainer.setOutputMarkupId(true);
+        add(eventsContainer);
+
+        authorizeList();
+        authorizeChanges();
+
+        categoryContainer.add(new Label("typeLabel", new ResourceModel("type", "type")));
+
+        type = new AjaxDropDownChoicePanel<EventCategoryType>(
+                "type",
+                "type",
+                new PropertyModel<EventCategoryType>(eventCategoryTO, "type"),
+                false);
+        type.setChoices(Arrays.asList(EventCategoryType.values()));
+        type.setStyleSheet("ui-widget-content ui-corner-all");
+        type.setChoiceRenderer(new IChoiceRenderer<EventCategoryType>() {
+
+            private static final long serialVersionUID = 2317134950949778735L;
+
+            @Override
+            public String getDisplayValue(final EventCategoryType eventCategoryType) {
+                return eventCategoryType.name();
+            }
+
+            @Override
+            public String getIdValue(final EventCategoryType eventCategoryType, final int i) {
+                return eventCategoryType.name();
+            }
+        });
+        categoryContainer.add(type);
+
+        type.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                send(LoggerCategoryPanel.this, Broadcast.EXACT, new ChangeCategoryEvent(target, type));
+            }
+        });
+
+        categoryContainer.add(new Label("categoryLabel", new ResourceModel("category", "category")));
+
+        category = new AjaxDropDownChoicePanel<String>(
+                "category",
+                "category",
+                new PropertyModel<String>(eventCategoryTO, "category"),
+                false);
+        category.setChoices(filter(eventCategoryTOs, type.getModelObject()));
+        category.setStyleSheet("ui-widget-content ui-corner-all");
+        categoryContainer.add(category);
+
+        category.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306811L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                send(LoggerCategoryPanel.this, Broadcast.EXACT, new ChangeCategoryEvent(target, category));
+            }
+        });
+
+        categoryContainer.add(new Label("subcategoryLabel", new ResourceModel("subcategory", "subcategory")));
+
+        subcategory = new AjaxDropDownChoicePanel<String>(
+                "subcategory",
+                "subcategory",
+                new PropertyModel<String>(eventCategoryTO, "subcategory"),
+                false);
+        subcategory.setChoices(filter(eventCategoryTOs, type.getModelObject(), category.getModelObject()));
+        subcategory.setStyleSheet("ui-widget-content ui-corner-all");
+        categoryContainer.add(subcategory);
+
+        subcategory.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306812L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                send(LoggerCategoryPanel.this, Broadcast.EXACT, new ChangeCategoryEvent(target, subcategory));
+            }
+        });
+
+        eventsContainer.add(new EventSelectionPanel("eventsPanel", eventCategoryTO, model) {
+
+            private static final long serialVersionUID = 3513194801190026082L;
+
+            @Override
+            public void onEvent(final IEvent<?> event) {
+                onEventAction(event);
+            }
+        });
+    }
+
+    private List<String> filter(
+            final List<EventCategoryTO> eventCategoryTOs, final EventCategoryType type) {
+        final Set<String> res = new HashSet<String>();
+
+        for (EventCategoryTO eventCategory : eventCategoryTOs) {
+            if (type == eventCategory.getType() && StringUtils.isNotEmpty(eventCategory.getCategory())) {
+                res.add(eventCategory.getCategory());
+            }
+        }
+
+        final List<String> filtered = new ArrayList<String>(res);
+        Collections.sort(filtered);
+        return filtered;
+    }
+
+    private List<String> filter(
+            final List<EventCategoryTO> eventCategoryTOs, final EventCategoryType type, final String category) {
+        final Set<String> res = new HashSet<String>();
+
+        for (EventCategoryTO eventCategory : eventCategoryTOs) {
+            if (type == eventCategory.getType() && StringUtils.equals(category, eventCategory.getCategory())
+                    && StringUtils.isNotEmpty(eventCategory.getSubcategory())) {
+                res.add(eventCategory.getSubcategory());
+            }
+        }
+
+        final List<String> filtered = new ArrayList<String>(res);
+        Collections.sort(filtered);
+        return filtered;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof ChangeCategoryEvent) {
+            // update objects ....
+            eventCategoryTO.getEvents().clear();
+
+            final ChangeCategoryEvent change = (ChangeCategoryEvent) event.getPayload();
+
+            final Panel changedPanel = change.getChangedPanel();
+            if ("type".equals(changedPanel.getId())) {
+                category.setChoices(filter(eventCategoryTOs, type.getModelObject()));
+                subcategory.setChoices(Collections.<String>emptyList());
+                eventCategoryTO.setType(type.getModelObject());
+                eventCategoryTO.setCategory(null);
+                eventCategoryTO.setSubcategory(null);
+                change.getTarget().add(categoryContainer);
+            } else if ("category".equals(changedPanel.getId())) {
+                subcategory.setChoices(filter(eventCategoryTOs, type.getModelObject(), category.getModelObject()));
+                eventCategoryTO.setCategory(category.getModelObject());
+                eventCategoryTO.setSubcategory(null);
+                change.getTarget().add(categoryContainer);
+            } else {
+                eventCategoryTO.setSubcategory(subcategory.getModelObject());
+            }
+
+            setEvents();
+
+            eventsContainer.addOrReplace(new EventSelectionPanel("eventsPanel", eventCategoryTO, model) {
+
+                private static final long serialVersionUID = 3513194801190026082L;
+
+                @Override
+                public void onEvent(final IEvent<?> event) {
+                    onEventAction(event);
+                }
+            });
+            change.getTarget().add(eventsContainer);
+        }
+    }
+
+    private void setEvents() {
+        final Iterator<EventCategoryTO> itor = eventCategoryTOs.iterator();
+        while (itor.hasNext() && eventCategoryTO.getEvents().isEmpty()) {
+            final EventCategoryTO eventCategory = itor.next();
+            if (eventCategory.getType() == eventCategoryTO.getType()
+                    && StringUtils.equals(eventCategory.getCategory(), eventCategoryTO.getCategory())
+                    && StringUtils.equals(eventCategory.getSubcategory(), eventCategoryTO.getSubcategory())) {
+                eventCategoryTO.getEvents().addAll(eventCategory.getEvents());
+            }
+        }
+    }
+
+    private class ChangeCategoryEvent {
+
+        private final AjaxRequestTarget target;
+
+        private final Panel changedPanel;
+
+        public ChangeCategoryEvent(final AjaxRequestTarget target, final Panel changedPanel) {
+            this.target = target;
+            this.changedPanel = changedPanel;
+        }
+
+        public AjaxRequestTarget getTarget() {
+            return target;
+        }
+
+        public Panel getChangedPanel() {
+            return changedPanel;
+        }
+    }
+
+    /**
+     * To be extended in order to add actions on events.
+     *
+     * @param event event.
+     */
+    protected void onEventAction(final IEvent<?> event) {
+        // nothing by default
+    }
+
+    private void authorizeList() {
+        for (String role : getListRoles()) {
+            MetaDataRoleAuthorizationStrategy.authorize(selectedEventsPanel, RENDER, role);
+        }
+    }
+
+    private void authorizeChanges() {
+        for (String role : getChangeRoles()) {
+            MetaDataRoleAuthorizationStrategy.authorize(categoryContainer, RENDER, role);
+            MetaDataRoleAuthorizationStrategy.authorize(eventsContainer, RENDER, role);
+        }
+    }
+
+    protected abstract String[] getListRoles();
+
+    protected abstract String[] getChangeRoles();
+}