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 2016/04/08 18:30:12 UTC

[1/4] syncope git commit: [SYNCOPE-745] Provides notification and email template management. Still missing tests and tasks per notification.

Repository: syncope
Updated Branches:
  refs/heads/master 91befa207 -> 8455cb96b


http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients_it.properties
new file mode 100644
index 0000000..4ee971c
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients_it.properties
@@ -0,0 +1,22 @@
+# 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.
+recipients=Destinatari
+staticRecipients=Destinatari
+selfAsRecipient=Includi i soggetti
+userNotifications=Notifiche utente
+userNotificationsWarning=Non selezionare questa checkbox se gli eventi catturati non riguardano espressamente utenti
+search=Cerca destinatari

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients_pt_BR.properties
new file mode 100644
index 0000000..33da973
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients_pt_BR.properties
@@ -0,0 +1,22 @@
+# 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.
+recipients=Destinat\u00e1rios
+staticRecipients=Destinat\u00e1rios
+selfAsRecipient=Incluir usu\u00e1rios relacionados Sobre
+userNotifications=Notifica\u00e7\u00f5es do usu\u00e1rio
+userNotificationsWarning=N\u00e3o selecione esta op\u00e7\u00e3o a menos que os eventos n\u00e3o s\u00e3o direcionados a usu\u00e1rios
+search=Pesquisa para os destinat\u00e1rios

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/SelectedEventsPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/SelectedEventsPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/SelectedEventsPanel.html
new file mode 100644
index 0000000..3ef0354
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/SelectedEventsPanel.html
@@ -0,0 +1,33 @@
+<!--
+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>
+    <style>
+      div#selectionContainer select{
+        width: 585px;
+        min-width: 585px;
+      }
+    </style>
+  </wicket:head>
+  <wicket:panel>
+    <div id="selectionContainer" wicket:id="selectionContainer">
+      <select wicket:id="selectedEvents" ></select>
+    </div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications.html b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications.html
index a2a9242..7e23e5b 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications.html
@@ -1,4 +1,4 @@
-s<!--
+<!--
 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
@@ -18,26 +18,18 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:extend>
-
     <section class="content-header">
-      <h1>
-        &nbsp;
-        <small>Work in progress</small>
-      </h1>
+      <h1>&nbsp;</h1>
       <ol class="breadcrumb">
-        <li><a wicket:id="dashboardBr"><i class="fa fa-dashboard"></i> <wicket:message key="dashboard"/></a></li>
-        <li class="active"><wicket:message key="configuration"/></li>
-        <li class="active"><wicket:message key="notifications"/></li>
+        <li><a wicket:id="dashboardBr"><i class="fa fa-dashboard"></i> <wicket:message key="dashboard"></wicket:message></a></li>
+        <li class="active" id="pageTitleInBradcrumb"><wicket:message key="notifications"></wicket:message></li>
       </ol>
     </section>
 
-    <section class="content">
-      <div class="progress progress active" style="margin:100px">
-        <div style="width: 70%" aria-valuemax="100" aria-valuemin="0" aria-valuenow="20" role="progressbar" class="progress-bar progress-bar-success progress-bar-striped">
-          <span class="sr-only">20% Complete</span>
-        </div>
+    <section class="content" wicket:id="content">
+      <div class="box">
+        <div class="box-body" wicket:id="tabbedPanel"/>
       </div>
     </section>
-
   </wicket:extend>
 </html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications.properties b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications.properties
new file mode 100644
index 0000000..22593e0
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications.properties
@@ -0,0 +1,46 @@
+# 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.
+notifications=Notifications
+notification.templates=Templates
+notification.executions=Executions
+
+subject=Subject
+sender=Sender
+template=Template
+traceLevel=Trace level
+notification=Notification
+about=About
+events=Events
+recipients=Recipients
+selfAsRecipient=Include users matching About
+recipientAttrType=Recipients e-mail attribute type
+recipientAttrName=Recipients e-mail attribute schema
+checkRecipients=Search recipients
+checkAbout=All
+userFilter=User filter
+userFilterWarning=Do not use this filter unless events are not targeted at users
+userNotifications=User notifications
+userNotificationsWarning=Do not select this checkbox unless events are not targeted at users
+roleFilter=Role filter
+roleFilterWarning=Do not use this filter unless events are not targeted at roles
+roleNotifications=Role notifications
+roleNotificationsWarning=Do not select this checkbox unless events are not targeted at roles
+isActive=Enabled
+checkUserAbout=Users
+checkRoleAbout=Roles
+
+active=Active

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications_it.properties
new file mode 100644
index 0000000..9d7fafa
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications_it.properties
@@ -0,0 +1,46 @@
+# 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.
+notifications=Notifiche
+notification.templates=Template
+notification.executions=Execuzioni
+
+subject=Oggetto
+sender=Mittente
+template=Modello
+traceLevel=Livello di tracciamento
+notification=Notifica
+about=Soggetti
+events=Eventi
+recipients=Destinatari
+selfAsRecipient=Includi i soggetti
+recipientAttrType=Tipo attributo e-mail destinatari
+recipientAttrName=Schema attributo e-mail destinatari
+checkRecipients=Cerca i destinatari
+checkAbout=Tutto
+userFilter=Filtro utenti
+userFilterWarning=Non usare questo filtro se gli eventi catturati non riguardano espressamente utenti
+userNotifications=Notifiche utente
+userNotificationsWarning=Non selezionare questa checkbox se gli eventi catturati non riguardano espressamente utenti
+roleFilter=Filtro ruoli
+roleFilterWarning=Non usare questo filtro se gli eventi catturati non riguardano espressamente i ruoli
+roleNotifications=Notifiche ruoli
+roleNotificationsWarning=Non selezionare questa checkbox se gli eventi catturati non riguardano espressamente ruoli
+isActive=Abilitata
+checkUserAbout=Utenti
+checkRoleAbout=Ruoli
+
+active=Attivo

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications_pt_BR.properties
new file mode 100644
index 0000000..941d1af
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Notifications_pt_BR.properties
@@ -0,0 +1,46 @@
+# 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.
+notifications=Notifica\u00e7\u00f5es
+notification.templates=Template
+notification.executions=Execu\u00e7\u00f5es
+
+subject=Assunto
+sender=Remetente
+template=Template
+traceLevel=N\u00edvel de Trace
+notification=Notifica\u00e7\u00e3o
+about=Sobre
+events=Eventos
+recipients=Destinat\u00e1rios
+selfAsRecipient=Incluir usu\u00e1rios relacionados Sobre
+recipientAttrType=Tipo de atributo de destinat\u00e1rio de email.
+recipientAttrName=Destinat\u00e1rios de e-mail para atributos de esquema
+checkRecipients=Buscar Destinat\u00e1rios
+checkAbout=Todos
+userFilter=Filtro usu\u00e1rio
+userFilterWarning=N\u00e3o use este filtro n\u00e3o ser que os eventos n\u00e3o s\u00e3o direcionados a usu\u00e1rios
+roleFilter=Filtro fun\u00e7\u00e3o
+roleFilterWarning=N\u00e3o use este filtro n\u00e3o ser que os eventos n\u00e3o s\u00e3o direcionados a fun\u00e7\u00f5es
+userNotifications=Notifica\u00e7\u00f5es do usu\u00e1rio
+userNotificationsWarning=N\u00e3o selecione esta op\u00e7\u00e3o a menos que os eventos n\u00e3o s\u00e3o direcionados a usu\u00e1rios
+roleNotifications=Notifica\u00e7\u00f5es do fun\u00e7\u00f5es
+roleNotificationsWarning=N\u00e3o selecione esta op\u00e7\u00e3o a menos que os eventos n\u00e3o s\u00e3o direcionados a fun\u00e7\u00f5es
+isActive=Ativada
+checkUserAbout=Usu\u00e1rio
+checkRoleAbout=Fun\u00e7\u00e3o
+
+active=Ativo

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel.properties
index a1c2b96..e73d518 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel.properties
@@ -18,5 +18,3 @@ operation=Operation
 anyTypeKind=Object Type
 anyKey=Local Key
 connObjectKey=Remote Key
-start=Start
-end=End

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel_it.properties
index 60c13cf..a55ca31 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel_it.properties
@@ -18,5 +18,3 @@ operation=Operazione
 anyTypeKind=Tipo Oggetto
 anyKey=Key Locale
 connObjectKey=Key Remota
-start=Inizio
-end=Fine

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel_pt_BR.properties
index a1c2b96..e73d518 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/PropagationTaskDirectoryPanel_pt_BR.properties
@@ -18,5 +18,3 @@ operation=Operation
 anyTypeKind=Object Type
 anyKey=Local Key
 connObjectKey=Remote Key
-start=Start
-end=End

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.properties
index 65ce5d1..bad8066 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.properties
@@ -19,6 +19,8 @@ executions=Executions
 title=Task
 
 key=Id
+start=Start date
+end=End date
 latestExecStatus=Last execution status
 
 task.view=Executions of task '${key}'

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel_it.properties
index 1d1f8e3..cba15ec 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel_it.properties
@@ -19,6 +19,8 @@ executions=Esecuzioni
 title=Task
 
 key=Id
+start=Data di avvio
+end=Data di conclusione
 latestExecStatus=Stato ultima esecuzione
 
 task.view=Esecuzioni del task '${key}'

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel_pt_BR.properties
index 0440e71..f3c118a 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/tasks/TaskDirectoryPanel_pt_BR.properties
@@ -19,6 +19,8 @@ executions=Esecuzioni
 title=Task
 
 key=Id
+start=Data inicial
+end=Data Final
 latestExecStatus=Stato ultima esecuzione
 
 task.view=Executions of task '${key}'

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.html
new file mode 100644
index 0000000..56220a9
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.html
@@ -0,0 +1,25 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div id="collection" wicket:id="collection">
+      <span id="item" wicket:id="item"/>
+    </div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
index eeb94e4..9b1fd08 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
@@ -43,6 +43,8 @@ under the License.
     <span wicket:id="panelCreate">[plus]</span>
     <span wicket:id="panelReset">[plus]</span>
     <span wicket:id="panelEdit">[plus]</span>
+    <span wicket:id="panelHtmlEdit">[plus]</span>
+    <span wicket:id="panelTextEdit">[plus]</span>
     <span wicket:id="panelExport">[plus]</span>
     <span wicket:id="panelPropagationTasks">[plus]</span>
     <span wicket:id="panelDelete">[plus]</span>
@@ -117,6 +119,14 @@ under the License.
       <a href="#" wicket:id="editLink" class="btn"><i id="actionLink" class="glyphicon glyphicon-pencil" alt="edit icon" title="Edit"></i></a>
     </wicket:fragment>
 
+    <wicket:fragment wicket:id="fragmentHtmlEdit">
+      <a href="#" wicket:id="htmlEditLink" class="btn"><i id="actionLink" class="fa fa-file-code-o" alt="html edit icon" title="HTML Edit"></i></a>
+    </wicket:fragment>
+
+    <wicket:fragment wicket:id="fragmentTextEdit">
+      <a href="#" wicket:id="textEditLink" class="btn"><i id="actionLink" class="fa fa-file-text-o" alt="text edit icon" title="TEXT Edit"></i></a>
+    </wicket:fragment>
+
     <wicket:fragment wicket:id="fragmentReset">
       <a href="#" wicket:id="resetLink" class="btn"><img id="actionLink" src="img/actions/reset.png" alt="reset icon" title="Reset sync token"/></a>
     </wicket:fragment>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.properties
deleted file mode 100644
index 958e9e0..0000000
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-# 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.
-minLength=Minimum password size
-maxLength=Maximum password size
-historyLength=Password history length
-digitRequired=Password must contain one or more digits
-prefixesNotPermitted=Password prefixes not permitted
-apply=Save

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel_it.properties
deleted file mode 100644
index 4c92961..0000000
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel_it.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-# 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.
-minLength=Lunghezza minima password
-maxLength=Lunghezza massima password
-historyLength=Lunghezza storico password
-digitRequired=La password deve contenere uno o pi\u00f9 numeri
-prefixesNotPermitted=Prefissi password non permessi
-apply=Salva

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel_pt_BR.properties
deleted file mode 100644
index d4f5693..0000000
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel_pt_BR.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-# 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.
-minLength=Tamanho m\u00EDnimo de senha
-maxLength=Tamanho m\u00E1ximo de senha
-historyLength=Tamanho dos hist\u00F3rico de senha
-digitRequired=Senha precisa contar um ou mais d\u00EDgitos
-prefixesNotPermitted=Prefixos de senha n\u00E3o permitidos
-apply=Salvar

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiPanel.html
new file mode 100644
index 0000000..1282925
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiPanel.html
@@ -0,0 +1,71 @@
+<!--
+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">
+  <head>
+    <title>Multivalue conatiner</title>
+  </head>
+  <body>
+    <wicket:extend>
+      <wicket:enclosure child="field-label">
+        <label wicket:id="field-label">[LABEL]</label><span wicket:id="required"/>
+        <span wicket:id="externalAction"/>
+      </wicket:enclosure>
+
+      <span wicket:id="multiValueContainer">
+        <form wicket:id="innerForm">
+          <span wicket:id="content">[content]</span>
+        </form>
+      </span>
+
+      <wicket:fragment wicket:id="noDataFragment">
+        <div class="form-group">
+          <div class="multipanel-box box input-group" style="background-color:#EEE">
+            <label wicket:id="field-label">[LABEL]</label>
+          </div>
+          <span wicket:id="panelPlus" class="input-group-addon">[plus]</span>
+        </div>
+      </wicket:fragment>
+
+      <wicket:fragment wicket:id="dataFragment">
+        <span wicket:id="view">
+          <div class="form-group">
+            <div class="input-group">
+              <div class="multipanel-box box">
+                <span wicket:id="panel">[form field]</span>
+              </div>
+              <div class="multipanel-btn-minus input-group-addon">
+                <a wicket:id="drop" class="btn btn-danger btn-circle btn-lg"><i class="glyphicon glyphicon-minus"></i></a>
+              </div>
+            </div>
+            <span wicket:id="panelPlus">[plus]</span>
+          </div>
+        </span>
+      </wicket:fragment>
+
+      <wicket:fragment wicket:id="fragmentPlus">
+        <div class="form-group pull-right input-group multipanel-btn-plus">
+          <a wicket:id="add" class="btn btn-primary btn-circle btn-lg"><i class="glyphicon glyphicon-plus"></i></a>
+        </div>
+      </wicket:fragment>
+
+      <wicket:fragment wicket:id="emptyFragment">
+      </wicket:fragment>
+    </wicket:extend>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutesDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutesDirectoryPanel.java b/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutesDirectoryPanel.java
index b859c64..70040e8 100644
--- a/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutesDirectoryPanel.java
+++ b/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutesDirectoryPanel.java
@@ -33,7 +33,6 @@ import org.apache.syncope.client.console.pages.CamelRoutes;
 import org.apache.syncope.client.console.panels.CamelRoutesDirectoryPanel.CamelRoutesProvider;
 import org.apache.syncope.client.console.rest.CamelRoutesRestClient;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
-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.ActionLinksPanel;
 import org.apache.syncope.client.console.wizards.AbstractModalPanelBuilder;
@@ -77,13 +76,12 @@ public class CamelRoutesDirectoryPanel extends DirectoryPanel<
         modal.size(Modal.Size.Large);
         initResultTable();
 
-        this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<CamelRouteTO>(
-                BaseModal.CONTENT_ID, new CamelRouteTO(), pageRef) {
+        this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<CamelRouteTO>(new CamelRouteTO(), pageRef) {
 
             private static final long serialVersionUID = -6388405037134399367L;
 
             @Override
-            public ModalPanel<CamelRouteTO> build(final int index, final AjaxWizard.Mode mode) {
+            public ModalPanel<CamelRouteTO> build(final String id, final int index, final AjaxWizard.Mode mode) {
                 final CamelRouteTO modelObject = newModelObject();
                 return new CamelRoutesModalPanel(modal, modelObject, pageRef) {
 


[4/4] syncope git commit: [SYNCOPE-745] Provides notification and email template management. Still missing tests and tasks per notification.

Posted by fm...@apache.org.
[SYNCOPE-745] Provides notification and email template management. Still missing tests and tasks per notification.


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

Branch: refs/heads/master
Commit: 8455cb96be28ce8298dbfe41c073356710a06350
Parents: 91befa2
Author: fmartelli <fa...@gmail.com>
Authored: Fri Apr 8 18:29:45 2016 +0200
Committer: fmartelli <fa...@gmail.com>
Committed: Fri Apr 8 18:29:45 2016 +0200

----------------------------------------------------------------------
 .../console/approvals/ApprovalDetails.java      |   4 +-
 .../client/console/commons/Constants.java       |   2 +
 .../notifications/EventSelectionPanel.java      | 240 +++++++++
 .../notifications/LoggerCategoryPanel.java      | 509 +++++++++++++++++++
 .../notifications/MailTemplateContentModal.java | 117 +++++
 .../MailTemplateDirectoryPanel.java             | 222 ++++++++
 .../notifications/MailTemplateModal.java        |  72 +++
 .../NotificationDirectoryPanel.java             | 208 ++++++++
 .../notifications/NotificationHandler.java      | 131 +++++
 .../notifications/NotificationTasks.java        |  71 +++
 .../NotificationWizardBuilder.java              | 435 ++++++++++++++++
 .../notifications/SelectedEventsPanel.java      | 172 +++++++
 .../client/console/pages/Notifications.java     |  41 ++
 .../syncope/client/console/pages/Roles.java     |   4 +-
 .../syncope/client/console/panels/AnyPanel.java |   7 +-
 .../console/panels/AnyTypeClassesPanel.java     |   6 +-
 .../client/console/panels/AnyTypesPanel.java    |   6 +-
 .../panels/ParametersCreateModalPanel.java      |   4 +-
 .../panels/ParametersCreateWizardPanel.java     |   4 +-
 .../client/console/panels/ParametersPanel.java  |   5 +-
 .../console/panels/RelationshipTypesPanel.java  |   7 +-
 .../client/console/panels/ResourceModal.java    |   6 +-
 .../client/console/panels/SchemaTypePanel.java  |   8 +-
 .../console/panels/SecurityQuestionsPanel.java  |   7 +-
 .../console/rest/NotificationRestClient.java    |  39 ++
 .../tasks/NotificationTaskDirectoryPanel.java   | 208 ++++++++
 .../console/tasks/SchedTaskWizardBuilder.java   |   2 +-
 .../repeater/data/table/CollectionPanel.java    |  47 ++
 .../data/table/CollectionPropertyColumn.java    |  54 ++
 .../markup/html/form/AbstractMultiPanel.java    | 217 ++++++++
 .../wicket/markup/html/form/ActionLink.java     |   2 +
 .../markup/html/form/ActionLinksPanel.java      |  48 ++
 .../markup/html/form/MultiFieldPanel.java       | 197 +------
 .../wicket/markup/html/form/MultiPanel.java     |  37 ++
 .../wizards/AbstractModalPanelBuilder.java      |  16 +-
 .../console/wizards/AjaxWizardBuilder.java      |  29 +-
 .../client/console/wizards/WizardMgtPanel.java  |   6 +
 .../wizards/any/AnyObjectWizardBuilder.java     |   4 +-
 .../console/wizards/any/AnyWizardBuilder.java   |  12 +-
 .../console/wizards/any/GroupHandler.java       |  20 +-
 .../console/wizards/any/GroupWizardBuilder.java |   6 +-
 .../console/wizards/any/UserWizardBuilder.java  |   4 +-
 .../provision/ProvisionWizardBuilder.java       |   5 +-
 .../console/wizards/role/RoleHandler.java       |   5 +-
 .../console/wizards/role/RoleWizardBuilder.java |   5 +-
 .../META-INF/resources/css/syncopeConsole.css   |  38 +-
 .../notifications/EventSelectionPanel.html      | 125 +++++
 .../notifications/LoggerCategoryPanel.html      |  91 ++++
 .../notifications/MailTemplateContentModal.html |  54 ++
 .../MailTemplateDirectoryPanel.properties       |  20 +
 .../MailTemplateDirectoryPanel_it.properties    |  20 +
 .../MailTemplateDirectoryPanel_pt_BR.properties |  20 +
 .../notifications/MailTemplateModal.html        |  28 +
 .../NotificationDirectoryPanel.properties       |  20 +
 .../NotificationDirectoryPanel_it.properties    |  20 +
 .../NotificationDirectoryPanel_pt_BR.properties |  20 +
 .../notifications/NotificationTasks.html        |  24 +
 .../NotificationWizardBuilder$About.html        |  31 ++
 .../NotificationWizardBuilder$Abouts.html       |  28 +
 .../NotificationWizardBuilder$Abouts.properties |  24 +
 ...tificationWizardBuilder$Abouts_it.properties |  24 +
 ...icationWizardBuilder$Abouts_pt_BR.properties |  24 +
 .../NotificationWizardBuilder$Details.html      |  46 ++
 ...NotificationWizardBuilder$Details.properties |  23 +
 ...ificationWizardBuilder$Details_it.properties |  23 +
 ...cationWizardBuilder$Details_pt_BR.properties |  23 +
 .../NotificationWizardBuilder$Events.html       |  27 +
 .../NotificationWizardBuilder$Events.properties |  42 ++
 ...tificationWizardBuilder$Events_it.properties |  42 ++
 ...icationWizardBuilder$Events_pt_BR.properties |  42 ++
 .../NotificationWizardBuilder$Recipients.html   |  43 ++
 ...ificationWizardBuilder$Recipients.properties |  22 +
 ...cationWizardBuilder$Recipients_it.properties |  22 +
 ...ionWizardBuilder$Recipients_pt_BR.properties |  22 +
 .../notifications/SelectedEventsPanel.html      |  33 ++
 .../client/console/pages/Notifications.html     |  22 +-
 .../console/pages/Notifications.properties      |  46 ++
 .../console/pages/Notifications_it.properties   |  46 ++
 .../pages/Notifications_pt_BR.properties        |  46 ++
 .../PropagationTaskDirectoryPanel.properties    |   2 -
 .../PropagationTaskDirectoryPanel_it.properties |   2 -
 ...opagationTaskDirectoryPanel_pt_BR.properties |   2 -
 .../console/tasks/TaskDirectoryPanel.properties |   2 +
 .../tasks/TaskDirectoryPanel_it.properties      |   2 +
 .../tasks/TaskDirectoryPanel_pt_BR.properties   |   2 +
 .../repeater/data/table/CollectionPanel.html    |  25 +
 .../markup/html/form/ActionLinksPanel.html      |  10 +
 .../markup/html/form/MultiFieldPanel.properties |  22 -
 .../html/form/MultiFieldPanel_it.properties     |  22 -
 .../html/form/MultiFieldPanel_pt_BR.properties  |  22 -
 .../wicket/markup/html/form/MultiPanel.html     |  71 +++
 .../panels/CamelRoutesDirectoryPanel.java       |   6 +-
 92 files changed, 4267 insertions(+), 355 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
index 7c954fd..c5787ed 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
@@ -40,8 +40,8 @@ public class ApprovalDetails extends MultilevelPanel.SecondLevel {
         final UserTO userTO = new UserRestClient().read(formTO.getUserKey());
         final List<String> anyTypeClasses = new AnyTypeRestClient().read(AnyTypeKind.USER.name()).getClasses();
 
-        final AjaxWizard<AnyHandler<UserTO>> wizard
-                = new UserWizardBuilder("wizard", userTO, anyTypeClasses, pageRef).build(AjaxWizard.Mode.READONLY);
+        final AjaxWizard<AnyHandler<UserTO>> wizard = new UserWizardBuilder(userTO, anyTypeClasses, pageRef).
+                build(AjaxWizard.Mode.READONLY);
 
         add(wizard);
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/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 def4c51..96d4ba6 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
@@ -105,6 +105,8 @@ public final class Constants {
 
     public static final String PREF_NOTIFICATION_PAGINATOR_ROWS = "notification.paginator.rows";
 
+    public static final String PREF_MAIL_TEMPLATE_PAGINATOR_ROWS = "mail.template.paginator.rows";
+
     public static final String PREF_PROPAGATION_TASKS_PAGINATOR_ROWS = "proagationtasks.paginator.rows";
 
     public static final String PREF_TASK_EXECS_PAGINATOR_ROWS = "task.execs.paginator.rows";

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/notifications/EventSelectionPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/EventSelectionPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/EventSelectionPanel.java
new file mode 100644
index 0000000..38d2dd9
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/EventSelectionPanel.java
@@ -0,0 +1,240 @@
+/*
+ * 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.notifications;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.to.EventCategoryTO;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+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 final 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 = AuditLoggerName.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 ListView<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 ListView<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>(AuditLoggerName.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 = AuditLoggerName.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 ListView<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>(AuditLoggerName.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.PULL == eventCategoryTO.getType()
+                    || AuditElements.EventCategoryType.PUSH == 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;
+    }
+
+    /**
+     * To be extended in order to add actions on events.
+     *
+     * @param event event.
+     */
+    protected abstract void onEventAction(final IEvent<?> event);
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        onEventAction(event);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/notifications/LoggerCategoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/LoggerCategoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/LoggerCategoryPanel.java
new file mode 100644
index 0000000..d1715f1
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/LoggerCategoryPanel.java
@@ -0,0 +1,509 @@
+/*
+ * 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.notifications;
+
+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.Map;
+import java.util.Set;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.notifications.SelectedEventsPanel.EventSelectionChanged;
+import org.apache.syncope.client.console.notifications.SelectedEventsPanel.InspectSelectedEvent;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.lib.to.EventCategoryTO;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.EventCategoryType;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+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.Model;
+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 AjaxTextFieldPanel custom;
+
+    private final ActionLinksPanel actionPanel;
+
+    private final IModel<List<String>> model;
+
+    public LoggerCategoryPanel(
+            final String id,
+            final List<EventCategoryTO> eventCategoryTOs,
+            final IModel<List<String>> model,
+            final PageReference pageReference,
+            final String pageId) {
+        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();
+
+        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();
+            }
+
+            @Override
+            public EventCategoryType getObject(
+                    final String id, final IModel<? extends List<? extends EventCategoryType>> choices) {
+                return IterableUtils.find(choices.getObject(), new Predicate<EventCategoryType>() {
+
+                    @Override
+                    public boolean evaluate(final EventCategoryType object) {
+                        return object.name().equals(id);
+                    }
+                });
+            }
+        });
+        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));
+            }
+        });
+
+        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));
+            }
+        });
+
+        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));
+            }
+        });
+
+        categoryContainer.add(new Label("customLabel", new ResourceModel("custom", "custom")).setVisible(false));
+
+        custom = new AjaxTextFieldPanel("custom", "custom", new Model<String>(null));
+//        custom.setStyleSheet("ui-widget-content ui-corner-all short_fixedsize");
+        custom.setVisible(false);
+        custom.setEnabled(false);
+
+        categoryContainer.add(custom.hideLabel());
+
+        actionPanel = ActionLinksPanel.<EventCategoryTO>builder().
+                add(new ActionLink<EventCategoryTO>() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final EventCategoryTO modelObject) {
+
+                        if (StringUtils.isNotBlank(custom.getModelObject())) {
+                            final Map.Entry<EventCategoryTO, AuditElements.Result> parsed = AuditLoggerName.
+                                    parseEventCategory(
+                                            custom.getModelObject());
+
+                            final String eventString = AuditLoggerName.buildEvent(
+                                    parsed.getKey().getType(),
+                                    null,
+                                    null,
+                                    parsed.getKey().getEvents().isEmpty()
+                                            ? StringUtils.EMPTY : parsed.getKey().getEvents().iterator().next(),
+                                    parsed.getValue());
+
+                            custom.setModelObject(StringUtils.EMPTY);
+                            send(LoggerCategoryPanel.this.getPage(), Broadcast.BREADTH, new EventSelectionChanged(
+                                    target,
+                                    Collections.<String>singleton(eventString),
+                                    Collections.<String>emptySet()));
+                            target.add(categoryContainer);
+                        }
+                    }
+                }, ActionLink.ActionType.CREATE, StandardEntitlement.NOTIFICATION_UPDATE, true).
+                add(new ActionLink<EventCategoryTO>() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final EventCategoryTO modelObject) {
+                        if (StringUtils.isNotBlank(custom.getModelObject())) {
+                            final Map.Entry<EventCategoryTO, AuditElements.Result> parsed = AuditLoggerName.
+                                    parseEventCategory(
+                                            custom.getModelObject());
+
+                            final String eventString = AuditLoggerName.buildEvent(
+                                    parsed.getKey().getType(),
+                                    null,
+                                    null,
+                                    parsed.getKey().getEvents().isEmpty()
+                                            ? StringUtils.EMPTY : parsed.getKey().getEvents().iterator().next(),
+                                    parsed.getValue());
+
+                            custom.setModelObject(StringUtils.EMPTY);
+                            send(LoggerCategoryPanel.this.getPage(), Broadcast.BREADTH, new EventSelectionChanged(
+                                    target,
+                                    Collections.<String>singleton(eventString),
+                                    Collections.<String>emptySet()));
+                            target.add(categoryContainer);
+                        }
+                    }
+                }, ActionLink.ActionType.CREATE, pageId, true).
+                add(new ActionLink<EventCategoryTO>() {
+
+                    private static final long serialVersionUID = -3722207913631435521L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final EventCategoryTO modelObject) {
+                        if (StringUtils.isNotBlank(custom.getModelObject())) {
+                            final Map.Entry<EventCategoryTO, AuditElements.Result> parsed = AuditLoggerName.
+                                    parseEventCategory(
+                                            custom.getModelObject());
+
+                            final String eventString = AuditLoggerName.buildEvent(
+                                    parsed.getKey().getType(),
+                                    null,
+                                    null,
+                                    parsed.getKey().getEvents().isEmpty()
+                                            ? StringUtils.EMPTY : parsed.getKey().getEvents().iterator().next(),
+                                    parsed.getValue());
+
+                            custom.setModelObject(StringUtils.EMPTY);
+                            send(LoggerCategoryPanel.this.getPage(), Broadcast.BREADTH, new EventSelectionChanged(
+                                    target,
+                                    Collections.<String>emptySet(),
+                                    Collections.<String>singleton(eventString)));
+                            target.add(categoryContainer);
+                        }
+                    }
+                }, ActionLink.ActionType.DELETE, pageId, true).build("customActions");
+
+        //, new Model(), pageReference);
+        categoryContainer.add(actionPanel);
+
+        actionPanel.setVisible(false);
+        actionPanel.setEnabled(false);
+
+        eventsContainer.add(new EventSelectionPanel("eventsPanel", eventCategoryTO, model) {
+
+            private static final long serialVersionUID = 3513194801190026082L;
+
+            @Override
+            protected void onEventAction(final IEvent<?> event) {
+                LoggerCategoryPanel.this.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())) {
+                eventCategoryTO.setType(type.getModelObject());
+                eventCategoryTO.setCategory(null);
+                eventCategoryTO.setSubcategory(null);
+
+                if (type.getModelObject() == EventCategoryType.CUSTOM) {
+                    category.setChoices(Collections.<String>emptyList());
+                    subcategory.setChoices(Collections.<String>emptyList());
+                    category.setEnabled(false);
+                    subcategory.setEnabled(false);
+                    custom.setVisible(true);
+                    custom.setEnabled(true);
+                    actionPanel.setVisible(true);
+                    actionPanel.setEnabled(true);
+
+                } else {
+                    category.setChoices(filter(eventCategoryTOs, type.getModelObject()));
+                    subcategory.setChoices(Collections.<String>emptyList());
+                    category.setEnabled(true);
+                    subcategory.setEnabled(true);
+                    custom.setVisible(false);
+                    custom.setEnabled(false);
+                    actionPanel.setVisible(false);
+                    actionPanel.setEnabled(false);
+                }
+                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());
+            }
+
+            updateEventsContainer(change.getTarget());
+        } else if (event.getPayload() instanceof InspectSelectedEvent) {
+            // update objects ....
+            eventCategoryTO.getEvents().clear();
+
+            final InspectSelectedEvent inspectSelectedEvent = (InspectSelectedEvent) event.getPayload();
+
+            final Map.Entry<EventCategoryTO, AuditElements.Result> categoryEvent = AuditLoggerName.parseEventCategory(
+                    inspectSelectedEvent.getEvent());
+
+            eventCategoryTO.setType(categoryEvent.getKey().getType());
+            category.setChoices(filter(eventCategoryTOs, type.getModelObject()));
+
+            eventCategoryTO.setCategory(categoryEvent.getKey().getCategory());
+            subcategory.setChoices(filter(eventCategoryTOs, type.getModelObject(), category.getModelObject()));
+
+            eventCategoryTO.setSubcategory(categoryEvent.getKey().getSubcategory());
+
+            if (categoryEvent.getKey().getType() == EventCategoryType.CUSTOM) {
+                custom.setModelObject(AuditLoggerName.buildEvent(
+                        categoryEvent.getKey().getType(),
+                        categoryEvent.getKey().getCategory(),
+                        categoryEvent.getKey().getSubcategory(),
+                        categoryEvent.getKey().getEvents().isEmpty()
+                                ? StringUtils.EMPTY : categoryEvent.getKey().getEvents().iterator().next(),
+                        categoryEvent.getValue()));
+
+                category.setEnabled(false);
+                subcategory.setEnabled(false);
+                custom.setVisible(true);
+                custom.setEnabled(true);
+                actionPanel.setVisible(true);
+                actionPanel.setEnabled(true);
+            } else {
+                category.setEnabled(true);
+                subcategory.setEnabled(true);
+                custom.setVisible(false);
+                custom.setEnabled(false);
+                actionPanel.setVisible(false);
+                actionPanel.setEnabled(false);
+            }
+
+            inspectSelectedEvent.getTarget().add(categoryContainer);
+            updateEventsContainer(inspectSelectedEvent.getTarget());
+        }
+    }
+
+    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;
+
+        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);
+        }
+    }
+
+    private void updateEventsContainer(final AjaxRequestTarget target) {
+        setEvents();
+
+        eventsContainer.addOrReplace(new EventSelectionPanel("eventsPanel", eventCategoryTO, model) {
+
+            private static final long serialVersionUID = 3513194801190026082L;
+
+            @Override
+            public void onEventAction(final IEvent<?> event) {
+                LoggerCategoryPanel.this.onEventAction(event);
+            }
+        });
+        target.add(eventsContainer);
+    }
+
+    protected abstract String[] getListRoles();
+
+    protected abstract String[] getChangeRoles();
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateContentModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateContentModal.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateContentModal.java
new file mode 100644
index 0000000..9603446
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateContentModal.java
@@ -0,0 +1,117 @@
+/*
+ * 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.notifications;
+
+import java.io.Serializable;
+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.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.rest.NotificationRestClient;
+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.types.MailTemplateFormat;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.OnLoadHeaderItem;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextArea;
+import org.apache.wicket.model.PropertyModel;
+
+public class MailTemplateContentModal extends AbstractModalPanel<Serializable> {
+
+    private static final long serialVersionUID = 2053048734388383021L;
+
+    private final MailTemplateContentTO content;
+
+    public MailTemplateContentModal(
+            final BaseModal<Serializable> modal,
+            final MailTemplateContentTO content,
+            final PageReference pageRef) {
+        super(modal, pageRef);
+        this.content = content;
+
+        TextArea<String> templateDefArea = new TextArea<>("template", new PropertyModel<String>(content, "content"));
+        templateDefArea.setMarkupId("template").setOutputMarkupPlaceholderTag(true);
+        add(templateDefArea);
+    }
+
+    @Override
+    public void renderHead(final IHeaderResponse response) {
+        super.renderHead(response);
+        response.render(OnLoadHeaderItem.forScript(
+                "CodeMirror.fromTextArea(document.getElementById('template'), {"
+                + "  lineNumbers: true, "
+                + "  autoRefresh: true"
+                + "}).on('change', updateTextArea);"));
+    }
+
+    @Override
+    public MailTemplateContentTO getItem() {
+        return this.content;
+    }
+
+    @Override
+    public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+        try {
+            new NotificationRestClient().updateTemplateFormat(
+                    content.getKey(), content.getContent(), content.getFormat());
+            info(getString(Constants.OPERATION_SUCCEEDED));
+            modal.show(false);
+            modal.close(target);
+        } catch (Exception e) {
+            LOG.error("While updating template for {}", content.getKey(), e);
+            error(StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
+        }
+        SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+    }
+
+    public static class MailTemplateContentTO extends AbstractBaseBean {
+
+        private static final long serialVersionUID = -1756961687134322845L;
+
+        private final String key;
+
+        private String content;
+
+        private final MailTemplateFormat format;
+
+        public MailTemplateContentTO(final String key, final MailTemplateFormat format) {
+            this.key = key;
+            this.format = format;
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public String getContent() {
+            return content;
+        }
+
+        public void setContent(final String content) {
+            this.content = content;
+        }
+
+        public MailTemplateFormat getFormat() {
+            return format;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel.java
new file mode 100644
index 0000000..1e6cbe2
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel.java
@@ -0,0 +1,222 @@
+/*
+ * 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.notifications;
+
+import static org.apache.wicket.Component.ENABLE;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+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.notifications.MailTemplateDirectoryPanel.MailTemplateProvider;
+import org.apache.syncope.client.console.panels.DirectoryPanel;
+import org.apache.syncope.client.console.panels.ModalPanel;
+import org.apache.syncope.client.console.rest.NotificationRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
+import org.apache.syncope.client.console.wizards.AbstractModalPanelBuilder;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.MailTemplateTO;
+import org.apache.syncope.common.lib.types.MailTemplateFormat;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.markup.html.repeater.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.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class MailTemplateDirectoryPanel
+        extends DirectoryPanel<MailTemplateTO, MailTemplateTO, MailTemplateProvider, NotificationRestClient> {
+
+    private static final long serialVersionUID = -3789392431954221446L;
+
+    public MailTemplateDirectoryPanel(final String id, final PageReference pageReference) {
+        super(id, pageReference, true);
+        disableCheckBoxes();
+
+        modal.size(Modal.Size.Small);
+        modal.addSumbitButton();
+        setFooterVisibility(true);
+
+        utilityModal.size(Modal.Size.Large);
+        utilityModal.addSumbitButton();
+
+        addNewItemPanelBuilder(new AbstractModalPanelBuilder<MailTemplateTO>(new MailTemplateTO(), pageRef) {
+
+            private static final long serialVersionUID = 1995192603527154740L;
+
+            @Override
+            public ModalPanel<MailTemplateTO> build(final String id, final int index, final AjaxWizard.Mode mode) {
+                return new MailTemplateModal(modal, new MailTemplateTO(), pageReference);
+            }
+        }, true);
+        restClient = new NotificationRestClient();
+
+        initResultTable();
+
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, ENABLE, StandardEntitlement.MAIL_TEMPLATE_CREATE);
+    }
+
+    @Override
+    protected List<IColumn<MailTemplateTO, String>> getColumns() {
+
+        final List<IColumn<MailTemplateTO, String>> columns = new ArrayList<IColumn<MailTemplateTO, String>>();
+        columns.add(new PropertyColumn<MailTemplateTO, String>(
+                new StringResourceModel("key", this, null), "key", "key"));
+
+        columns.add(new ActionColumn<MailTemplateTO, String>(new ResourceModel("actions", "")) {
+
+            private static final long serialVersionUID = -3503023501954863131L;
+
+            @Override
+            public ActionLinksPanel<MailTemplateTO> getActions(
+                    final String componentId, final IModel<MailTemplateTO> model) {
+
+                final ActionLinksPanel.Builder<MailTemplateTO> panel = ActionLinksPanel.builder();
+
+                panel.add(new ActionLink<MailTemplateTO>() {
+
+                    private static final long serialVersionUID = -7978723352517770645L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final MailTemplateTO ignore) {
+                        MailTemplateContentModal.MailTemplateContentTO content
+                                = new MailTemplateContentModal.MailTemplateContentTO(
+                                        model.getObject().getKey(), MailTemplateFormat.HTML);
+                        content.setContent(
+                                restClient.readTemplateFormat(model.getObject().getKey(), MailTemplateFormat.HTML));
+
+                        utilityModal.header(new ResourceModel("mail.template.html", "HTML Content"));
+                        utilityModal.setContent(new MailTemplateContentModal(utilityModal, content, pageRef));
+                        utilityModal.show(true);
+                        target.add(utilityModal);
+                    }
+                }, ActionLink.ActionType.HTML_EDIT, StandardEntitlement.MAIL_TEMPLATE_UPDATE);
+
+                panel.add(new ActionLink<MailTemplateTO>() {
+
+                    private static final long serialVersionUID = -7978723352517770645L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final MailTemplateTO ignore) {
+                        MailTemplateContentModal.MailTemplateContentTO content
+                                = new MailTemplateContentModal.MailTemplateContentTO(
+                                        model.getObject().getKey(), MailTemplateFormat.TEXT);
+                        content.setContent(
+                                restClient.readTemplateFormat(model.getObject().getKey(), MailTemplateFormat.TEXT));
+                        
+                        utilityModal.setFormModel(content);
+                        utilityModal.header(new ResourceModel("mail.template.text", "TEXT Content"));
+                        utilityModal.setContent(new MailTemplateContentModal(utilityModal, content, pageRef));
+                        utilityModal.show(true);
+                        target.add(utilityModal);
+                    }
+                }, ActionLink.ActionType.TEXT_EDIT, StandardEntitlement.MAIL_TEMPLATE_UPDATE);
+
+                panel.add(new ActionLink<MailTemplateTO>() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final MailTemplateTO ignore) {
+                        try {
+                            restClient.deleteTemplate(model.getObject().getKey());
+                            info(getString(Constants.OPERATION_SUCCEEDED));
+                            target.add(container);
+                        } catch (SyncopeClientException e) {
+                            LOG.error("While deleting object {}", model.getObject().getKey(), e);
+                            error(StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
+                        }
+                        SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+                    }
+                }, ActionLink.ActionType.DELETE, StandardEntitlement.MAIL_TEMPLATE_DELETE);
+
+                return panel.build(componentId);
+            }
+        });
+        return columns;
+    }
+
+    @Override
+    protected MailTemplateProvider dataProvider() {
+        return new MailTemplateProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_MAIL_TEMPLATE_PAGINATOR_ROWS;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+
+    public class MailTemplateProvider extends DirectoryDataProvider<MailTemplateTO> {
+
+        private static final long serialVersionUID = -276043813563988590L;
+
+        private final SortableDataProviderComparator<MailTemplateTO> comparator;
+
+        public MailTemplateProvider(final int paginatorRows) {
+            super(paginatorRows);
+            setSort("key", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<MailTemplateTO>(this);
+        }
+
+        @Override
+        public Iterator<MailTemplateTO> iterator(final long first, final long count) {
+            final List<MailTemplateTO> list = restClient.getAllAvailableTemplates();
+            Collections.sort(list, comparator);
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.getAllAvailableTemplates().size();
+        }
+
+        @Override
+        public IModel<MailTemplateTO> model(final MailTemplateTO mailTemplateTO) {
+            return new AbstractReadOnlyModel<MailTemplateTO>() {
+
+                private static final long serialVersionUID = 774694801558497248L;
+
+                @Override
+                public MailTemplateTO getObject() {
+                    return mailTemplateTO;
+                }
+            };
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateModal.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateModal.java
new file mode 100644
index 0000000..69ec7aa
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/MailTemplateModal.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.notifications;
+
+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.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.rest.NotificationRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.MailTemplateTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.PropertyModel;
+
+public class MailTemplateModal extends AbstractModalPanel<MailTemplateTO> {
+
+    private static final long serialVersionUID = 2053048734388383021L;
+
+    private final MailTemplateTO mailTemplateTO;
+
+    public MailTemplateModal(
+            final BaseModal<MailTemplateTO> modal,
+            final MailTemplateTO mailTemplateTO,
+            final PageReference pageRef) {
+        super(modal, pageRef);
+        this.mailTemplateTO = mailTemplateTO;
+
+        final AjaxTextFieldPanel key
+                = new AjaxTextFieldPanel("key", "key", new PropertyModel<String>(mailTemplateTO, "key"), false);
+        key.setOutputMarkupPlaceholderTag(true);
+        add(key.setRenderBodyOnly(true));
+    }
+
+    @Override
+    public MailTemplateTO getItem() {
+        return this.mailTemplateTO;
+    }
+
+    @Override
+    public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+        try {
+            new NotificationRestClient().createTemplate(mailTemplateTO);
+            info(getString(Constants.OPERATION_SUCCEEDED));
+            modal.show(false);
+            modal.close(target);
+        } catch (SyncopeClientException e) {
+            LOG.error("While creating template for {}", mailTemplateTO.getKey(), e);
+            error(StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
+        }
+        SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.java
new file mode 100644
index 0000000..3bb90eb
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.java
@@ -0,0 +1,208 @@
+/*
+ * 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.notifications;
+
+import static org.apache.wicket.Component.ENABLE;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+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.notifications.NotificationDirectoryPanel.NotificationProvider;
+import org.apache.syncope.client.console.panels.DirectoryPanel;
+import org.apache.syncope.client.console.rest.NotificationRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.CollectionPropertyColumn;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.NotificationTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.extensions.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.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class NotificationDirectoryPanel
+        extends DirectoryPanel<NotificationTO, NotificationHandler, NotificationProvider, NotificationRestClient> {
+
+    private static final long serialVersionUID = -3789392431954221446L;
+
+    public NotificationDirectoryPanel(final String id, final PageReference pageReference) {
+        super(id, pageReference, true);
+        disableCheckBoxes();
+
+        modal.size(Modal.Size.Large);
+        altDefaultModal.size(Modal.Size.Large);
+
+        addNewItemPanelBuilder(new NotificationWizardBuilder(new NotificationTO(), pageReference), true);
+
+        restClient = new NotificationRestClient();
+
+        initResultTable();
+
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, ENABLE, StandardEntitlement.NOTIFICATION_CREATE);
+    }
+
+    @Override
+    protected List<IColumn<NotificationTO, String>> getColumns() {
+
+        final List<IColumn<NotificationTO, String>> columns = new ArrayList<IColumn<NotificationTO, String>>();
+        columns.add(new PropertyColumn<NotificationTO, String>(
+                new StringResourceModel("key", this, null), "key", "key"));
+        columns.add(new CollectionPropertyColumn<NotificationTO>(
+                new StringResourceModel("events", this, null), "events", "events"));
+        columns.add(new PropertyColumn<NotificationTO, String>(
+                new StringResourceModel("subject", this, null), "subject", "subject"));
+        columns.add(new PropertyColumn<NotificationTO, String>(
+                new StringResourceModel("template", this, null), "template", "template"));
+        columns.add(new PropertyColumn<NotificationTO, String>(
+                new StringResourceModel("traceLevel", this, null), "traceLevel", "traceLevel"));
+        columns.add(new PropertyColumn<NotificationTO, String>(
+                new StringResourceModel("active", this, null), "active", "active"));
+
+        columns.add(new ActionColumn<NotificationTO, String>(new ResourceModel("actions", "")) {
+
+            private static final long serialVersionUID = -3503023501954863131L;
+
+            @Override
+            public ActionLinksPanel<NotificationTO> getActions(
+                    final String componentId, final IModel<NotificationTO> model) {
+
+                final ActionLinksPanel.Builder<NotificationTO> panel = ActionLinksPanel.builder();
+
+                panel.add(new ActionLink<NotificationTO>() {
+
+                    private static final long serialVersionUID = -7978723352517770645L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final NotificationTO ignore) {
+                        target.add(utilityModal.setContent(new NotificationTasks(
+                                utilityModal, pageRef, model.getObject())));
+                        utilityModal.header(new ResourceModel("notification.executions", "Tasks"));
+                        utilityModal.show(true);
+                        target.add(utilityModal);
+                    }
+                }, ActionLink.ActionType.VIEW, StandardEntitlement.TASK_LIST);
+
+                panel.add(new ActionLink<NotificationTO>() {
+
+                    private static final long serialVersionUID = -7978723352517770645L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final NotificationTO ignore) {
+                        send(NotificationDirectoryPanel.this, Broadcast.EXACT,
+                                new AjaxWizard.EditItemActionEvent<>(
+                                        new NotificationHandler(restClient.read(model.getObject().getKey())), target));
+                    }
+                }, ActionLink.ActionType.EDIT, StandardEntitlement.NOTIFICATION_UPDATE);
+
+                panel.add(new ActionLink<NotificationTO>() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final NotificationTO ignore) {
+                        try {
+                            restClient.delete(model.getObject().getKey());
+                            info(getString(Constants.OPERATION_SUCCEEDED));
+                            target.add(container);
+                        } catch (SyncopeClientException e) {
+                            LOG.error("While deleting object {}", model.getObject().getKey(), e);
+                            error(StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
+                        }
+                        SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+                    }
+                }, ActionLink.ActionType.DELETE, StandardEntitlement.NOTIFICATION_DELETE);
+
+                return panel.build(componentId);
+            }
+        });
+        return columns;
+    }
+
+    @Override
+    protected NotificationProvider dataProvider() {
+        return new NotificationProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_NOTIFICATION_PAGINATOR_ROWS;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+
+    public class NotificationProvider extends DirectoryDataProvider<NotificationTO> {
+
+        private static final long serialVersionUID = -276043813563988590L;
+
+        private final SortableDataProviderComparator<NotificationTO> comparator;
+
+        public NotificationProvider(final int paginatorRows) {
+            super(paginatorRows);
+            setSort("key", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<NotificationTO>(this);
+        }
+
+        @Override
+        public Iterator<NotificationTO> iterator(final long first, final long count) {
+            final List<NotificationTO> list = restClient.getAllNotifications();
+            Collections.sort(list, comparator);
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.getAllNotifications().size();
+        }
+
+        @Override
+        public IModel<NotificationTO> model(final NotificationTO notification) {
+            return new AbstractReadOnlyModel<NotificationTO>() {
+
+                private static final long serialVersionUID = 774694801558497248L;
+
+                @Override
+                public NotificationTO getObject() {
+                    return notification;
+                }
+            };
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationHandler.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationHandler.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationHandler.java
new file mode 100644
index 0000000..274eb13
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationHandler.java
@@ -0,0 +1,131 @@
+/*
+ * 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.notifications;
+
+import java.io.Serializable;
+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.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.client.console.panels.search.SearchClause;
+import org.apache.syncope.client.console.panels.search.SearchUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
+import org.apache.syncope.common.lib.to.NotificationTO;
+
+public class NotificationHandler implements Serializable {
+
+    private static final long serialVersionUID = 8058288034211558376L;
+
+    private final NotificationTO notificationTO;
+
+    private List<Pair<String, List<SearchClause>>> aboutClauses;
+
+    private List<SearchClause> recipientClauses;
+
+    public NotificationHandler(final NotificationTO notificationTO) {
+        this.notificationTO = notificationTO;
+    }
+
+    public final Long getKey() {
+        return this.notificationTO.getKey();
+    }
+
+    public List<Pair<String, List<SearchClause>>> getAboutClauses() {
+        if (this.aboutClauses == null) {
+            this.aboutClauses = new ArrayList<>();
+            for (Map.Entry<String, String> entry : this.notificationTO.getAbouts().entrySet()) {
+                this.aboutClauses.add(Pair.of(entry.getKey(), SearchUtils.getSearchClauses(entry.getValue())));
+            }
+        }
+
+        return this.aboutClauses;
+    }
+
+    public void setAboutClauses(final List<Pair<String, List<SearchClause>>> dynClauses) {
+        this.aboutClauses = dynClauses;
+    }
+
+    public List<SearchClause> getRecipientClauses() {
+        if (this.recipientClauses == null) {
+            this.recipientClauses = SearchUtils.getSearchClauses(this.notificationTO.getRecipientsFIQL());
+        }
+        return this.recipientClauses;
+    }
+
+    public void setRecipientClauses(final List<SearchClause> dynClauses) {
+        this.recipientClauses = dynClauses;
+    }
+
+    public Map<String, String> getAboutFIQLs() {
+        if (CollectionUtils.isEmpty(this.aboutClauses)) {
+            return this.notificationTO.getAbouts();
+        } else {
+
+            final Map<String, String> res = new HashMap<>();
+            for (Pair<String, List<SearchClause>> pair : this.aboutClauses) {
+                AbstractFiqlSearchConditionBuilder builder;
+                switch (pair.getLeft()) {
+                    case "USER":
+                        builder = SyncopeClient.getUserSearchConditionBuilder();
+                        break;
+                    case "GROUP":
+                        builder = SyncopeClient.getGroupSearchConditionBuilder();
+                        break;
+                    default:
+                        builder = SyncopeClient.getAnyObjectSearchConditionBuilder(pair.getLeft());
+                        break;
+
+                }
+                res.put(pair.getLeft(), getFIQLString(pair.getRight(), builder));
+            }
+            return res;
+        }
+    }
+
+    private String getRecipientsFIQL() {
+        if (CollectionUtils.isEmpty(this.recipientClauses)) {
+            return StringUtils.EMPTY;
+        } else {
+            return getFIQLString(this.recipientClauses, SyncopeClient.getUserSearchConditionBuilder());
+        }
+    }
+
+    private String getFIQLString(final List<SearchClause> clauses, final AbstractFiqlSearchConditionBuilder bld) {
+        return SearchUtils.buildFIQL(clauses, bld);
+    }
+
+    public NotificationTO fillAboutConditions() {
+        this.notificationTO.getAbouts().clear();
+        this.notificationTO.getAbouts().putAll(this.getAboutFIQLs());
+        return this.notificationTO;
+    }
+
+    public NotificationTO fillRecipientConditions() {
+        this.notificationTO.setRecipientsFIQL(this.getRecipientsFIQL());
+        return this.notificationTO;
+    }
+
+    public NotificationTO getInnerObject() {
+        return this.notificationTO;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationTasks.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationTasks.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationTasks.java
new file mode 100644
index 0000000..ead48bb
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationTasks.java
@@ -0,0 +1,71 @@
+/*
+ * 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.notifications;
+
+import java.io.Serializable;
+import org.apache.syncope.client.console.panels.ModalPanel;
+import org.apache.syncope.client.console.panels.MultilevelPanel;
+import org.apache.syncope.client.console.tasks.NotificationTaskDirectoryPanel;
+import org.apache.syncope.client.console.tasks.TaskExecutionDetails;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.common.lib.to.NotificationTO;
+import org.apache.syncope.common.lib.to.NotificationTaskTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.panel.Panel;
+
+public class NotificationTasks extends Panel implements ModalPanel<Serializable> {
+
+    private static final long serialVersionUID = 1066124171682570083L;
+
+    public NotificationTasks(
+            final BaseModal<?> baseModal, final PageReference pageReference, final NotificationTO notificationTO) {
+        super(BaseModal.CONTENT_ID);
+
+        final MultilevelPanel mlp = new MultilevelPanel("tasks");
+        add(mlp);
+
+        mlp.setFirstLevel(new NotificationTaskDirectoryPanel(null, mlp, pageReference) {
+
+            private static final long serialVersionUID = -2195387360323687302L;
+
+            @Override
+            protected void viewTask(final NotificationTaskTO taskTO, final AjaxRequestTarget target) {
+                mlp.next("task.view", new TaskExecutionDetails<>(null, taskTO, pageReference), target);
+            }
+        });
+    }
+
+    @Override
+    public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public void onError(final AjaxRequestTarget target, final Form<?> form) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public NotificationTO getItem() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+}


[2/4] syncope git commit: [SYNCOPE-745] Provides notification and email template management. Still missing tests and tasks per notification.

Posted by fm...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
index 8f48f82..16df1e7 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
@@ -22,32 +22,18 @@ import java.io.Serializable;
 import java.util.List;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.wicket.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
-import org.apache.syncope.client.console.wicket.ajax.markup.html.IndicatorAjaxSubmitLink;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.event.Broadcast;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.list.ListItem;
-import org.apache.wicket.markup.html.list.ListView;
-import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.ResourceModel;
 
-public abstract class MultiFieldPanel<E extends Serializable> extends AbstractFieldPanel<List<E>> {
+public abstract class MultiFieldPanel<E extends Serializable> extends AbstractMultiPanel<E> {
 
     private static final long serialVersionUID = -6322397761456513324L;
 
-    private final ListView<E> view;
-
     private final FieldPanel<? extends Serializable> panelTemplate;
 
-    private final boolean eventTemplate;
-
-    private final WebMarkupContainer container;
-
-    private final Form<?> form;
-
     private MultiFieldPanel(
             final String id,
             final String name,
@@ -55,102 +41,10 @@ public abstract class MultiFieldPanel<E extends Serializable> extends AbstractFi
             final FieldPanel<? extends Serializable> panelTemplate,
             final boolean eventTemplate) {
 
-        super(id, name, model);
-
+        super(id, name, model, eventTemplate);
         this.panelTemplate = panelTemplate;
-        this.eventTemplate = eventTemplate;
-
-        // -----------------------
-        // Object container definition
-        // -----------------------
-        container = new WebMarkupContainer("multiValueContainer");
-        container.setOutputMarkupId(true);
-        add(container);
-
-        form = new Form<>("innerForm");
-        container.add(form);
-        // -----------------------
-
-        view = new InnerView("view", name, model);
-
-        final List<E> obj = model.getObject();
-        if (obj == null || obj.isEmpty()) {
-            form.addOrReplace(getNoDataFragment(model, name));
-        } else {
-            form.addOrReplace(getDataFragment(model, name));
-        }
-    }
-
-    private Fragment getNoDataFragment(final IModel<List<E>> model, final String label) {
-        final Fragment fragment = new Fragment("content", "noDataFragment", MultiFieldPanel.this);
-        fragment.add(new Label("field-label", new ResourceModel(label, label)));
-        fragment.add(getPlusFragment(model, label));
-        return fragment;
-    }
-
-    private Fragment getDataFragment(final IModel<List<E>> model, final String label) {
-        final Fragment contentFragment = new Fragment("content", "dataFragment", MultiFieldPanel.this);
-        contentFragment.add(view.setOutputMarkupId(true));
-        return contentFragment;
-    }
-
-    private Fragment getPlusFragment(final IModel<List<E>> model, final String label) {
-        final IndicatorAjaxSubmitLink plus = new IndicatorAjaxSubmitLink("add") {
-
-            private static final long serialVersionUID = -7978723352517770644L;
-
-            @Override
-            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
-                //Add current component
-                model.getObject().add(newModelObject());
-
-                if (model.getObject().size() == 1) {
-                    form.addOrReplace(getDataFragment(model, label));
-                }
-
-                target.add(container);
-            }
-
-            @Override
-            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
-                error(getString(Constants.OPERATION_ERROR));
-                super.onError(target, form);
-            }
-
-        };
-
-        final Fragment fragment = new Fragment("panelPlus", "fragmentPlus", MultiFieldPanel.this);
-        fragment.addOrReplace(plus);
-        fragment.setRenderBodyOnly(true);
-
-        return fragment;
-    }
-
-    public ListView<E> getView() {
-        return view;
-    }
-
-    @Override
-    public MultiFieldPanel<E> setModelObject(final List<E> object) {
-        view.setModelObject(object);
-        return this;
-    }
-
-    public static class MultiValueSelectorEvent {
-
-        private final AjaxRequestTarget target;
-
-        public MultiValueSelectorEvent(final AjaxRequestTarget target) {
-            this.target = target;
-        }
-
-        public AjaxRequestTarget getTarget() {
-            return target;
-        }
     }
 
-    protected abstract E newModelObject();
-
     public static class Builder<E extends Serializable> implements Serializable {
 
         private static final long serialVersionUID = 1L;
@@ -194,83 +88,34 @@ public abstract class MultiFieldPanel<E extends Serializable> extends AbstractFi
                     return Builder.this.newModelObject();
                 }
 
-            };
-        }
-    }
-
-    private final class InnerView extends ListView<E> {
-
-        private static final long serialVersionUID = -9180479401817023838L;
-
-        private final String label;
-
-        private final IModel<List<E>> model;
-
-        private InnerView(final String id, final String label, final IModel<List<E>> model) {
-            super(id, model);
-            this.label = label;
-            this.model = model;
-        }
-
-        @Override
-        protected void populateItem(final ListItem<E> item) {
-            final FieldPanel<? extends Serializable> fieldPanel = panelTemplate.clone();
-            fieldPanel.setIndex(item.getIndex());
-            fieldPanel.setNewModel(item);
-            fieldPanel.settingsDependingComponents();
-
-            if (eventTemplate) {
-                fieldPanel.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
-
-                    private static final long serialVersionUID = -1107858522700306810L;
-
-                    @Override
-                    protected void onUpdate(final AjaxRequestTarget target) {
-                        send(getPage(), Broadcast.BREADTH, new MultiValueSelectorEvent(target));
-                    }
-                });
-            }
-
-            item.add(fieldPanel.hideLabel().setRenderBodyOnly(true));
-
-            final IndicatorAjaxSubmitLink minus = new IndicatorAjaxSubmitLink("drop") {
-
-                private static final long serialVersionUID = -7978723352517770644L;
-
                 @Override
-                protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
-                    //Drop current component
-                    model.getObject().remove(item.getModelObject());
-                    fieldPanel.getField().clearInput();
+                protected FieldPanel<? extends Serializable> getItemPanel(final ListItem<E> item) {
+                    final FieldPanel<? extends Serializable> fieldPanel = panelTemplate.clone();
+                    fieldPanel.setIndex(item.getIndex());
+                    fieldPanel.setNewModel(item);
+                    fieldPanel.settingsDependingComponents();
+                    fieldPanel.hideLabel();
 
-                    if (model.getObject().isEmpty()) {
-                        form.addOrReplace(getNoDataFragment(model, label));
-                    }
+                    if (eventTemplate) {
+                        fieldPanel.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
 
-                    target.add(container);
+                            private static final long serialVersionUID = -1107858522700306810L;
 
-                    if (eventTemplate) {
-                        send(getPage(), Broadcast.BREADTH, new MultiValueSelectorEvent(target));
+                            @Override
+                            protected void onUpdate(final AjaxRequestTarget target) {
+                                send(getPage(), Broadcast.BREADTH, new MultiValueSelectorEvent(target));
+                            }
+                        });
                     }
+
+                    return fieldPanel;
                 }
 
                 @Override
-                protected void onError(final AjaxRequestTarget target, final Form<?> form) {
-                    error(getString(Constants.OPERATION_ERROR));
-                    super.onError(target, form);
+                protected void clearInput(final Panel panel) {
+                    FieldPanel.class.cast(panel).getField().clearInput();
                 }
             };
-
-            item.add(minus);
-
-            final Fragment fragment;
-            if (item.getIndex() == model.getObject().size() - 1) {
-                fragment = getPlusFragment(model, label);
-            } else {
-                fragment = new Fragment("panelPlus", "emptyFragment", MultiFieldPanel.this);
-            }
-
-            item.add(fragment.setRenderBodyOnly(true));
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiPanel.java
new file mode 100644
index 0000000..e8a2e97
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiPanel.java
@@ -0,0 +1,37 @@
+/*
+ * 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 java.io.Serializable;
+import java.util.List;
+import org.apache.wicket.model.IModel;
+
+public abstract class MultiPanel<INNER extends Serializable> extends AbstractMultiPanel<INNER> {
+
+    private static final long serialVersionUID = -6322397761456513324L;
+
+    public MultiPanel(
+            final String id,
+            final String name,
+            final IModel<List<INNER>> model,
+            final boolean eventTemplate) {
+
+        super(id, name, model, eventTemplate);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractModalPanelBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractModalPanelBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractModalPanelBuilder.java
index b4d7a69..7209f26 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractModalPanelBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractModalPanelBuilder.java
@@ -27,8 +27,6 @@ public abstract class AbstractModalPanelBuilder<T extends Serializable> implemen
 
     private static final long serialVersionUID = 5241745929825564456L;
 
-    protected final String id;
-
     protected final PageReference pageRef;
 
     private final T defaultItem;
@@ -38,17 +36,23 @@ public abstract class AbstractModalPanelBuilder<T extends Serializable> implemen
     /**
      * Construct.
      *
-     * @param id The component id
      * @param defaultItem default item.
      * @param pageRef Caller page reference.
      */
-    public AbstractModalPanelBuilder(final String id, final T defaultItem, final PageReference pageRef) {
-        this.id = id;
+    public AbstractModalPanelBuilder(final T defaultItem, final PageReference pageRef) {
         this.defaultItem = defaultItem;
         this.pageRef = pageRef;
     }
 
-    public abstract ModalPanel<T> build(final int index, final AjaxWizard.Mode mode);
+    /**
+     * Build the wizard.
+     *
+     * @param id component id.
+     * @param index step index.
+     * @param mode mode.
+     * @return wizard.
+     */
+    public abstract ModalPanel<T> build(final String id, final int index, final AjaxWizard.Mode mode);
 
     protected void onCancelInternal(final T modelObject) {
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardBuilder.java
index 3e79047..3240a6b 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardBuilder.java
@@ -29,24 +29,43 @@ public abstract class AjaxWizardBuilder<T extends Serializable> extends Abstract
     /**
      * Construct.
      *
-     * @param id The component id
      * @param defaultItem default item.
      * @param pageRef Caller page reference.
      */
-    public AjaxWizardBuilder(final String id, final T defaultItem, final PageReference pageRef) {
-        super(id, defaultItem, pageRef);
+    public AjaxWizardBuilder(final T defaultItem, final PageReference pageRef) {
+        super(defaultItem, pageRef);
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
-    public AjaxWizard<T> build(final int index, final AjaxWizard.Mode mode) {
-        final AjaxWizard<T> wizard = build(mode);
+    public AjaxWizard<T> build(final String id, final int index, final AjaxWizard.Mode mode) {
+        final AjaxWizard<T> wizard = build(id, mode);
         for (int i = 1; i < index; i++) {
             wizard.getWizardModel().next();
         }
         return wizard;
     }
 
+    /**
+     * Build the wizard with a default wizard id.
+     *
+     * @param mode wizard mode.
+     * @return wizard.
+     */
     public AjaxWizard<T> build(final AjaxWizard.Mode mode) {
+        return build(WizardMgtPanel.WIZARD_ID, mode);
+    }
+
+    /**
+     * Build the wizard.
+     *
+     * @param id component id.
+     * @param mode wizard mode.
+     * @return wizard.
+     */
+    public AjaxWizard<T> build(final String id, final AjaxWizard.Mode mode) {
         // ge the specified item if available
         final T modelObject = newModelObject();
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
index e8bc981..49a1180 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
@@ -46,6 +46,10 @@ public abstract class WizardMgtPanel<T extends Serializable> extends Panel imple
 
     private static final long serialVersionUID = -4152438633429194882L;
 
+    protected static final String WIZARD_ID = "wizard";
+
+    private final String actualId;
+
     private final WebMarkupContainer container;
 
     private final Fragment initialFragment;
@@ -90,6 +94,7 @@ public abstract class WizardMgtPanel<T extends Serializable> extends Panel imple
         super(id);
         setOutputMarkupId(true);
 
+        this.actualId = wizardInModal ? BaseModal.CONTENT_ID : WIZARD_ID;
         this.wizardInModal = wizardInModal;
 
         super.add(modal);
@@ -131,6 +136,7 @@ public abstract class WizardMgtPanel<T extends Serializable> extends Panel imple
                 newItemPanelBuilder.setItem(item);
 
                 final ModalPanel<T> modalPanel = newItemPanelBuilder.build(
+                        actualId,
                         ((AjaxWizard.NewItemActionEvent<T>) newItemEvent).getIndex(),
                         item != null ? AjaxWizard.Mode.EDIT : AjaxWizard.Mode.CREATE);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
index ebe2d14..8404b2f 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
@@ -35,17 +35,15 @@ public class AnyObjectWizardBuilder extends AnyWizardBuilder<AnyObjectTO> implem
     /**
      * Construct.
      *
-     * @param id The component id
      * @param anyObjectTO any object TO.
      * @param anyTypeClasses any type classes
      * @param pageRef Caller page reference.
      */
     public AnyObjectWizardBuilder(
-            final String id,
             final AnyObjectTO anyObjectTO,
             final List<String> anyTypeClasses,
             final PageReference pageRef) {
-        super(id, anyObjectTO, anyTypeClasses, pageRef);
+        super(anyObjectTO, anyTypeClasses, pageRef);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
index 47f255d..6fae131 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
@@ -46,31 +46,27 @@ public abstract class AnyWizardBuilder<T extends AnyTO> extends AjaxWizardBuilde
     /**
      * Construct.
      *
-     * @param id The component id
      * @param anyTO any
      * @param anyTypeClasses any type classes
      * @param pageRef Caller page reference.
      */
-    public AnyWizardBuilder(
-            final String id, final T anyTO, final List<String> anyTypeClasses, final PageReference pageRef) {
-        super(id, new AnyHandler<>(anyTO), pageRef);
+    public AnyWizardBuilder(final T anyTO, final List<String> anyTypeClasses, final PageReference pageRef) {
+        super(new AnyHandler<>(anyTO), pageRef);
         this.anyTypeClasses = anyTypeClasses;
     }
 
     /**
      * Construct.
      *
-     * @param id The component id
      * @param handler any handler
      * @param anyTypeClasses any type classes
      * @param pageRef Caller page reference.
      */
     public AnyWizardBuilder(
-            final String id,
             final AnyHandler<T> handler,
             final List<String> anyTypeClasses,
             final PageReference pageRef) {
-        super(id, handler, pageRef);
+        super(handler, pageRef);
         this.anyTypeClasses = anyTypeClasses;
     }
 
@@ -106,7 +102,7 @@ public abstract class AnyWizardBuilder<T extends AnyTO> extends AjaxWizardBuilde
         wizardModel.add(new Resources(modelObject.getInnerObject()));
         return wizardModel;
     }
-    
+
     protected AnyWizardBuilder<T> addOptionalDetailsPanel(
             final AnyHandler<T> modelObject, final WizardModel wizardModel) {
         if (modelObject.getInnerObject().getKey() != null && modelObject.getInnerObject().getKey() > 0) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupHandler.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupHandler.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupHandler.java
index 59fc58d..5966efc 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupHandler.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupHandler.java
@@ -38,9 +38,11 @@ public class GroupHandler extends AnyHandler<GroupTO> {
 
     public GroupHandler(final GroupTO groupTO) {
         super(groupTO);
+        getUDynClauses();
+        getADynClauses();
     }
 
-    public List<SearchClause> getUDynClauses() {
+    public final List<SearchClause> getUDynClauses() {
         if (this.uDynClauses == null) {
             this.uDynClauses = SearchUtils.getSearchClauses(this.anyTO.getUDynMembershipCond());
         }
@@ -51,7 +53,7 @@ public class GroupHandler extends AnyHandler<GroupTO> {
         this.uDynClauses = uDynClauses;
     }
 
-    public Map<String, List<SearchClause>> getADynClauses() {
+    public final Map<String, List<SearchClause>> getADynClauses() {
         if (this.aDynClauses == null) {
             this.aDynClauses = SearchUtils.getSearchClauses(this.anyTO.getADynMembershipConds());
         }
@@ -64,27 +66,24 @@ public class GroupHandler extends AnyHandler<GroupTO> {
 
     public String getUDynMembershipCond() {
         if (CollectionUtils.isEmpty(this.uDynClauses)) {
-            return this.anyTO.getUDynMembershipCond();
+            return null;
         } else {
             return getFIQLString(this.uDynClauses, SyncopeClient.getUserSearchConditionBuilder());
         }
     }
 
     public Map<String, String> getADynMembershipConds() {
-        if (this.aDynClauses == null || this.aDynClauses.isEmpty()) {
-            return this.anyTO.getADynMembershipConds();
-        } else {
-            final Map<String, String> res = new HashMap<>();
-
+        final Map<String, String> res = new HashMap<>();
+        if (this.aDynClauses != null && !this.aDynClauses.isEmpty()) {
             for (Map.Entry<String, List<SearchClause>> entry : this.aDynClauses.entrySet()) {
                 if (CollectionUtils.isNotEmpty(entry.getValue())) {
                     res.put(entry.getKey(), getFIQLString(entry.getValue(),
                             SyncopeClient.getAnyObjectSearchConditionBuilder(entry.getKey())));
                 }
             }
-
-            return res;
         }
+
+        return res;
     }
 
     private String getFIQLString(final List<SearchClause> clauses, final AbstractFiqlSearchConditionBuilder bld) {
@@ -93,6 +92,7 @@ public class GroupHandler extends AnyHandler<GroupTO> {
 
     public GroupTO fillDynamicConditions() {
         this.anyTO.setUDynMembershipCond(this.getUDynMembershipCond());
+        this.anyTO.getADynMembershipConds().clear();
         this.anyTO.getADynMembershipConds().putAll(this.getADynMembershipConds());
         return this.anyTO;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
index 7314fa2..c77d190 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
@@ -41,14 +41,12 @@ public class GroupWizardBuilder extends AnyWizardBuilder<GroupTO> {
     /**
      * Construct.
      *
-     * @param id The component id
      * @param groupTO any
      * @param anyTypeClasses any type classes
      * @param pageRef Caller page reference.
      */
-    public GroupWizardBuilder(
-            final String id, final GroupTO groupTO, final List<String> anyTypeClasses, final PageReference pageRef) {
-        super(id, new GroupHandler(groupTO), anyTypeClasses, pageRef);
+    public GroupWizardBuilder(final GroupTO groupTO, final List<String> anyTypeClasses, final PageReference pageRef) {
+        super(new GroupHandler(groupTO), anyTypeClasses, pageRef);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
index 6c9b1a3..c791942 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
@@ -45,17 +45,15 @@ public class UserWizardBuilder extends AnyWizardBuilder<UserTO> {
     /**
      * Construct.
      *
-     * @param id The component id
      * @param userTO any
      * @param anyTypeClasses any type classes
      * @param pageRef Caller page reference.
      */
     public UserWizardBuilder(
-            final String id,
             final UserTO userTO,
             final List<String> anyTypeClasses,
             final PageReference pageRef) {
-        super(id, userTO, anyTypeClasses, pageRef);
+        super(userTO, anyTypeClasses, pageRef);
         statusModel = new ListModel<>(new ArrayList<StatusBean>());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
index 73d6bfb..c2f7653 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
@@ -229,12 +229,11 @@ public class ProvisionWizardBuilder extends AjaxWizardBuilder<ProvisionTO> imple
     /**
      * Construct.
      *
-     * @param id The component id
      * @param resurceTO external resource to be updated.
      * @param pageRef Caller page reference.
      */
-    public ProvisionWizardBuilder(final String id, final ResourceTO resurceTO, final PageReference pageRef) {
-        super(id, new ProvisionTO(), pageRef);
+    public ProvisionWizardBuilder(final ResourceTO resurceTO, final PageReference pageRef) {
+        super(new ProvisionTO(), pageRef);
         this.resourceTO = resurceTO;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleHandler.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleHandler.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleHandler.java
index 19ce196..f7034b6 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleHandler.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleHandler.java
@@ -37,9 +37,10 @@ public class RoleHandler implements Serializable {
 
     public RoleHandler(final RoleTO roleTO) {
         this.roleTO = roleTO;
+        getDynClauses();
     }
 
-    public List<SearchClause> getDynClauses() {
+    public final List<SearchClause> getDynClauses() {
         if (this.dynClauses == null) {
             this.dynClauses = SearchUtils.getSearchClauses(this.roleTO.getDynMembershipCond());
         }
@@ -52,7 +53,7 @@ public class RoleHandler implements Serializable {
 
     public String getDynMembershipCond() {
         if (CollectionUtils.isEmpty(this.dynClauses)) {
-            return this.roleTO.getDynMembershipCond();
+            return null;
         } else {
             return getFIQLString(this.dynClauses, SyncopeClient.getUserSearchConditionBuilder());
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
index dc6cfc1..9c2bea1 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
@@ -55,12 +55,11 @@ public class RoleWizardBuilder extends AjaxWizardBuilder<RoleHandler> {
     /**
      * Construct.
      *
-     * @param id The component id
      * @param roleTO role
      * @param pageRef Caller page reference.
      */
-    public RoleWizardBuilder(final String id, final RoleTO roleTO, final PageReference pageRef) {
-        super(id, new RoleHandler(roleTO), pageRef);
+    public RoleWizardBuilder(final RoleTO roleTO, final PageReference pageRef) {
+        super(new RoleHandler(roleTO), pageRef);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css b/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
index cc1a8ea..05183af 100644
--- a/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
+++ b/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
@@ -297,7 +297,7 @@ span.overridable div.checkbox label div div.toggle-group label {
   padding-left: 7px;
 }
 
-div#outer.modal-lg, div#utilityModal.modal-lg {
+div#outer.modal-lg, div#utilityModal.modal-lg, section#notifications .modal-lg {
   width: 1200px;
 }
 
@@ -503,6 +503,22 @@ div.listview-actions a {
 .btn-circle i, .circular-actions a i {
   margin: 0px;
 }
+
+.multipanel-btn-minus {
+  padding: 0px 0px 0px 6px;
+  border: 0 none !important;
+}
+
+.multipanel-btn-plus {
+  padding: 15px 0px 8px 6px;
+  border: 0 none !important;
+}
+
+.multipanel-box {
+  padding: 5px;
+  display: inline-table;
+  margin: 0px;
+}
 /**
 END - Actions
 */
@@ -669,4 +685,24 @@ li.todoitem a {
 .form-palette {
   display: inline-block;
   width: 119%;
+}
+
+.form-group-inline {
+  display: inline-table;
+  width: 100%;
+}
+
+div#userFilter #title{
+  margin: 10px 0 0 8px;
+  font-weight: bold;
+}
+
+div#userFilter  #warning{
+  margin: 1px 0 5px 8px;
+  color: #e00000;
+  font-size: 9px;
+}
+
+div#userFilter  #check {
+  margin: 1px 0 5px 8px;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/EventSelectionPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/EventSelectionPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/EventSelectionPanel.html
new file mode 100644
index 0000000..ae36ca1
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/EventSelectionPanel.html
@@ -0,0 +1,125 @@
+<!--
+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>
+    <style>
+      .container {
+        display: block;
+        width: 100%;
+      }
+
+      .labrut {
+        font-size: 12px;
+        vertical-align: middle;
+        font-family: Verdana,Tahoma,sans-serif;
+        display: table-cell;
+        width: 300px;
+        height: 20px;
+      }
+
+      .divtableheaderlabrut {
+        font-weight: bold;
+        float: left;
+        margin-left: 5px;
+        display: table-cell;
+        width: 70px;
+      }
+
+      .divtableheadercell {
+        display: inline-table;
+        vertical-align: top;
+        text-align: right;
+        width: 105px;
+        height: 20px;
+      }
+
+      .divtablecheckcolumn {
+        display: table-cell;
+        vertical-align: top;
+        text-align: right;
+        width: 105px;
+      }
+
+      .divtablecheck {
+        width: 105px;
+        height: 20px;
+      }
+
+    </style>
+  </wicket:head>
+  <wicket:panel>
+    <div class="eventSelectionWidzard">
+      <div class="container">
+        <div style="display:inline-table;">
+          <div class="labrut">
+            &nbsp;
+          </div>
+          <div style="display: table-cell">
+            <div class="divtableheadercell">
+              <div class="divtableheaderlabrut">
+                <labrut wicket:id="successLabel"/>
+              </div>
+              <div style="display: table-cell">
+                <input type="checkbox" wicket:id="successSelector" style="margin-top: 0px;margin-bottom: 0px;"/>
+              </div>
+            </div>
+          </div>
+          <div style="display: table-cell">
+            <div class="divtableheadercell">
+              <div class="divtableheaderlabrut">
+                <labrut wicket:id="failureLabel"/>
+              </div>
+              <div style="display: table-cell">
+                <input type="checkbox" wicket:id="failureSelector" style="margin-top: 0px;margin-bottom: 0px;"/>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="container">
+        <div class="labrut">
+          <div id="divtablerow-labrut" wicket:id="categoryView">
+            <div class="labrut">
+              <span wicket:id="subcategory">[subcategory]</span>
+            </div>
+          </div>
+        </div>
+
+        <div id="divtablerow-success" class="divtablecheckcolumn">
+          <span wicket:id="successGroup">
+            <div wicket:id="successView" class="divtablecheck">
+              <div class="divtableheaderlabrut">&nbsp;</div>
+              <input type="checkbox" wicket:id="successCheck"/>
+            </div>
+          </span>
+        </div>
+
+        <div id="divtablerow-failure" class="divtablecheckcolumn">
+          <span wicket:id="failureGroup">
+            <div wicket:id="failureView" class="divtablecheck">
+              <div class="divtableheaderlabrut">&nbsp;</div>
+              <input type="checkbox" wicket:id="failureCheck"/>
+            </div>
+          </span>
+        </div>
+      </div>
+    </div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/LoggerCategoryPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/LoggerCategoryPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/LoggerCategoryPanel.html
new file mode 100644
index 0000000..3fb3baf
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/LoggerCategoryPanel.html
@@ -0,0 +1,91 @@
+<!--
+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>
+    <style>
+
+      .events{
+        display: table-row;
+        width: 990px;
+      }
+
+      .selectedEvents{
+        display: inline-block;
+        height: 100px;
+        margin: 10px 10px 0 10px;
+        overflow-y: auto;
+      }
+
+      .eventSelection{
+        display: inline-table;
+        float: right;
+        width: 380px;
+        min-width: 380px;
+      }
+
+      .eventSelection div#eventElement{
+        display: table-row;
+        width:100%;
+      }
+      .eventSelection div#eventElement div#labrut{
+        display: table-cell;
+      }
+
+      .eventSelection div#eventElement div#value{
+        display: table-cell;
+        padding: 0 0 0 10px;
+      }
+    </style>
+  </wicket:head>
+  <wicket:panel>
+
+    <div class="events">
+      <div class="selectedEvents">
+        <span wicket:id="selectedEventsPanel">[selected events panel]</span>
+      </div>
+
+      <div class="eventSelection">
+        <div wicket:id="categoryContainer">
+          <div>
+            <span wicket:id="type">[type]</span>
+          </div>
+          <div>
+            <span wicket:id="category">[category]</span>
+          </div>
+          <div>
+            <span wicket:id="subcategory">[sub-category]</span>
+          </div>
+          <div>
+            <div id="labrut">
+              <labrut wicket:id="customLabel"/>
+            </div>
+            <div id="value">
+              <span wicket:id="custom">[custom]</span>
+              <span wicket:id="customActions">[actions]</span>
+            </div>
+          </div>
+        </div>
+        <div wicket:id="eventsContainer">
+          <span wicket:id="eventsPanel">[events panel]</span>
+        </div>
+      </div>
+    </div>
+
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateContentModal.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateContentModal.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateContentModal.html
new file mode 100644
index 0000000..feed01e
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateContentModal.html
@@ -0,0 +1,54 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <head><title></title></head>
+  <body>
+    <wicket:head>
+      <link rel="stylesheet" type="text/css" href="webjars/codemirror/${codemirror.version}/lib/codemirror.css"/>
+
+      <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/lib/codemirror.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">
+        function updateTextArea(editor) {
+          document.getElementById("templateDefForm").children["template"].value = editor.getValue();
+        }
+      </script>
+      <style>
+        .w_content_3 {
+          padding: 0;
+          color: #333333;
+          font-family: Verdana,Tahoma,sans-serif;
+          font-size: 100%;
+          border: 1px solid #BBBBBB;
+          padding: 1%;
+        }
+      </style>
+    </wicket:head>
+    <wicket:panel>
+      <div style="padding: 1%;">
+        <div class="w_content_3" id="templateDefForm">
+          <textarea wicket:id="template" id="template" name="template" style="width: 100%; height: 350px;">
+          </textarea>
+        </div>
+      </div>
+    </wicket:panel>
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel.properties
new file mode 100644
index 0000000..70c7a20
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel.properties
@@ -0,0 +1,20 @@
+# 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.
+any.edit=Edit ${key}
+any.new=New e-mail template
+any.finish=Submit
+any.cancel=Cancel

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel_it.properties
new file mode 100644
index 0000000..d0d15f8
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel_it.properties
@@ -0,0 +1,20 @@
+# 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.
+any.edit=Modifica ${key}
+any.new=Nuovo e-mail template
+any.finish=Invia
+any.cancel=Annulla

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel_pt_BR.properties
new file mode 100644
index 0000000..de37c65
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateDirectoryPanel_pt_BR.properties
@@ -0,0 +1,20 @@
+# 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.
+any.edit=Editar ${key}
+any.new=Novo e-mail template
+any.finish=Enviar
+any.cancel=Cancelar

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateModal.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateModal.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateModal.html
new file mode 100644
index 0000000..3a0676c
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateModal.html
@@ -0,0 +1,28 @@
+<!--
+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">
+  <head><title></title></head>
+  <body>
+    <wicket:panel>
+      <div class="form-group">
+        <span wicket:id="key"/>
+      </div>
+    </wicket:panel>
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.properties
new file mode 100644
index 0000000..70c7a20
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.properties
@@ -0,0 +1,20 @@
+# 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.
+any.edit=Edit ${key}
+any.new=New e-mail template
+any.finish=Submit
+any.cancel=Cancel

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel_it.properties
new file mode 100644
index 0000000..d0d15f8
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel_it.properties
@@ -0,0 +1,20 @@
+# 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.
+any.edit=Modifica ${key}
+any.new=Nuovo e-mail template
+any.finish=Invia
+any.cancel=Annulla

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel_pt_BR.properties
new file mode 100644
index 0000000..de37c65
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel_pt_BR.properties
@@ -0,0 +1,20 @@
+# 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.
+any.edit=Editar ${key}
+any.new=Novo e-mail template
+any.finish=Enviar
+any.cancel=Cancelar

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationTasks.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationTasks.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationTasks.html
new file mode 100644
index 0000000..ac755e8
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationTasks.html
@@ -0,0 +1,24 @@
+<!--
+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">
+  <head><title></title></head>
+  <wicket:panel>
+    <span wicket:id="tasks">[TASKS]</span>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$About.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$About.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$About.html
new file mode 100644
index 0000000..006425d
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$About.html
@@ -0,0 +1,31 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <head><title></title></head>
+  <body>
+    <wicket:panel>
+      <div class="form-group">
+        <span wicket:id="about"></span>
+      </div>
+      <div wicket:id="search">
+        <span wicket:id="clauses"></span>
+      </div>
+    </wicket:panel>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.html
new file mode 100644
index 0000000..c1cbc27
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.html
@@ -0,0 +1,28 @@
+<!--
+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">
+  <head><title></title></head>
+  <body>
+    <wicket:panel>
+      <div wicket:id="about" class="form-group">
+        <span wicket:id="abouts">[abouts]</span>
+      </div>
+    </wicket:panel>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.properties
new file mode 100644
index 0000000..8a8946a
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.properties
@@ -0,0 +1,24 @@
+# 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.
+checkAbout=All
+userFilter=User filter
+userFilterWarning=Do not use this filter unless events are not targeted at users
+roleFilter=Role filter
+roleFilterWarning=Do not use this filter unless events are not targeted at roles
+checkUserAbout=Users
+checkRoleAbout=Roles
+abouts=What is the notification referring to?

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts_it.properties
new file mode 100644
index 0000000..43c055b
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts_it.properties
@@ -0,0 +1,24 @@
+# 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.
+checkAbout=Tutto
+userFilter=Filtro utenti
+userFilterWarning=Non usare questo filtro se gli eventi catturati non riguardano espressamente utenti
+roleFilter=Filtro ruoli
+roleFilterWarning=Non usare questo filtro se gli eventi catturati non riguardano espressamente ruoli
+checkUserAbout=Utenti
+checkRoleAbout=Ruoli
+abouts=A cosa si riferisce la notifica?

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts_pt_BR.properties
new file mode 100644
index 0000000..f31449f
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts_pt_BR.properties
@@ -0,0 +1,24 @@
+# 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.
+checkAbout=Todos
+userFilter=Filtro usu\u00e1rio
+userFilterWarning=N\u00e3o use este filtro n\u00e3o ser que os eventos n\u00e3o s\u00e3o direcionados a usu\u00e1rios
+roleFilter=Filtro fun\u00e7\u00e3o
+roleFilterWarning=N\u00e3o use este filtro n\u00e3o ser que os eventos n\u00e3o s\u00e3o direcionados a fun\u00e7\u00f5es
+checkUserAbout=Usu\u00e1rio
+checkRoleAbout=Fun\u00e7\u00e3o
+abouts=O que diz respeito \u00e0 notifica\u00e7\u00e3o?

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.html
new file mode 100644
index 0000000..1ceee21
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.html
@@ -0,0 +1,46 @@
+<!--
+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">
+  <head><title></title></head>
+  <body>
+    <wicket:panel>
+      <div class="form-group">
+        <span wicket:id="sender">[sender]</span>
+      </div>
+      <div class="form-group">
+        <span wicket:id="subject">[subject]</span>
+      </div>
+      <div class="form-group">
+        <span wicket:id="recipientAttrType">[recipientAttrType]</span>
+      </div>
+      <div class="form-group">
+        <span wicket:id="recipientAttrName">[recipientAttrName]</span>
+      </div>
+      <div class="form-group">
+        <span wicket:id="template">[template]</span>
+      </div>
+      <div class="form-group">
+        <span wicket:id="traceLevel">[traceLevel]</span>
+      </div>
+      <div class="form-group">
+        <span wicket:id="isActive">[isActive]</span>
+      </div>
+    </wicket:panel>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.properties
new file mode 100644
index 0000000..4021981
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.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.
+subject=Subject
+sender=Sender
+template=Template
+traceLevel=Trace level
+recipientAttrType=Recipients e-mail attribute type
+recipientAttrName=Recipients e-mail attribute schema
+isActive=Enabled

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details_it.properties
new file mode 100644
index 0000000..6b7a1ca
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details_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.
+subject=Oggetto
+sender=Mittente
+template=Modello
+traceLevel=Livello di tracciamento
+recipientAttrType=Tipo attributo e-mail destinatari
+recipientAttrName=Schema attributo e-mail destinatari
+isActive=Abilitata

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details_pt_BR.properties
new file mode 100644
index 0000000..d2ffaf6
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details_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.
+subject=Assunto
+sender=Remetente
+template=Template
+traceLevel=N\u00edvel de Trace
+recipientAttrType=Tipo de atributo de destinat\u00e1rio de email.
+recipientAttrName=Destinat\u00e1rios de e-mail para atributos de esquema
+isActive=Ativada

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.html
new file mode 100644
index 0000000..c697099
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.html
@@ -0,0 +1,27 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <head><title></title></head>
+  <body>
+    <wicket:panel>
+      <span wicket:id="eventSelection"/>
+    </wicket:panel>
+  </body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.properties
new file mode 100644
index 0000000..690ae7e
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.properties
@@ -0,0 +1,42 @@
+# 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.
+title=Edit notification
+submit=Save
+subject=Subject
+sender=Sender
+template=Template
+traceLevel=Trace level
+notification=Notification
+about=About
+events=Events
+recipients=Recipients
+selfAsRecipient=Include users matching About
+recipientAttrType=Recipients e-mail attribute type
+recipientAttrName=Recipients e-mail attribute schema
+checkRecipients=Search recipients
+checkAbout=All
+userFilter=User filter
+userFilterWarning=Do not use this filter unless events are not targeted at users
+userNotifications=User notifications
+userNotificationsWarning=Do not select this checkbox unless events are not targeted at users
+roleFilter=Role filter
+roleFilterWarning=Do not use this filter unless events are not targeted at roles
+roleNotifications=Role notifications
+roleNotificationsWarning=Do not select this checkbox unless events are not targeted at roles
+isActive=Enabled
+checkUserAbout=Users
+checkRoleAbout=Roles

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events_it.properties
new file mode 100644
index 0000000..db3bb59
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events_it.properties
@@ -0,0 +1,42 @@
+# 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.
+title=Gestisci notifiche
+submit=Salva
+subject=Oggetto
+sender=Mittente
+template=Modello
+traceLevel=Livello di tracciamento
+notification=Notifica
+about=Soggetti
+events=Eventi
+recipients=Destinatari
+selfAsRecipient=Includi i soggetti
+recipientAttrType=Tipo attributo e-mail destinatari
+recipientAttrName=Schema attributo e-mail destinatari
+checkRecipients=Cerca i destinatari
+checkAbout=Tutto
+userFilter=Filtro utenti
+userFilterWarning=Non usare questo filtro se gli eventi catturati non riguardano espressamente utenti
+userNotifications=Notifiche utente
+userNotificationsWarning=Non selezionare questa checkbox se gli eventi catturati non riguardano espressamente utenti
+roleFilter=Filtro ruoli
+roleFilterWarning=Non usare questo filtro se gli eventi catturati non riguardano espressamente ruoli
+roleNotifications=Notifiche ruoli
+roleNotificationsWarning=Non selezionare questa checkbox se gli eventi catturati non riguardano espressamente ruoli
+isActive=Abilitata
+checkUserAbout=Utenti
+checkRoleAbout=Ruoli

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events_pt_BR.properties
new file mode 100644
index 0000000..9b13d12
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events_pt_BR.properties
@@ -0,0 +1,42 @@
+# 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.
+title=Alterar Notifica\u00e7\u00e3o
+submit=Salvar
+subject=Assunto
+sender=Remetente
+template=Template
+traceLevel=N\u00edvel de Trace
+notification=Notifica\u00e7\u00e3o
+about=Sobre
+events=Eventos
+recipients=Destinat\u00e1rios
+selfAsRecipient=Incluir usu\u00e1rios relacionados Sobre
+recipientAttrType=Tipo de atributo de destinat\u00e1rio de email.
+recipientAttrName=Destinat\u00e1rios de e-mail para atributos de esquema
+checkRecipients=Buscar Destinat\u00e1rios
+checkAbout=Todos
+userFilter=Filtro usu\u00e1rio
+userFilterWarning=N\u00e3o use este filtro n\u00e3o ser que os eventos n\u00e3o s\u00e3o direcionados a usu\u00e1rios
+roleFilter=Filtro fun\u00e7\u00e3o
+roleFilterWarning=N\u00e3o use este filtro n\u00e3o ser que os eventos n\u00e3o s\u00e3o direcionados a fun\u00e7\u00f5es
+userNotifications=Notifica\u00e7\u00f5es do usu\u00e1rio
+userNotificationsWarning=N\u00e3o selecione esta op\u00e7\u00e3o a menos que os eventos n\u00e3o s\u00e3o direcionados a usu\u00e1rios
+roleNotifications=Notifica\u00e7\u00f5es do fun\u00e7\u00f5es
+roleNotificationsWarning=N\u00e3o selecione esta op\u00e7\u00e3o a menos que os eventos n\u00e3o s\u00e3o direcionados a fun\u00e7\u00f5es
+isActive=Ativada
+checkUserAbout=Usu\u00e1rio
+checkRoleAbout=Fun\u00e7\u00e3o

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.html
new file mode 100644
index 0000000..b8387d6
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.html
@@ -0,0 +1,43 @@
+<!--
+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">
+  <head><title></title></head>
+  <body>
+    <wicket:panel>
+      <div class="form-group">
+        <span wicket:id="staticRecipients">[staticRecipients]</span>
+      </div>
+      <div class="form-group form-group-inline">
+        <span wicket:id="recipients">[recipients]</span>
+      </div>
+      <div id="userFilter" class="form-group box">
+        <div id="title">
+          <span for="userNotifications"><wicket:message key="userNotifications"/></span>
+        </div>
+        <div id="warning">
+          <span for="userNotificationsWarning"><wicket:message key="userNotificationsWarning"/></span>
+        </div>
+        <div id="check">
+          <span wicket:id="selfAsRecipient">[selfAsRecipient]</span>
+        </div>
+      </div>
+    </wicket:panel>
+  </body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.properties b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.properties
new file mode 100644
index 0000000..dec4920
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.properties
@@ -0,0 +1,22 @@
+# 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.
+recipients=Recipients
+staticRecipients=Recipients
+selfAsRecipient=Include users matching About
+userNotifications=User notifications
+userNotificationsWarning=Do not select this checkbox unless events are not targeted at users
+search=Search for recipients


[3/4] syncope git commit: [SYNCOPE-745] Provides notification and email template management. Still missing tests and tasks per notification.

Posted by fm...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
new file mode 100644
index 0000000..037773d
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
@@ -0,0 +1,435 @@
+/*
+ * 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.notifications;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.panels.search.AbstractSearchPanel;
+import org.apache.syncope.client.console.panels.search.AnyObjectSearchPanel;
+import org.apache.syncope.client.console.panels.search.GroupSearchPanel;
+import org.apache.syncope.client.console.panels.search.SearchClause;
+import org.apache.syncope.client.console.panels.search.UserSearchPanel;
+import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.rest.LoggerRestClient;
+import org.apache.syncope.client.console.rest.NotificationRestClient;
+import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.MultiPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizardBuilder;
+import org.apache.syncope.common.lib.EntityTOUtils;
+import org.apache.syncope.common.lib.to.AnyTypeTO;
+import org.apache.syncope.common.lib.to.DerSchemaTO;
+import org.apache.syncope.common.lib.to.MailTemplateTO;
+import org.apache.syncope.common.lib.to.NotificationTO;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.to.VirSchemaTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.extensions.wizard.WizardModel;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.list.ListItem;
+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.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.util.ListModel;
+import org.apache.wicket.validation.validator.EmailAddressValidator;
+
+public class NotificationWizardBuilder extends AjaxWizardBuilder<NotificationHandler> {
+
+    private static final long serialVersionUID = -1975312550059578553L;
+
+    private final NotificationRestClient restClient = new NotificationRestClient();
+
+    private final SchemaRestClient schemaRestClient = new SchemaRestClient();
+
+    private final LoggerRestClient loggerRestClient = new LoggerRestClient();
+
+    /**
+     * Construct.
+     *
+     * @param notificationTO notification.
+     * @param pageRef Caller page reference.
+     */
+    public NotificationWizardBuilder(final NotificationTO notificationTO, final PageReference pageRef) {
+        super(new NotificationHandler(notificationTO), pageRef);
+    }
+
+    @Override
+    protected Serializable onApplyInternal(final NotificationHandler modelObject) {
+        modelObject.fillRecipientConditions();
+        modelObject.fillAboutConditions();
+
+        final boolean createFlag
+                = modelObject.getInnerObject().getKey() == null || modelObject.getInnerObject().getKey() <= 0;
+
+        if (createFlag) {
+            restClient.create(modelObject.getInnerObject());
+        } else {
+            restClient.update(modelObject.getInnerObject());
+        }
+
+        return null;
+    }
+
+    @Override
+    protected WizardModel buildModelSteps(final NotificationHandler modelObject, final WizardModel wizardModel) {
+        wizardModel.add(new NotificationWizardBuilder.Details(modelObject));
+        wizardModel.add(new NotificationWizardBuilder.Events(modelObject));
+        wizardModel.add(new NotificationWizardBuilder.Abouts(modelObject));
+        wizardModel.add(new NotificationWizardBuilder.Recipients(modelObject));
+        return wizardModel;
+    }
+
+    public class Details extends WizardStep {
+
+        private static final long serialVersionUID = -7709805590497687958L;
+
+        public Details(final NotificationHandler modelObject) {
+            final NotificationTO notificationTO = modelObject.getInnerObject();
+            final boolean createFlag = notificationTO.getKey() == null || notificationTO.getKey() <= 0;
+
+            final AjaxTextFieldPanel sender = new AjaxTextFieldPanel("sender", getString("sender"),
+                    new PropertyModel<String>(notificationTO, "sender"));
+            sender.addRequiredLabel();
+            sender.addValidator(EmailAddressValidator.getInstance());
+            add(sender);
+
+            final AjaxTextFieldPanel subject = new AjaxTextFieldPanel("subject", getString("subject"),
+                    new PropertyModel<String>(notificationTO, "subject"));
+            subject.addRequiredLabel();
+            add(subject);
+
+            final AjaxDropDownChoicePanel<IntMappingType> recipientAttrType
+                    = new AjaxDropDownChoicePanel<IntMappingType>(
+                            "recipientAttrType",
+                            new ResourceModel("recipientAttrType", "recipientAttrType").getObject(),
+                            new PropertyModel<IntMappingType>(notificationTO, "recipientAttrType"));
+            recipientAttrType.setChoices(
+                    new ArrayList<IntMappingType>(IntMappingType.getAttributeTypes(AnyTypeKind.USER,
+                            EnumSet.of(IntMappingType.UserKey, IntMappingType.Password))));
+            recipientAttrType.addRequiredLabel();
+            add(recipientAttrType);
+
+            final AjaxDropDownChoicePanel<String> recipientAttrName = new AjaxDropDownChoicePanel<String>(
+                    "recipientAttrName", new ResourceModel("recipientAttrName", "recipientAttrName").getObject(),
+                    new PropertyModel<String>(notificationTO, "recipientAttrName"));
+            recipientAttrName.setChoices(getSchemaNames(recipientAttrType.getModelObject()));
+            recipientAttrName.addRequiredLabel();
+            add(recipientAttrName);
+
+            recipientAttrType.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    recipientAttrName.setChoices(getSchemaNames(recipientAttrType.getModelObject()));
+                    target.add(recipientAttrName);
+                }
+            });
+
+            final AjaxDropDownChoicePanel<String> template = new AjaxDropDownChoicePanel<String>(
+                    "template", getString("template"),
+                    new PropertyModel<String>(notificationTO, "template"));
+
+            template.setChoices(CollectionUtils.collect(
+                    restClient.getAllAvailableTemplates(), new Transformer<MailTemplateTO, String>() {
+
+                @Override
+                public String transform(final MailTemplateTO input) {
+                    return input.getKey();
+                }
+            }, new ArrayList<String>()));
+
+            template.addRequiredLabel();
+            add(template);
+
+            final AjaxDropDownChoicePanel<TraceLevel> traceLevel = new AjaxDropDownChoicePanel<TraceLevel>(
+                    "traceLevel", getString("traceLevel"),
+                    new PropertyModel<TraceLevel>(notificationTO, "traceLevel"));
+            traceLevel.setChoices(Arrays.asList(TraceLevel.values()));
+            traceLevel.addRequiredLabel();
+            add(traceLevel);
+
+            final AjaxCheckBoxPanel isActive = new AjaxCheckBoxPanel("isActive",
+                    getString("isActive"), new PropertyModel<Boolean>(notificationTO, "active"));
+
+            if (createFlag) {
+                isActive.getField().setDefaultModelObject(Boolean.TRUE);
+            }
+            add(isActive);
+        }
+
+    }
+
+    public class Events extends WizardStep {
+
+        private static final long serialVersionUID = -7709805590497687958L;
+
+        public Events(final NotificationHandler modelObject) {
+            final NotificationTO notificationTO = modelObject.getInnerObject();
+            add(new LoggerCategoryPanel(
+                    "eventSelection",
+                    loggerRestClient.listEvents(),
+                    new PropertyModel<List<String>>(notificationTO, "events"),
+                    pageRef,
+                    "Notification") {
+
+                private static final long serialVersionUID = 6429053774964787735L;
+
+                @Override
+                protected String[] getListRoles() {
+                    return new String[] {};
+                }
+
+                @Override
+                protected String[] getChangeRoles() {
+                    return new String[] {};
+                }
+            });
+        }
+
+    }
+
+    public class About extends Panel {
+
+        private static final long serialVersionUID = -9149543787708482882L;
+
+        public About(final String id, final IModel<Pair<String, List<SearchClause>>> model) {
+            super(id, model);
+            setOutputMarkupId(true);
+
+            final List<String> anyTypeTOs = CollectionUtils.collect(
+                    new AnyTypeRestClient().list(),
+                    EntityTOUtils.<String, AnyTypeTO>keyTransformer(),
+                    new ArrayList<String>());
+
+            final AjaxDropDownChoicePanel<String> type
+                    = new AjaxDropDownChoicePanel<String>("about", "about", new Model<String>() {
+
+                        private static final long serialVersionUID = -2350296434572623272L;
+
+                        @Override
+                        public String getObject() {
+                            return model.getObject().getLeft();
+                        }
+
+                        @Override
+                        public void setObject(final String object) {
+                            model.setObject(Pair.of(object, model.getObject().getRight()));
+                        }
+
+                    });
+            type.setChoices(anyTypeTOs);
+            type.addRequiredLabel();
+            add(type);
+
+            final ListModel<SearchClause> clauseModel = new ListModel<SearchClause>() {
+
+                private static final long serialVersionUID = 3769540249683319782L;
+
+                @Override
+                public List<SearchClause> getObject() {
+                    return model.getObject().getRight();
+                }
+
+                @Override
+                public void setObject(final List<SearchClause> object) {
+                    model.getObject().setValue(object);
+                }
+
+            };
+
+            final WebMarkupContainer searchContainer = new WebMarkupContainer("search");
+            add(searchContainer.setOutputMarkupId(true));
+
+            searchContainer.add(getClauseBuilder(model.getObject().getLeft(), clauseModel).build("clauses"));
+
+            type.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    clauseModel.getObject().clear();
+                    searchContainer.addOrReplace(getClauseBuilder(type.getModelObject(), clauseModel).build("clauses").
+                            setRenderBodyOnly(true));
+                    target.add(searchContainer);
+                }
+            });
+        }
+
+        private AbstractSearchPanel.Builder<?> getClauseBuilder(
+                final String type, final ListModel<SearchClause> clauseModel) {
+            AbstractSearchPanel.Builder<?> clause;
+
+            switch (type) {
+                case "USER":
+                    clause = new UserSearchPanel.Builder(clauseModel);
+                    break;
+                case "GROUP":
+                    clause = new GroupSearchPanel.Builder(clauseModel);
+                    break;
+                default:
+                    clause = new AnyObjectSearchPanel.Builder(type, clauseModel);
+                    break;
+            }
+            return clause;
+        }
+
+    }
+
+    public class Abouts extends WizardStep {
+
+        private static final long serialVersionUID = -7709805590497687958L;
+
+        public Abouts(final NotificationHandler modelObject) {
+            final WebMarkupContainer aboutContainer = new WebMarkupContainer("about");
+            aboutContainer.setOutputMarkupId(true);
+            add(aboutContainer);
+
+            final IModel<List<Pair<String, List<SearchClause>>>> model
+                    = new PropertyModel<List<Pair<String, List<SearchClause>>>>(modelObject, "aboutClauses");
+
+            aboutContainer.add(new MultiPanel<Pair<String, List<SearchClause>>>("abouts", "abouts", model, false) {
+
+                private static final long serialVersionUID = -2481579077338205547L;
+
+                @Override
+                protected Pair<String, List<SearchClause>> newModelObject() {
+                    return Pair.<String, List<SearchClause>>of(AnyTypeKind.USER.name(), new ArrayList<SearchClause>());
+                }
+
+                @Override
+                protected About getItemPanel(final ListItem<Pair<String, List<SearchClause>>> item) {
+
+                    return new About("panel", new Model<Pair<String, List<SearchClause>>>() {
+
+                        private static final long serialVersionUID = 6799404673615637845L;
+
+                        @Override
+                        public Pair<String, List<SearchClause>> getObject() {
+                            return item.getModelObject();
+                        }
+
+                        @Override
+                        public void setObject(final Pair<String, List<SearchClause>> object) {
+                            item.setModelObject(object);
+                        }
+
+                        @Override
+                        public void detach() {
+                            // no detach
+                        }
+                    });
+                }
+            }.hideLabel());
+        }
+    }
+
+    public class Recipients extends WizardStep {
+
+        private static final long serialVersionUID = -7709805590497687958L;
+
+        public Recipients(final NotificationHandler modelObject) {
+            final NotificationTO notificationTO = modelObject.getInnerObject();
+            final boolean createFlag = notificationTO.getKey() == null || notificationTO.getKey() <= 0;
+
+            final AjaxTextFieldPanel staticRecipientsFieldPanel
+                    = new AjaxTextFieldPanel("panel", "staticRecipients", new Model<String>());
+            staticRecipientsFieldPanel.addValidator(EmailAddressValidator.getInstance());
+
+            final MultiFieldPanel<String> staticRecipients = new MultiFieldPanel.Builder<String>(
+                    new PropertyModel<List<String>>(notificationTO, "staticRecipients")).
+                    build("staticRecipients", "staticRecipients", staticRecipientsFieldPanel);
+
+            add(staticRecipients.hideLabel());
+
+            final AnyObjectSearchPanel recipients = new UserSearchPanel.Builder(
+                    new PropertyModel<List<SearchClause>>(modelObject, "recipientClauses")).
+                    required(false).build("recipients");
+            add(recipients);
+
+            final AjaxCheckBoxPanel selfAsRecipient = new AjaxCheckBoxPanel("selfAsRecipient",
+                    getString("selfAsRecipient"), new PropertyModel<Boolean>(notificationTO, "selfAsRecipient"));
+            add(selfAsRecipient);
+
+            if (createFlag) {
+                selfAsRecipient.getField().setDefaultModelObject(Boolean.FALSE);
+            }
+        }
+
+    }
+
+    private List<String> getSchemaNames(final IntMappingType type) {
+        final List<String> result;
+
+        if (type == null) {
+            result = Collections.<String>emptyList();
+        } else {
+            switch (type) {
+                case UserPlainSchema:
+                    result = CollectionUtils.collect(
+                            schemaRestClient.<PlainSchemaTO>getSchemas(SchemaType.PLAIN, AnyTypeKind.USER.name()),
+                            EntityTOUtils.<String, PlainSchemaTO>keyTransformer(), new ArrayList<String>());
+                    break;
+
+                case UserDerivedSchema:
+                    result = CollectionUtils.collect(
+                            schemaRestClient.<DerSchemaTO>getSchemas(SchemaType.DERIVED, AnyTypeKind.USER.name()),
+                            EntityTOUtils.<String, DerSchemaTO>keyTransformer(), new ArrayList<String>());
+                    break;
+
+                case UserVirtualSchema:
+                    result = CollectionUtils.collect(
+                            schemaRestClient.<VirSchemaTO>getSchemas(SchemaType.VIRTUAL, AnyTypeKind.USER.name()),
+                            EntityTOUtils.<String, VirSchemaTO>keyTransformer(), new ArrayList<String>());
+                    break;
+
+                case Username:
+                    result = Collections.singletonList("Username");
+                    break;
+
+                default:
+                    result = Collections.<String>emptyList();
+            }
+        }
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/notifications/SelectedEventsPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/SelectedEventsPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/SelectedEventsPanel.java
new file mode 100644
index 0000000..cf396f6
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/SelectedEventsPanel.java
@@ -0,0 +1,172 @@
+/*
+ * 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.notifications;
+
+import java.util.List;
+import java.util.Set;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.markup.html.form.ListMultipleChoice;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.util.ListModel;
+
+public class SelectedEventsPanel extends Panel {
+
+    private static final long serialVersionUID = -4832450230348213500L;
+
+    private final WebMarkupContainer selectionContainer;
+
+    private ListMultipleChoice<String> selectedEvents;
+
+    private final IModel<List<String>> model;
+
+    public SelectedEventsPanel(final String id, final IModel<List<String>> model) {
+        super(id);
+
+        this.model = model;
+
+        selectionContainer = new WebMarkupContainer("selectionContainer");
+        selectionContainer.setOutputMarkupId(true);
+        add(selectionContainer);
+
+        selectedEvents = new ListMultipleChoice<String>("selectedEvents", new ListModel<String>(), model) {
+
+            private static final long serialVersionUID = 1226677544225737338L;
+
+            @Override
+            protected void onComponentTag(final ComponentTag tag) {
+                super.onComponentTag(tag);
+                tag.remove("size");
+                tag.remove("multiple");
+                tag.put("size", 5);
+            }
+        };
+
+        selectedEvents.setMaxRows(5);
+        selectedEvents.setChoiceRenderer(new IChoiceRenderer<String>() {
+
+            private static final long serialVersionUID = -4288397951948436434L;
+
+            @Override
+            public Object getDisplayValue(final String object) {
+                return object;
+            }
+
+            @Override
+            public String getIdValue(final String object, final int index) {
+                return object;
+            }
+
+            @Override
+            public String getObject(final String id, final IModel<? extends List<? extends String>> choices) {
+                return id;
+            }
+        });
+
+        selectedEvents.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -151291731388673682L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                send(SelectedEventsPanel.this.getPage(),
+                        Broadcast.BREADTH,
+                        new InspectSelectedEvent(target, selectedEvents.getModelValue()));
+            }
+        });
+
+        selectionContainer.add(selectedEvents);
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof EventSelectionChanged) {
+            final EventSelectionChanged eventSelectionChanged = (EventSelectionChanged) event.getPayload();
+
+            for (String toBeRemoved : eventSelectionChanged.getToBeRemoved()) {
+                model.getObject().remove(toBeRemoved);
+            }
+
+            for (String toBeAdded : eventSelectionChanged.getToBeAdded()) {
+                if (!model.getObject().contains(toBeAdded)) {
+                    model.getObject().add(toBeAdded);
+                }
+            }
+
+            eventSelectionChanged.getTarget().add(selectionContainer);
+        }
+    }
+
+    public static class InspectSelectedEvent {
+
+        private final AjaxRequestTarget target;
+
+        private final String event;
+
+        public InspectSelectedEvent(final AjaxRequestTarget target, final String event) {
+            this.target = target;
+            this.event = event;
+        }
+
+        public AjaxRequestTarget getTarget() {
+            return target;
+        }
+
+        public String getEvent() {
+            return event;
+        }
+    }
+
+    public static class EventSelectionChanged {
+
+        private final AjaxRequestTarget target;
+
+        private final Set<String> toBeRemoved;
+
+        private final Set<String> toBeAdded;
+
+        public EventSelectionChanged(
+                final AjaxRequestTarget target,
+                final Set<String> toBeAdded,
+                final Set<String> toBeRemoved) {
+            this.target = target;
+            this.toBeAdded = toBeAdded;
+            this.toBeRemoved = toBeRemoved;
+        }
+
+        public AjaxRequestTarget getTarget() {
+            return target;
+        }
+
+        public Set<String> getToBeRemoved() {
+            return toBeRemoved;
+        }
+
+        public Set<String> getToBeAdded() {
+            return toBeAdded;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/pages/Notifications.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Notifications.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Notifications.java
index b1b7ee9..c09657c 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/Notifications.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Notifications.java
@@ -18,7 +18,17 @@
  */
 package org.apache.syncope.client.console.pages;
 
+import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel;
+import java.util.ArrayList;
+import java.util.List;
 import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.notifications.MailTemplateDirectoryPanel;
+import org.apache.syncope.client.console.notifications.NotificationDirectoryPanel;
+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.panel.Panel;
+import org.apache.wicket.model.ResourceModel;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 
 public class Notifications extends BasePage {
@@ -29,5 +39,36 @@ public class Notifications extends BasePage {
         super(parameters);
 
         body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", Dashboard.class));
+
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        content.setMarkupId("notifications");
+        content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList()));
+        body.add(content);
+    }
+
+    private List<ITab> buildTabList() {
+        final List<ITab> tabs = new ArrayList<>();
+
+        tabs.add(new AbstractTab(new ResourceModel("notifications")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new NotificationDirectoryPanel(panelId, getPageReference());
+            }
+        });
+
+        tabs.add(new AbstractTab(new ResourceModel("notification.templates")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new MailTemplateDirectoryPanel(panelId, getPageReference());
+            }
+        });
+        return tabs;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/pages/Roles.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Roles.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Roles.java
index 02ac0e3..3758258 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/Roles.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Roles.java
@@ -20,7 +20,6 @@ package org.apache.syncope.client.console.pages;
 
 import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
 import org.apache.syncope.client.console.panels.RoleDirectoryPanel;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import org.apache.syncope.client.console.wizards.WizardMgtPanel;
 import org.apache.syncope.client.console.wizards.role.RoleHandler;
 import org.apache.syncope.client.console.wizards.role.RoleWizardBuilder;
@@ -46,8 +45,7 @@ public class Roles extends BasePage {
             private static final long serialVersionUID = -5960765294082359003L;
 
         }.disableCheckBoxes().addNewItemPanelBuilder(
-                new RoleWizardBuilder(BaseModal.CONTENT_ID, new RoleTO(), getPageReference()), true).
-                build("rolesPanel");
+                new RoleWizardBuilder(new RoleTO(), getPageReference()), true).build("rolesPanel");
 
         content.add(rolesPanel);
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java
index 3ff36bd..f6aeaa5 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java
@@ -25,7 +25,6 @@ import org.apache.syncope.client.console.panels.search.SearchClausePanel;
 import org.apache.syncope.client.console.panels.search.SearchUtils;
 import org.apache.syncope.client.console.panels.search.UserSearchPanel;
 import org.apache.syncope.client.console.rest.AnyTypeClassRestClient;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.tabs.Accordion;
 import org.apache.syncope.client.console.wizards.any.AnyObjectWizardBuilder;
 import org.apache.syncope.client.console.wizards.any.GroupWizardBuilder;
@@ -200,7 +199,7 @@ public class AnyPanel extends Panel {
                         anyTypeTO.getKey(),
                         pageRef).setRealm(realmTO.getFullPath()).setFiltered(true).
                         setFiql(fiql).addNewItemPanelBuilder(new UserWizardBuilder(
-                        BaseModal.CONTENT_ID, userTO, anyTypeTO.getClasses(), pageRef)).build(id);
+                        userTO, anyTypeTO.getClasses(), pageRef)).build(id);
                 MetaDataRoleAuthorizationStrategy.authorize(panel, WebPage.RENDER, StandardEntitlement.USER_LIST);
                 break;
             case GROUP:
@@ -212,7 +211,7 @@ public class AnyPanel extends Panel {
                         anyTypeTO.getKey(),
                         pageRef).setRealm(realmTO.getFullPath()).setFiltered(true).
                         setFiql(fiql).addNewItemPanelBuilder(new GroupWizardBuilder(
-                        BaseModal.CONTENT_ID, groupTO, anyTypeTO.getClasses(), pageRef)).build(id);
+                        groupTO, anyTypeTO.getClasses(), pageRef)).build(id);
                 // list of group is available to all authenticated users
                 break;
             case ANY_OBJECT:
@@ -226,7 +225,7 @@ public class AnyPanel extends Panel {
                         anyTypeTO.getKey(),
                         pageRef).setRealm(realmTO.getFullPath()).setFiltered(true).
                         setFiql(fiql).addNewItemPanelBuilder(new AnyObjectWizardBuilder(
-                        BaseModal.CONTENT_ID, anyObjectTO, anyTypeTO.getClasses(), pageRef)).build(id);
+                        anyObjectTO, anyTypeTO.getClasses(), pageRef)).build(id);
                 MetaDataRoleAuthorizationStrategy.authorize(
                         panel, WebPage.RENDER, AnyEntitlement.LIST.getFor(anyTypeTO.getKey()));
                 break;

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyTypeClassesPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyTypeClassesPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyTypeClassesPanel.java
index 03005f9..fdfbc65 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyTypeClassesPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyTypeClassesPanel.java
@@ -33,7 +33,6 @@ import org.apache.syncope.client.console.commons.DirectoryDataProvider;
 import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
-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.ActionLinksPanel;
 import org.apache.syncope.client.console.wizards.AbstractModalPanelBuilder;
@@ -60,13 +59,12 @@ public class AnyTypeClassesPanel extends TypesDirectoryPanel<AnyTypeClassTO, Any
         super(id, pageRef);
         disableCheckBoxes();
 
-        this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<AnyTypeClassTO>(
-                BaseModal.CONTENT_ID, new AnyTypeClassTO(), pageRef) {
+        this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<AnyTypeClassTO>(new AnyTypeClassTO(), pageRef) {
 
             private static final long serialVersionUID = -6388405037134399367L;
 
             @Override
-            public ModalPanel<AnyTypeClassTO> build(final int index, final AjaxWizard.Mode mode) {
+            public ModalPanel<AnyTypeClassTO> build(final String id, final int index, final AjaxWizard.Mode mode) {
                 final AnyTypeClassTO modelObject = newModelObject();
                 return new AnyTypeClassModalPanel(modal, modelObject, pageRef) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyTypesPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyTypesPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyTypesPanel.java
index f77654c..6b3c042 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyTypesPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyTypesPanel.java
@@ -34,7 +34,6 @@ import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
 import org.apache.syncope.client.console.panels.AnyTypesPanel.AnyTypeProvider;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
-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.ActionLinksPanel;
 import org.apache.syncope.client.console.wizards.AbstractModalPanelBuilder;
@@ -61,13 +60,12 @@ public class AnyTypesPanel extends TypesDirectoryPanel<AnyTypeTO, AnyTypeProvide
         super(id, pageRef);
         disableCheckBoxes();
 
-        this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<AnyTypeTO>(
-                BaseModal.CONTENT_ID, new AnyTypeTO(), pageRef) {
+        this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<AnyTypeTO>(new AnyTypeTO(), pageRef) {
 
             private static final long serialVersionUID = -6388405037134399367L;
 
             @Override
-            public ModalPanel<AnyTypeTO> build(final int index, final AjaxWizard.Mode mode) {
+            public ModalPanel<AnyTypeTO> build(final String id, final int index, final AjaxWizard.Mode mode) {
                 final AnyTypeTO modelObject = newModelObject();
                 return new AnyTypeModalPanel(modal, modelObject, pageRef) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateModalPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateModalPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateModalPanel.java
index 6e71a2b..87eeee3 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateModalPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateModalPanel.java
@@ -35,8 +35,8 @@ public class ParametersCreateModalPanel extends AbstractModalPanel<AttrTO> {
             final PageReference pageRef) {
         super(modal, pageRef);
         this.attrTO = attrTO;
-        add(new ParametersCreateWizardPanel("parametersCreateWizardPanel",
-                new ParametersCreateWizardPanel.ParametersForm(), pageRef).build(AjaxWizard.Mode.CREATE));
+        add(new ParametersCreateWizardPanel(new ParametersCreateWizardPanel.ParametersForm(), pageRef).
+                build("parametersCreateWizardPanel", AjaxWizard.Mode.CREATE));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateWizardPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateWizardPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateWizardPanel.java
index 060160e..eace16b 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateWizardPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateWizardPanel.java
@@ -37,8 +37,8 @@ public class ParametersCreateWizardPanel extends AjaxWizardBuilder<ParametersCre
 
     private static final Logger LOG = LoggerFactory.getLogger(ParametersCreateWizardPanel.class);
 
-    public ParametersCreateWizardPanel(final String id, final ParametersForm defaultItem, final PageReference pageRef) {
-        super(id, defaultItem, pageRef);
+    public ParametersCreateWizardPanel(final ParametersForm defaultItem, final PageReference pageRef) {
+        super(defaultItem, pageRef);
 
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersPanel.java
index 67da08d..df265fd 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersPanel.java
@@ -95,13 +95,12 @@ public class ParametersPanel extends DirectoryPanel<
 
         addInnerObject(modalDetails);
 
-        this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<AttrTO>(
-                BaseModal.CONTENT_ID, new AttrTO(), pageRef) {
+        this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<AttrTO>(new AttrTO(), pageRef) {
 
             private static final long serialVersionUID = 1995192603527154740L;
 
             @Override
-            public ModalPanel<AttrTO> build(final int index, final AjaxWizard.Mode mode) {
+            public ModalPanel<AttrTO> build(final String id, final int index, final AjaxWizard.Mode mode) {
                 return new ParametersCreateModalPanel(modal, newModelObject(), pageRef);
             }
         }, true);

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/panels/RelationshipTypesPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/RelationshipTypesPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/RelationshipTypesPanel.java
index eea61bf..314ce42 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/RelationshipTypesPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/RelationshipTypesPanel.java
@@ -34,7 +34,6 @@ import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
 import org.apache.syncope.client.console.panels.RelationshipTypesPanel.RelationshipTypeProvider;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
-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.ActionLinksPanel;
 import org.apache.syncope.client.console.wizards.AbstractModalPanelBuilder;
@@ -61,13 +60,13 @@ public class RelationshipTypesPanel extends TypesDirectoryPanel<RelationshipType
         super(id, pageRef);
         disableCheckBoxes();
 
-        this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<RelationshipTypeTO>(
-                BaseModal.CONTENT_ID, new RelationshipTypeTO(), pageRef) {
+        this.addNewItemPanelBuilder(
+                new AbstractModalPanelBuilder<RelationshipTypeTO>(new RelationshipTypeTO(), pageRef) {
 
             private static final long serialVersionUID = -6388405037134399367L;
 
             @Override
-            public ModalPanel<RelationshipTypeTO> build(final int index, final AjaxWizard.Mode mode) {
+            public ModalPanel<RelationshipTypeTO> build(final String id, final int index, final AjaxWizard.Mode mode) {
                 final RelationshipTypeTO modelObject = newModelObject();
                 return new RelationshipTypeModalPanel(modal, modelObject, pageRef) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
index 12a19d0..11c7468 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
@@ -92,8 +92,8 @@ public class ResourceModal<T extends Serializable> extends AbstractResourceModal
         //--------------------------------
         // Resource provision panels
         //--------------------------------
-        final ListViewPanel.Builder<ProvisionTO> builder =
-                new ListViewPanel.Builder<ProvisionTO>(ProvisionTO.class, pageRef) {
+        final ListViewPanel.Builder<ProvisionTO> builder = new ListViewPanel.Builder<ProvisionTO>(ProvisionTO.class,
+                pageRef) {
 
             private static final long serialVersionUID = 4907732721283972943L;
 
@@ -163,7 +163,7 @@ public class ResourceModal<T extends Serializable> extends AbstractResourceModal
                     }
                 }, ActionLink.ActionType.DELETE, StandardEntitlement.RESOURCE_DELETE);
 
-        builder.addNewItemPanelBuilder(new ProvisionWizardBuilder("wizard", model.getObject(), pageRef));
+        builder.addNewItemPanelBuilder(new ProvisionWizardBuilder(model.getObject(), pageRef));
 
         tabs.add(new AbstractTab(new ResourceModel("provisions")) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypePanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypePanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypePanel.java
index bd3b18d..7d61e47 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypePanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypePanel.java
@@ -38,7 +38,6 @@ import org.apache.syncope.client.console.panels.SchemaTypePanel.SchemaProvider;
 import org.apache.syncope.client.console.rest.SchemaRestClient;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
-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.ActionLinksPanel;
 import org.apache.syncope.client.console.wizards.AbstractModalPanelBuilder;
@@ -89,13 +88,14 @@ public class SchemaTypePanel extends TypesDirectoryPanel<AbstractSchemaTO, Schem
         this.schemaType = schemaType;
 
         try {
-            this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<AbstractSchemaTO>(
-                    BaseModal.CONTENT_ID, schemaType.getToClass().newInstance(), pageRef) {
+            this.addNewItemPanelBuilder(
+                    new AbstractModalPanelBuilder<AbstractSchemaTO>(schemaType.getToClass().newInstance(), pageRef) {
 
                 private static final long serialVersionUID = -6388405037134399367L;
 
                 @Override
-                public ModalPanel<AbstractSchemaTO> build(final int index, final AjaxWizard.Mode mode) {
+                public ModalPanel<AbstractSchemaTO> build(
+                        final String id, final int index, final AjaxWizard.Mode mode) {
                     final AbstractSchemaTO modelObject = newModelObject();
                     return new SchemaModalPanel(modal, modelObject, pageRef) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/panels/SecurityQuestionsPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/SecurityQuestionsPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/SecurityQuestionsPanel.java
index 4db7acf..38d81cf 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/SecurityQuestionsPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/SecurityQuestionsPanel.java
@@ -35,7 +35,6 @@ import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
 import org.apache.syncope.client.console.panels.SecurityQuestionsPanel.SecurityQuestionsProvider;
 import org.apache.syncope.client.console.rest.SecurityQuestionRestClient;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
-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.ActionLinksPanel;
 import org.apache.syncope.client.console.wizards.AbstractModalPanelBuilder;
@@ -70,13 +69,13 @@ public class SecurityQuestionsPanel extends DirectoryPanel<
             }
         }.disableCheckBoxes());
 
-        this.addNewItemPanelBuilder(new AbstractModalPanelBuilder<SecurityQuestionTO>(
-                BaseModal.CONTENT_ID, new SecurityQuestionTO(), pageRef) {
+        this.addNewItemPanelBuilder(
+                new AbstractModalPanelBuilder<SecurityQuestionTO>(new SecurityQuestionTO(), pageRef) {
 
             private static final long serialVersionUID = -6388405037134399367L;
 
             @Override
-            public ModalPanel<SecurityQuestionTO> build(final int index, final AjaxWizard.Mode mode) {
+            public ModalPanel<SecurityQuestionTO> build(final String id, final int index, final AjaxWizard.Mode mode) {
                 final SecurityQuestionTO modelObject = newModelObject();
                 return new SecurityQuestionsModalPanel(modal, modelObject, pageRef);
             }

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/rest/NotificationRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/NotificationRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/NotificationRestClient.java
index 053768a..3f9d820 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/NotificationRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/NotificationRestClient.java
@@ -18,14 +18,23 @@
  */
 package org.apache.syncope.client.console.rest;
 
+import java.io.InputStream;
 import java.util.List;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.to.MailTemplateTO;
 import org.apache.syncope.common.lib.to.NotificationTO;
+import org.apache.syncope.common.lib.types.MailTemplateFormat;
+import org.apache.syncope.common.rest.api.service.MailTemplateService;
 import org.apache.syncope.common.rest.api.service.NotificationService;
+import org.slf4j.LoggerFactory;
 
 public class NotificationRestClient extends BaseRestClient {
 
     private static final long serialVersionUID = 6328933265096511690L;
 
+    protected static final org.slf4j.Logger LOG = LoggerFactory.getLogger(NotificationRestClient.class);
+
     public List<NotificationTO> getAllNotifications() {
         return getService(NotificationService.class).list();
     }
@@ -45,4 +54,34 @@ public class NotificationRestClient extends BaseRestClient {
     public void delete(final Long key) {
         getService(NotificationService.class).delete(key);
     }
+
+    public List<MailTemplateTO> getAllAvailableTemplates() {
+        return getService(MailTemplateService.class).list();
+    }
+
+    public void createTemplate(final MailTemplateTO mailTemplateTO) {
+        getService(MailTemplateService.class).create(mailTemplateTO);
+    }
+
+    public void deleteTemplate(final String key) {
+        getService(MailTemplateService.class).delete(key);
+    }
+
+    public MailTemplateTO readTemplate(final String key) {
+        return getService(MailTemplateService.class).read(key);
+    }
+
+    public String readTemplateFormat(final String key, final MailTemplateFormat format) {
+        try {
+            return IOUtils.toString(InputStream.class.cast(
+                    getService(MailTemplateService.class).getFormat(key, format).getEntity()));
+        } catch (Exception e) {
+            LOG.info("Error retrieving mail tenplate content");
+            return StringUtils.EMPTY;
+        }
+    }
+
+    public void updateTemplateFormat(final String key, final String str, final MailTemplateFormat format) {
+        getService(MailTemplateService.class).setFormat(key, format, IOUtils.toInputStream(str));
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/tasks/NotificationTaskDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/tasks/NotificationTaskDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/tasks/NotificationTaskDirectoryPanel.java
new file mode 100644
index 0000000..6309e74
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/tasks/NotificationTaskDirectoryPanel.java
@@ -0,0 +1,208 @@
+/*
+ * 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.tasks;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+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.TaskDataProvider;
+import org.apache.syncope.client.console.panels.ModalPanel;
+import org.apache.syncope.client.console.panels.MultilevelPanel;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.CollectionPropertyColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.NotificationTaskTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
+
+/**
+ * Tasks page.
+ */
+public abstract class NotificationTaskDirectoryPanel
+        extends TaskDirectoryPanel<NotificationTaskTO> implements ModalPanel<NotificationTaskTO> {
+
+    private static final long serialVersionUID = 4984337552918213290L;
+
+    protected NotificationTaskDirectoryPanel(
+            final BaseModal<?> baseModal,
+            final MultilevelPanel multiLevelPanelRef,
+            final PageReference pageRef) {
+        super(baseModal, multiLevelPanelRef, pageRef);
+        initResultTable();
+    }
+
+    @Override
+    protected List<IColumn<NotificationTaskTO, String>> getColumns() {
+        final List<IColumn<NotificationTaskTO, String>> columns = new ArrayList<>();
+
+        columns.add(new PropertyColumn<NotificationTaskTO, String>(
+                new StringResourceModel("key", this, null), "key", "key"));
+
+        columns.add(new PropertyColumn<NotificationTaskTO, String>(
+                new StringResourceModel("subject", this, null), "subject", "subject"));
+
+        columns.add(new CollectionPropertyColumn<NotificationTaskTO>(
+                new StringResourceModel("recipients", this, null), "recipients", "recipients"));
+
+        columns.add(new DatePropertyColumn<NotificationTaskTO>(
+                new StringResourceModel("start", this, null), "start", "start"));
+
+        columns.add(new DatePropertyColumn<NotificationTaskTO>(
+                new StringResourceModel("end", this, null), "end", "end"));
+
+        columns.add(new PropertyColumn<NotificationTaskTO, String>(
+                new StringResourceModel("latestExecStatus", this, null), "latestExecStatus", "latestExecStatus"));
+
+        columns.add(new ActionColumn<NotificationTaskTO, String>(new ResourceModel("actions", "")) {
+
+            private static final long serialVersionUID = 2054811145491901166L;
+
+            @Override
+            public ActionLinksPanel<NotificationTaskTO> getActions(
+                    final String componentId, final IModel<NotificationTaskTO> model) {
+
+                final NotificationTaskTO taskTO = model.getObject();
+
+                final ActionLinksPanel<NotificationTaskTO> panel = ActionLinksPanel.<NotificationTaskTO>builder().
+                        add(new ActionLink<NotificationTaskTO>() {
+
+                            private static final long serialVersionUID = -3722207913631435501L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target, final NotificationTaskTO modelObject) {
+                                viewTask(taskTO, target);
+                            }
+                        }, ActionLink.ActionType.VIEW, StandardEntitlement.TASK_READ).
+                        add(new ActionLink<NotificationTaskTO>() {
+
+                            private static final long serialVersionUID = -3722207913631435501L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target, final NotificationTaskTO modelObject) {
+                                try {
+                                    restClient.startExecution(taskTO.getKey(), new Date());
+                                    info(getString(Constants.OPERATION_SUCCEEDED));
+                                    target.add(container);
+                                } catch (SyncopeClientException e) {
+                                    error(StringUtils.isBlank(e.getMessage())
+                                            ? e.getClass().getName() : e.getMessage());
+                                    LOG.error("While running {}", taskTO.getKey(), e);
+                                }
+                                SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+                            }
+                        }, ActionLink.ActionType.EXECUTE, StandardEntitlement.TASK_EXECUTE).
+                        add(new ActionLink<NotificationTaskTO>() {
+
+                            private static final long serialVersionUID = -3722207913631435501L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target, final NotificationTaskTO modelObject) {
+                                try {
+                                    restClient.delete(taskTO.getKey(), NotificationTaskTO.class);
+                                    info(getString(Constants.OPERATION_SUCCEEDED));
+                                    target.add(container);
+                                } catch (SyncopeClientException e) {
+                                    LOG.error("While deleting {}", taskTO.getKey(), e);
+                                    error(StringUtils.isBlank(e.getMessage())
+                                            ? e.getClass().getName() : e.getMessage());
+                                }
+                                SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+                            }
+                        }, ActionLink.ActionType.DELETE, StandardEntitlement.TASK_DELETE).build(componentId);
+
+                return panel;
+            }
+
+            @Override
+            public ActionLinksPanel<NotificationTaskTO> getHeader(final String componentId) {
+                final ActionLinksPanel.Builder<NotificationTaskTO> panel = ActionLinksPanel.builder();
+
+                return panel.add(new ActionLink<NotificationTaskTO>() {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final NotificationTaskTO ignore) {
+                        if (target != null) {
+                            target.add(container);
+                        }
+                    }
+                }, ActionLink.ActionType.RELOAD, StandardEntitlement.TASK_LIST).build(componentId);
+            }
+        });
+
+        return columns;
+    }
+
+    @Override
+    protected Collection<ActionType> getBulkActions() {
+        final List<ActionType> bulkActions = new ArrayList<>();
+        bulkActions.add(ActionType.DELETE);
+        bulkActions.add(ActionType.EXECUTE);
+        return bulkActions;
+    }
+
+    @Override
+    protected NotificationTasksProvider dataProvider() {
+        return new NotificationTasksProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_PROPAGATION_TASKS_PAGINATOR_ROWS;
+    }
+
+    public class NotificationTasksProvider extends TaskDataProvider<NotificationTaskTO> {
+
+        private static final long serialVersionUID = 4725679400450513556L;
+
+        public NotificationTasksProvider(final int paginatorRows) {
+            super(paginatorRows, TaskType.PROPAGATION, restClient);
+        }
+
+        @Override
+        public Iterator<NotificationTaskTO> iterator(final long first, final long count) {
+            final int page = ((int) first / paginatorRows);
+
+            final List<NotificationTaskTO> tasks = restClient.list(
+                    NotificationTaskTO.class, (page < 0 ? 0 : page) + 1, paginatorRows, getSort());
+
+            Collections.sort(tasks, getComparator());
+            return tasks.iterator();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
index e8d902c..61d1d48 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
@@ -81,7 +81,7 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends AjaxWizardBui
     };
 
     public SchedTaskWizardBuilder(final T taskTO, final PageReference pageRef) {
-        super("wizard", taskTO, pageRef);
+        super(taskTO, pageRef);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.java
new file mode 100644
index 0000000..f04fa5a
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.java
@@ -0,0 +1,47 @@
+/*
+ * 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.extensions.markup.html.repeater.data.table;
+
+import java.util.List;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.ResourceModel;
+
+public class CollectionPanel extends Panel {
+
+    private static final long serialVersionUID = -4042497356836230377L;
+
+    @SuppressWarnings("unchecked")
+    public CollectionPanel(final String id, final List values) {
+        super(id);
+
+        add(new ListView("collection", values) {
+
+            private static final long serialVersionUID = 4949588177564901031L;
+
+            @Override
+            protected void populateItem(final ListItem item) {
+                final String value = item.getModelObject() == null ? null : item.getModelObject().toString();
+                item.add(new Label("item", new ResourceModel(value, value)));
+            }
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPropertyColumn.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPropertyColumn.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPropertyColumn.java
new file mode 100644
index 0000000..a5fd774
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPropertyColumn.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+
+public class CollectionPropertyColumn<T> extends PropertyColumn<T, String> {
+
+    private static final long serialVersionUID = 8077865338230121496L;
+
+    public CollectionPropertyColumn(
+            final IModel<String> displayModel,
+            final String sortProperty,
+            final String propertyExpression) {
+        super(displayModel, sortProperty, propertyExpression);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void populateItem(
+            final Item<ICellPopulator<T>> cellItem, final String componentId, final IModel<T> rowModel) {
+
+        final Object value = getDataModel(rowModel).getObject();
+
+        if (value instanceof Collection) {
+            final List values = new ArrayList((Collection) value);
+            Collections.sort(values);
+            cellItem.add(new CollectionPanel(componentId, values));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AbstractMultiPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AbstractMultiPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AbstractMultiPanel.java
new file mode 100644
index 0000000..08b304e
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AbstractMultiPanel.java
@@ -0,0 +1,217 @@
+/*
+ * 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 java.util.List;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.IndicatorAjaxSubmitLink;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
+
+public abstract class AbstractMultiPanel<INNER> extends AbstractFieldPanel<List<INNER>> {
+
+    private static final long serialVersionUID = -6322397761456513324L;
+
+    private final ListView<INNER> view;
+
+    private final boolean eventTemplate;
+
+    private final WebMarkupContainer container;
+
+    private final Form<?> form;
+
+    public AbstractMultiPanel(
+            final String id,
+            final String name,
+            final IModel<List<INNER>> model,
+            final boolean eventTemplate) {
+
+        super(id, name, model);
+
+        this.eventTemplate = eventTemplate;
+
+        // -----------------------
+        // Object container definition
+        // -----------------------
+        container = new WebMarkupContainer("multiValueContainer");
+        container.setOutputMarkupId(true);
+        add(container);
+
+        form = new Form<>("innerForm");
+        container.add(form);
+        // -----------------------
+
+        view = new InnerView("view", name, model);
+
+        final List<INNER> obj = model.getObject();
+        if (obj == null || obj.isEmpty()) {
+            form.addOrReplace(getNoDataFragment(model, name));
+        } else {
+            form.addOrReplace(getDataFragment());
+        }
+    }
+
+    private Fragment getNoDataFragment(final IModel<List<INNER>> model, final String label) {
+        final Fragment fragment = new Fragment("content", "noDataFragment", AbstractMultiPanel.this);
+        fragment.add(new Label("field-label", new ResourceModel(label, label)));
+        fragment.add(getPlusFragment(model, label));
+        return fragment;
+    }
+
+    private Fragment getDataFragment() {
+        final Fragment contentFragment = new Fragment("content", "dataFragment", AbstractMultiPanel.this);
+        contentFragment.add(view.setOutputMarkupId(true));
+        return contentFragment;
+    }
+
+    private Fragment getPlusFragment(final IModel<List<INNER>> model, final String label) {
+        final IndicatorAjaxSubmitLink plus = new IndicatorAjaxSubmitLink("add") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                //Add current component
+                model.getObject().add(newModelObject());
+
+                if (model.getObject().size() == 1) {
+                    form.addOrReplace(getDataFragment());
+                }
+
+                target.add(container);
+            }
+
+            @Override
+            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                error(getString(Constants.OPERATION_ERROR));
+                super.onError(target, form);
+                SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+            }
+
+        };
+
+        final Fragment fragment = new Fragment("panelPlus", "fragmentPlus", AbstractMultiPanel.this);
+        fragment.addOrReplace(plus);
+        fragment.setRenderBodyOnly(true);
+
+        return fragment;
+    }
+
+    public ListView<INNER> getView() {
+        return view;
+    }
+
+    @Override
+    public AbstractMultiPanel<INNER> setModelObject(final List<INNER> object) {
+        view.setModelObject(object);
+        return this;
+    }
+
+    public static class MultiValueSelectorEvent {
+
+        private final AjaxRequestTarget target;
+
+        public MultiValueSelectorEvent(final AjaxRequestTarget target) {
+            this.target = target;
+        }
+
+        public AjaxRequestTarget getTarget() {
+            return target;
+        }
+    }
+
+    protected abstract INNER newModelObject();
+
+    private final class InnerView extends ListView<INNER> {
+
+        private static final long serialVersionUID = -9180479401817023838L;
+
+        private final String label;
+
+        private final IModel<List<INNER>> model;
+
+        private InnerView(final String id, final String label, final IModel<List<INNER>> model) {
+            super(id, model);
+            this.label = label;
+            this.model = model;
+        }
+
+        @Override
+        protected void populateItem(final ListItem<INNER> item) {
+
+            final Panel panel = getItemPanel(item);
+
+            item.add(panel.setRenderBodyOnly(true));
+
+            final IndicatorAjaxSubmitLink minus = new IndicatorAjaxSubmitLink("drop") {
+
+                private static final long serialVersionUID = -7978723352517770644L;
+
+                @Override
+                protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                    //Drop current component
+                    model.getObject().remove(item.getModelObject());
+                    clearInput(panel);
+
+                    if (model.getObject().isEmpty()) {
+                        form.addOrReplace(getNoDataFragment(model, label));
+                    }
+
+                    target.add(container);
+
+                    if (eventTemplate) {
+                        send(getPage(), Broadcast.BREADTH, new MultiValueSelectorEvent(target));
+                    }
+                }
+
+                @Override
+                protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                    onSubmit(target, form);
+                }
+            };
+
+            item.add(minus);
+
+            final Fragment fragment;
+            if (item.getIndex() == model.getObject().size() - 1) {
+                fragment = getPlusFragment(model, label);
+            } else {
+                fragment = new Fragment("panelPlus", "emptyFragment", AbstractMultiPanel.this);
+            }
+
+            item.add(fragment.setRenderBodyOnly(true));
+        }
+    }
+
+    protected abstract Panel getItemPanel(final ListItem<INNER> item);
+
+    protected void clearInput(final Panel panel) {
+        // do nothing by default
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/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 8243d22..c254074 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
@@ -46,6 +46,8 @@ public abstract class ActionLink<T extends Serializable> implements Serializable
         CLONE("create"),
         CREATE("create"),
         EDIT("read"),
+        HTML_EDIT("read"),
+        TEXT_EDIT("read"),
         RESET("update"),
         ENABLE("update"),
         NOT_FOND("read"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/8455cb96/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.java
index 5474291..44b070a 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.java
@@ -61,6 +61,8 @@ public final class ActionLinksPanel<T extends Serializable> extends Panel {
         super.add(new Fragment("panelClone", "emptyFragment", this));
         super.add(new Fragment("panelCreate", "emptyFragment", this));
         super.add(new Fragment("panelEdit", "emptyFragment", this));
+        super.add(new Fragment("panelHtmlEdit", "emptyFragment", this));
+        super.add(new Fragment("panelTextEdit", "emptyFragment", this));
         super.add(new Fragment("panelReset", "emptyFragment", this));
         super.add(new Fragment("panelEnable", "emptyFragment", this));
         super.add(new Fragment("panelNotFound", "emptyFragment", this));
@@ -306,6 +308,44 @@ public final class ActionLinksPanel<T extends Serializable> extends Panel {
                 }.setVisible(link.isEnabled(model.getObject())));
                 break;
 
+            case HTML_EDIT:
+                fragment = new Fragment("panelHtmlEdit", "fragmentHtmlEdit", this);
+
+                fragment.addOrReplace(new IndicatingAjaxLink<Void>("htmlEditLink") {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        link.onClick(target, model.getObject());
+                    }
+
+                    @Override
+                    public String getAjaxIndicatorMarkupId() {
+                        return disableIndicator ? StringUtils.EMPTY : super.getAjaxIndicatorMarkupId();
+                    }
+                }.setVisible(link.isEnabled(model.getObject())));
+                break;
+
+            case TEXT_EDIT:
+                fragment = new Fragment("panelTextEdit", "fragmentTextEdit", this);
+
+                fragment.addOrReplace(new IndicatingAjaxLink<Void>("textEditLink") {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        link.onClick(target, model.getObject());
+                    }
+
+                    @Override
+                    public String getAjaxIndicatorMarkupId() {
+                        return disableIndicator ? StringUtils.EMPTY : super.getAjaxIndicatorMarkupId();
+                    }
+                }.setVisible(link.isEnabled(model.getObject())));
+                break;
+
             case ENABLE:
                 fragment = new Fragment("panelEnable", "fragmentEnable", this);
 
@@ -788,6 +828,14 @@ public final class ActionLinksPanel<T extends Serializable> extends Panel {
                 super.addOrReplace(new Fragment("panelEdit", "emptyFragment", this));
                 break;
 
+            case HTML_EDIT:
+                super.addOrReplace(new Fragment("panelHtmlEdit", "emptyFragment", this));
+                break;
+
+            case TEXT_EDIT:
+                super.addOrReplace(new Fragment("panelTestEdit", "emptyFragment", this));
+                break;
+
             case VIEW:
                 super.addOrReplace(new Fragment("panelView", "emptyFragment", this));
                 break;