diff --git a/html/main.php b/html/main.php
index 07a2c9c1ce7a8d2bc37aaa413e9c6c8df37d7404..4cc898d6e236a59b135dcf66aa296b31edd4d594 100644
--- a/html/main.php
+++ b/html/main.php
@@ -129,7 +129,8 @@ if (($expired == POSIX_WARN_ABOUT_EXPIRATION) && !session::is_set('POSIX_WARN_AB
     if ($value == 'user') {
       if (!isset($_GET['plug']) || ($_GET['plug'] != $key)) {
         $_GET['plug'] = $key;
-        msg_dialog::display(_('Warning'), _('Your password has expired, please set a new one.'), WARNING_DIALOG);
+        $warning = new FusionDirectoryWarning(htmlescape(_('Your password has expired, please set a new one.')));
+        $warning->display();
       }
       break;
     }
@@ -165,7 +166,8 @@ $ui->getSizeLimitHandler()->update();
 
 /* Check for memory */
 if (memory_get_usage() > (to_byte(ini_get('memory_limit')) - 2048000)) {
-  msg_dialog::display(_("Configuration error"), _("Running out of memory!"), WARNING_DIALOG);
+  $warning = new FusionDirectoryWarning(htmlescape(_('Running out of memory!')));
+  $warning->display();
 }
 
 /* show web frontend */
diff --git a/include/class_CopyPasteHandler.inc b/include/class_CopyPasteHandler.inc
index 5669321f371f5c5dfdf087a0c2641bd49116f5b7..feef7e8c8545655e98ea4fcc0d5e8b0124d6533a 100644
--- a/include/class_CopyPasteHandler.inc
+++ b/include/class_CopyPasteHandler.inc
@@ -200,7 +200,8 @@ class CopyPasteHandler
         foreach ($this->disallowed_objects as $entry) {
           $dns[] = $entry['dn'];
         }
-        msg_dialog::display(_('Permission'), msgPool::permCreate($dns), INFO_DIALOG);
+        $error = new FusionDirectoryPermissionError(msgPool::permCreate($dns));
+        $error->display();
       }
       $this->require_update = FALSE;
     }
diff --git a/include/class_acl.inc b/include/class_acl.inc
index 386720718e8c2b8a6d1579c9dd50e076e4d484a5..0a5329785829cf677ae7cd41d55dabf8ddfa8b43 100644
--- a/include/class_acl.inc
+++ b/include/class_acl.inc
@@ -100,7 +100,13 @@ class acl
 
     /* Handle unknown types */
     if (!in_array($type, ['subtree', 'base'])) {
-      msg_dialog::display(_("Internal error"), sprintf(_("Unkown ACL type '%s'!\nYou might need to run \"fusiondirectory-setup --migrate-acls\" to migrate your acls to the new format."), $type), ERROR_DIALOG);
+      $error = new FusionDirectoryError(
+        nl2br(htmlescape(sprintf(
+          _("Unkown ACL type \"%s\"!\nYou might need to run \"fusiondirectory-setup --migrate-acls\" to migrate your acls to the new format."),
+          $type
+        )))
+      );
+      $error->display();
       $a = [];
     }
     return $a;
diff --git a/include/class_config.inc b/include/class_config.inc
index 9b992d0319e8f2842f8195e9d60d0b1d52f2e0a5..b5db3a0aeee6a9e0d6b1232a54049b4cce6d0360 100644
--- a/include/class_config.inc
+++ b/include/class_config.inc
@@ -925,9 +925,13 @@ class config
       /* Send message if not done already */
       if (!session::is_set('snapshotFailMessageSend')) {
         session::set('snapshotFailMessageSend', TRUE);
-        msg_dialog::display(_('Configuration error'),
-            sprintf(_('The snapshot functionality is enabled, but the required variable "%s" is not set.'),
-                    'snapshotBase'), ERROR_DIALOG);
+        $error = new FusionDirectoryError(
+          htmlescape(sprintf(
+            _('The snapshot functionality is enabled, but the required variable "%s" is not set.'),
+            'snapshotBase'
+          ))
+        );
+        $error->display();
       }
       return FALSE;
     }
@@ -937,8 +941,13 @@ class config
       /* Send message if not done already */
       if (!session::is_set('snapshotFailMessageSend')) {
         session::set('snapshotFailMessageSend', TRUE);
-        msg_dialog::display(_('Configuration error'),
-            sprintf(_('The snapshot functionality is enabled, but the required compression module is missing. Please install "%s".'), 'php5-zip / php5-gzip'), ERROR_DIALOG);
+        $error = new FusionDirectoryError(
+          htmlescape(sprintf(
+            _('The snapshot functionality is enabled, but the required compression module is missing. Please install "%s".'),
+            'php-zip / php-gzip'
+          ))
+        );
+        $error->display();
       }
       return FALSE;
     }
diff --git a/include/class_exceptions.inc b/include/class_exceptions.inc
index ecaa716f15b15faee8e8a218c72a1a7f053973b2..e127526cd259a6efe5109a893319e2c6514c0e3d 100644
--- a/include/class_exceptions.inc
+++ b/include/class_exceptions.inc
@@ -37,6 +37,13 @@ class LDIFImportException extends FusionDirectoryException
 {
 }
 
+/*! \class LDIFExportException
+    \brief Exception class which can be thrown by LDAP class if LDIF export fails
+*/
+class LDIFExportException extends FusionDirectoryException
+{
+}
+
 /*! \class LdapGeneralizedTimeBadFormatException
     \brief Exception class which can be thrown by LdapGeneralizedTime if the format does not match
 */
diff --git a/include/class_ldap.inc b/include/class_ldap.inc
index 7cce10a97e60f285c54019adceea0e673fca360b..f259550e89e3ced0846d0a4875f239e0f9d40c49 100644
--- a/include/class_ldap.inc
+++ b/include/class_ldap.inc
@@ -355,7 +355,8 @@ class LDAP
       /* Check if query took longer as specified in max_ldap_query_time */
       $diff = microtime(TRUE) - $startTime;
       if ($this->max_ldap_query_time && ($diff > $this->max_ldap_query_time)) {
-        msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
+        $warning = new FusionDirectoryWarning(htmlescape(sprintf(_('LDAP performance is poor: last query took about %.2fs!'), $diff)));
+        $warning->display();
       }
 
       $this->log("LDAP operation: time=".$diff." operation=search('".$this->basedn."', '$filter')");
@@ -1098,30 +1099,24 @@ class LDAP
   /*!
    * \brief  Generates an ldif for all entries matching the filter settings, scope and limit.
    *
-   * \param  $dn           The entry to export.
+   * \param  string $dn       The entry to export.
    *
-   * \param  $filter       Limit the exported object to those maching this filter.
+   * \param  string $filter   Limit the exported object to those maching this filter.
    *
-   * \param  $scope        'base', 'sub' .. see manpage for 'ldapmodify' for details.
+   * \param  string $scope    'base', 'sub' .. see manpage for 'ldapmodify' for details.
    *
-   * \param  $limit        Limits the result.
+   * \param  int $limit       Limits the result.
    */
-  function generateLdif ($dn, $filter = "(objectClass=*)", $scope = 'sub', $limit = 0)
+  function generateLdif (string $dn, string $filter = '(objectClass=*)', string $scope = 'sub', int $limit = 0): string
   {
-    // Ensure that limit is numeric if not skip here.
-    if (!empty($limit) && !is_numeric($limit)) {
-      trigger_error(sprintf("Invalid parameter for limit '%s', a numeric value is required."), $limit);
-      return NULL;
-    }
-    $limit = (!$limit) ? '' : ' -z '.$limit;
+    $limit = (($limit == 0) ? '' : ' -z '.$limit);
 
     // Check scope values
     $scope = trim($scope);
     if (!empty($scope) && !in_array($scope, ['base', 'one', 'sub', 'children'])) {
-      trigger_error(sprintf("Invalid parameter for scope '%s', please use 'base', 'one', 'sub' or 'children'."), $scope);
-      return NULL;
+      throw new LDIFExportException(sprintf('Invalid parameter for scope "%s", please use "base", "one", "sub" or "children".'), $scope);
     }
-    $scope = (!empty($scope)) ? ' -s '.$scope : '';
+    $scope = (empty($scope) ? '' : ' -s '.$scope);
 
     // Prepare parameters to be valid for shell execution
     $dn     = escapeshellarg($dn);
@@ -1152,12 +1147,10 @@ class LDAP
 
       // Close the process and check its return value
       if (proc_close($process) != 0) {
-        $this->error = $err;
-        return NULL;
+        throw new LDIFExportException($err);
       }
     } else {
-      $this->error = _("proc_open failed to execute ldapsearch");
-      return NULL;
+      throw new LDIFExportException(_('proc_open failed to execute ldapsearch'));
     }
     return $res;
   }
diff --git a/include/class_logging.inc b/include/class_logging.inc
index 3bd5f3062b9d6fe15a5f57b1ac9b8b885b677eda..1e4c79a16870321775b074e4389f2616ed5efa34 100644
--- a/include/class_logging.inc
+++ b/include/class_logging.inc
@@ -76,7 +76,8 @@ class logging
     if (count($msgs)) {
       foreach ($msgs as $msg) {
         trigger_error('Logging failed, reason was: '.$msg);
-        msg_dialog::display(_('Internal error'), sprintf(_('Logging failed: %s'), $msg), ERROR_DIALOG);
+        $error = new FusionDirectoryError(htmlescape(sprintf(_('Logging failed: %s'), $msg)));
+        $error->display();
       }
     } else {
       if (is_object($config) && preg_match('/true/i', $config->get_cfg_value('logging', ''))) {
@@ -215,11 +216,17 @@ class logging
         msg_dialog::displayChecks($errors);
       }
     } catch (FusionDirectoryException $e) {
-      msg_dialog::display(
-        _('Error'),
-        htmlescape(sprintf(_('Failed to log event (%s - %s): %s'), $entry['action'], $entry['objecttype'], $e->getMessage())),
-        ERROR_DIALOG
+      $error = new FusionDirectoryError(
+        htmlescape(sprintf(
+          _('Failed to log event (%s - %s): %s'),
+          $entry['action'],
+          $entry['objecttype'],
+          $e->getMessage()
+        )),
+        0,
+        $e
       );
+      $error->display();
     }
   }
 }
diff --git a/include/class_timezone.inc b/include/class_timezone.inc
index 0dccc3f7cddcbb4a8ddd4875d9639d43d52bfa88..8ca62f9ed73ea0e54ba4e54d178af56dec42e698 100644
--- a/include/class_timezone.inc
+++ b/include/class_timezone.inc
@@ -47,11 +47,13 @@ class timezone
       if (@date_default_timezone_set($tz)) {
         return TRUE;
       } else {
-        msg_dialog::display(
-          _('Configuration error'),
-          sprintf(_('The timezone setting "%s" in your configuration is not valid.'), $tz),
-          ERROR_DIALOG
+        $error = new FusionDirectoryError(
+          htmlescape(sprintf(
+            _('The timezone setting "%s" in your configuration is not valid.'),
+            $tz
+          ))
         );
+        $error->display();
       }
     }
     return FALSE;
diff --git a/include/errors/class_FusionDirectoryError.inc b/include/errors/class_FusionDirectoryError.inc
index 9eb1576a90355d9d4923aa0ff3063c89ce52519f..ce1c925cc444259ff87afd5abbd0275ac4eaebe7 100644
--- a/include/errors/class_FusionDirectoryError.inc
+++ b/include/errors/class_FusionDirectoryError.inc
@@ -55,7 +55,7 @@ class FusionDirectoryError extends Error
     msg_dialog::display(...$this->computeMsgDialogParameters());
   }
 
-  public static function formatTrace(Throwable $throwable): array
+  public static function formatTrace (Throwable $throwable): array
   {
     $trace = $throwable->getTrace();
 
diff --git a/include/errors/class_FusionDirectoryLdapError.inc b/include/errors/class_FusionDirectoryLdapError.inc
new file mode 100644
index 0000000000000000000000000000000000000000..7392b932ab8d9bdb16054b1ae5b3e82b94cd7105
--- /dev/null
+++ b/include/errors/class_FusionDirectoryLdapError.inc
@@ -0,0 +1,31 @@
+<?php
+/*
+  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
+
+  Copyright (C) 2019-2020  FusionDirectory
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*! \class FusionDirectoryLdapError
+    \brief Error returned by an LDAP operation called from FusionDirectory
+*/
+class FusionDirectoryLdapError extends SimplePluginLdapError
+{
+  public function __construct (string $dn = NULL, int $operation = NULL, string $ldapError = '', int $code = 0, Throwable $previous = NULL)
+  {
+    parent::__construct(NULL, $dn, $operation, $ldapError, $code, $previous);
+  }
+}
diff --git a/include/errors/class_FusionDirectoryPermissionError.inc b/include/errors/class_FusionDirectoryPermissionError.inc
new file mode 100644
index 0000000000000000000000000000000000000000..0a0fe8c46e8e8802093cd374a9928b88b8e5591a
--- /dev/null
+++ b/include/errors/class_FusionDirectoryPermissionError.inc
@@ -0,0 +1,30 @@
+<?php
+/*
+  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
+
+  Copyright (C) 2019-2020  FusionDirectory
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*! \class FusionDirectoryPermissionError
+*/
+class FusionDirectoryPermissionError extends SimplePluginPermissionError
+{
+  public function __construct (string $htmlMessage, int $code = 0, Throwable $previous = NULL)
+  {
+    parent::__construct(NULL, $htmlMessage, $code, $previous);
+  }
+}
diff --git a/include/errors/class_SimplePluginPermissionError.inc b/include/errors/class_SimplePluginPermissionError.inc
new file mode 100644
index 0000000000000000000000000000000000000000..be078cf639aea545e0aee7b3cfc2a82623479d85
--- /dev/null
+++ b/include/errors/class_SimplePluginPermissionError.inc
@@ -0,0 +1,26 @@
+<?php
+/*
+  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
+
+  Copyright (C) 2019-2020  FusionDirectory
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*! \class SimplePluginPermissionError
+*/
+class SimplePluginPermissionError extends SimplePluginError
+{
+}
diff --git a/include/functions.inc b/include/functions.inc
index 8d1ccdf09d5c11925ee66ebe50e0ec40395c9bf3..72a3601c8dcbd472c1ef2a2c0974d52a307c3b5b 100644
--- a/include/functions.inc
+++ b/include/functions.inc
@@ -403,8 +403,9 @@ function add_lock ($object, $user)
   }
 
   /* Just a sanity check... */
-  if ($object == '' || $user == '') {
-    msg_dialog::display(_('Internal error'), _('Error while adding a lock. Contact the developers!'), ERROR_DIALOG);
+  if (empty($object) || empty($user)) {
+    $error = new FusionDirectoryError(htmlescape(_('Error while adding a lock. Contact the developers!')));
+    $error->display();
     return;
   }
 
@@ -426,7 +427,14 @@ function add_lock ($object, $user)
       ['fdUserDn']);
   }
   if (!$ldap->success()) {
-    msg_dialog::display(_('Configuration error'), sprintf(_('Cannot create locking information in LDAP tree. Please contact your administrator!').'<br><br>'._('LDAP server returned: %s'), '<br><br><i>'.$ldap->get_error().'</i>'), ERROR_DIALOG);
+    $error = new FusionDirectoryError(
+      sprintf(
+        htmlescape(_('Cannot create locking information in LDAP tree. Please contact your administrator!')).
+          '<br><br>'.htmlescape(_('LDAP server returned: %s')),
+        '<br><br><i>'.htmlescape($ldap->get_error()).'</i>'
+      )
+    );
+    $error->display();
     return;
   }
 
@@ -434,7 +442,8 @@ function add_lock ($object, $user)
   if ($ldap->count() == 0) {
     $attrs  = [];
     $name   = md5($object);
-    $ldap->cd('cn='.$name.','.get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
+    $dn     = 'cn='.$name.','.get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
+    $ldap->cd($dn);
     $attrs = [
       'objectClass'     => 'fdLockEntry',
       'fdUserDn'        => $user,
@@ -444,7 +453,8 @@ function add_lock ($object, $user)
     ];
     $ldap->add($attrs);
     if (!$ldap->success()) {
-      msg_dialog::display(_('LDAP error'), msgPool::ldaperror($ldap->get_error(), "cn=$name,".get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'], 0), LDAP_ERROR);
+      $error = new FusionDirectoryLdapError($dn, LDAP_ADD, $ldap->get_error(), $ldap->get_errno());
+      $error->display();
       return;
     }
   }
@@ -487,11 +497,13 @@ function del_lock ($object)
 
   /* Check for existance and remove the entry */
   $ldap = $config->get_ldap_link();
-  $ldap->cd(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
+  $dn   = get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
+  $ldap->cd($dn);
   $ldap->search('(&(objectClass=fdLockEntry)(fdObjectDn='.base64_encode($object).'))', ['fdObjectDn']);
   $attrs = $ldap->fetch();
   if (!$ldap->success()) {
-    msg_dialog::display(_('LDAP error'), msgPool::ldaperror($ldap->get_error(), $ldap->getDN(), LDAP_DEL, ERROR_DIALOG));
+    $error = new FusionDirectoryLdapError($dn, LDAP_SEARCH, $ldap->get_error(), $ldap->get_errno());
+    $error->display();
     return;
   } elseif (!empty($attrs['dn'])) {
     $ldap->rmdir($attrs['dn']);
@@ -537,7 +549,8 @@ function get_lock ($object)
 {
   /* Sanity check */
   if ($object == '') {
-    msg_dialog::display(_('Internal error'), _('Error while adding a lock. Contact the developers!'), ERROR_DIALOG);
+    $error = new FusionDirectoryError(htmlescape(_('Error while adding a lock. Contact the developers!')));
+    $error->display();
     return FALSE;
   }
 
@@ -596,10 +609,12 @@ function get_locks ($objects, $allow_readonly = FALSE)
 
   /* Get LDAP link, check for presence of the lock entry */
   $ldap = $config->get_ldap_link();
-  $ldap->cd(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
+  $dn   = get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
+  $ldap->cd($dn);
   $ldap->search($filter, ['fdUserDn','fdObjectDn', 'fdLockTimestamp']);
   if (!$ldap->success()) {
-    msg_dialog::display(_('LDAP error'), msgPool::ldaperror($ldap->get_error(), '', LDAP_SEARCH), LDAP_ERROR);
+    $error = new FusionDirectoryLdapError($dn, LDAP_SEARCH, $ldap->get_error(), $ldap->get_errno());
+    $error->display();
     return FALSE;
   }
 
@@ -615,7 +630,8 @@ function get_locks ($objects, $allow_readonly = FALSE)
 
   if (!is_array($objects) && (count($locks) > 1)) {
     /* Hmm. We're removing broken LDAP information here and issue a warning. */
-    msg_dialog::display(_('Warning'), _('Found multiple locks for object to be locked. This should not happen - cleaning up multiple references.'), WARNING_DIALOG);
+    $warning = new FusionDirectoryWarning(htmlescape(_('Found multiple locks for object to be locked. This should not happen - cleaning up multiple references.')));
+    $warning->display();
 
     /* Clean up these references now... */
     foreach ($locks as $lock) {
@@ -1312,7 +1328,13 @@ function clean_smarty_compile_dir ($directory)
             continue;
           }
           if (is_file($directory.'/'.$file) && !unlink($directory.'/'.$file)) {
-            msg_dialog::display(_("Internal error"), sprintf(_("File '%s' could not be deleted. Try fusiondirectory-setup --check-directories to fix permissions."), $directory."/".$file), ERROR_DIALOG);
+            $error = new FusionDirectoryError(
+              htmlescape(sprintf(
+                _('File "%s" could not be deleted. Try "fusiondirectory-setup --check-directories" to fix permissions.'),
+                $directory."/".$file
+              ))
+            );
+            $error->display();
           }
         }
       } else {
@@ -1343,14 +1365,28 @@ function create_revision ($revision_file, $revision)
   $result = FALSE;
 
   if (is_dir(dirname($revision_file)) && is_writable(dirname($revision_file))) {
-    if ($fh = fopen($revision_file, 'w')) {
+    $fh = fopenWithErrorHandling($revision_file, 'w');
+    if (is_array($fh)) {
+      $error = new FusionDirectoryError(
+        htmlescape(_('Cannot write to revision file:')).'<br/>'.
+        implode(
+          '<br/>',
+          array_map('htmlescape', $fh)
+        )
+      );
+      $error->display();
+      return $result;
+    } else {
       if (fwrite($fh, $revision)) {
         $result = TRUE;
       }
       fclose($fh);
     }
-  } else {
-    msg_dialog::display(_('Internal error'), _('Cannot write to revision file!'), ERROR_DIALOG);
+  }
+
+  if (!$result) {
+    $error = new FusionDirectoryError(htmlescape(_('Cannot write to revision file!')));
+    $error->display();
   }
 
   return $result;
@@ -1373,15 +1409,23 @@ function compare_revision ($revision_file, $revision)
 
   if (file_exists($revision_file) && is_readable($revision_file)) {
     // Open file
-    if ($fh = fopen($revision_file, "r")) {
+    $fh = fopenWithErrorHandling($revision_file, 'r');
+    if (is_array($fh)) {
+      $error = new FusionDirectoryError(
+        htmlescape(_('Cannot read revision file:')).'<br/>'.
+        implode(
+          '<br/>',
+          array_map('htmlescape', $fh)
+        )
+      );
+      $error->display();
+    } else {
       // Compare File contents with current revision
       if ($revision == fread($fh, filesize($revision_file))) {
         $result = TRUE;
       }
       // Close file
       fclose($fh);
-    } else {
-      msg_dialog::display(_('Internal error'), _('Cannot read revision file!'), ERROR_DIALOG);
     }
   }
 
@@ -1495,7 +1539,8 @@ function check_schema (array $cfg)
   $ldap = new ldapMultiplexer($ldapObj);
   $objectclasses = $ldap->get_objectclasses(TRUE);
   if (count($objectclasses) == 0) {
-    msg_dialog::display(_('LDAP warning'), _('Cannot get schema information from server. No schema check possible!'), WARNING_DIALOG);
+    $warning = new FusionDirectoryWarning(htmlescape(_('Cannot get schema information from server. No schema check possible!')));
+    $warning->display();
     return $checks;
   }
 
diff --git a/include/management/class_management.inc b/include/management/class_management.inc
index 1ac9495bfd46d5047c6b75dab6e09c2d24d0a8cb..10bce9c51532454820598d3d54f02bbfbc1f0b25 100644
--- a/include/management/class_management.inc
+++ b/include/management/class_management.inc
@@ -447,7 +447,8 @@ class management
           return $this->getHeader().$str;
         }
       } catch (FusionDirectoryException $e) {
-        msg_dialog::display(_('Error'), $e->getMessage(), ERROR_DIALOG);
+        $error = new FusionDirectoryError(htmlescape($e->getMessage()), 0, $e);
+        $error->display();
       }
     }
 
@@ -458,7 +459,8 @@ class management
       try {
         $this->dialogObject->save_object();
       } catch (FusionDirectoryException $e) {
-        msg_dialog::display(_('Error'), $e->getMessage(), ERROR_DIALOG);
+        $error = new FusionDirectoryError(htmlescape($e->getMessage()), 0, $e);
+        $error->display();
         $this->closeDialogs();
       }
     }
@@ -780,7 +782,8 @@ class management
       }
 
       if ($entry->isTemplate()) {
-        msg_dialog::display(_('Error'), _('Applying a template to a template is not possible'), ERROR_DIALOG);
+        $error = new FusionDirectoryError(htmlescape(_('Applying a template to a template is not possible')));
+        $error->display();
         $this->currentDns = [];
         return;
       }
@@ -788,7 +791,8 @@ class management
       if (!isset($type)) {
         $type = $entry->type;
       } elseif ($entry->type != $type) {
-        msg_dialog::display(_('Error'), _('All selected entries need to share the same type to be able to apply a template to them'), ERROR_DIALOG);
+        $error = new FusionDirectoryError(htmlescape(_('All selected entries need to share the same type to be able to apply a template to them')));
+        $error->display();
         $this->currentDns = [];
         return;
       }
@@ -925,7 +929,8 @@ class management
       }
     }
     if (count($disallowed)) {
-      msg_dialog::display(_('Permission'), msgPool::permDelete($disallowed), INFO_DIALOG);
+      $error = new FusionDirectoryPermissionError(msgPool::permDelete($disallowed));
+      $error->display();
     }
 
     // We've at least one entry to delete.
@@ -989,7 +994,8 @@ class management
         // Remove the lock for the current object.
         del_lock($this->currentDn);
       } else {
-        msg_dialog::display(_('Permission error'), msgPool::permDelete($dn), ERROR_DIALOG);
+        $error = new FusionDirectoryPermissionError(msgPool::permDelete($dn));
+        $error->display();
         logging::log('security', 'management/'.get_class($this), $dn, [], 'Tried to trick deletion.');
       }
     }
@@ -1075,8 +1081,13 @@ class management
     if ($entry->snapshotCreationAllowed()) {
       $this->dialogObject = new SnapshotCreateDialog($this->currentDn, $this, '');
     } else {
-      msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to create a snapshot for %s.'), $this->currentDn),
-          ERROR_DIALOG);
+      $error = new FusionDirectoryError(
+        htmlescape(sprintf(
+          _('You are not allowed to create a snapshot for %s.'),
+          $this->currentDn
+        ))
+      );
+      $error->display();
     }
   }
 
@@ -1105,8 +1116,13 @@ class management
       logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Snapshot restoring initiated!');
       $this->dialogObject = new SnapshotRestoreDialog($this->currentDn, $this, empty($action['targets']), $aclCategories);
     } else {
-      msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to restore a snapshot for %s.'), $this->currentDn),
-          ERROR_DIALOG);
+      $error = new FusionDirectoryError(
+        htmlescape(sprintf(
+          _('You are not allowed to restore a snapshot for %s.'),
+          $this->currentDn
+        ))
+      );
+      $error->display();
     }
   }
 
@@ -1194,8 +1210,8 @@ class management
       $this->snapHandler->createSnapshot($dn, $description, $entry->type);
       logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot created!');
     } else {
-      msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn),
-          ERROR_DIALOG);
+      $error = new FusionDirectoryPermissionError(htmlescape(sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn)));
+      $error->display();
     }
   }
 
@@ -1222,8 +1238,8 @@ class management
         $this->saveChanges();
       }
     } else {
-      msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn),
-          ERROR_DIALOG);
+      $error = new FusionDirectoryPermissionError(htmlescape(sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn)));
+      $error->display();
     }
   }
 
@@ -1239,8 +1255,8 @@ class management
       $this->snapHandler->removeSnapshot($dn);
       logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot deleted');
     } else {
-      msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to delete a snapshot for %s.'), $dn),
-          ERROR_DIALOG);
+      $error = new FusionDirectoryPermissionError(htmlescape(sprintf(_('You are not allowed to delete a snapshot for %s.'), $dn)));
+      $error->display();
     }
   }
 
diff --git a/include/management/class_managementFilter.inc b/include/management/class_managementFilter.inc
index b585a262becfd5e95fc22733f72975f9fc76bda2..e0e36e7052f07b7c66e4a0097b804de5e6e36ac4 100644
--- a/include/management/class_managementFilter.inc
+++ b/include/management/class_managementFilter.inc
@@ -230,14 +230,16 @@ class managementFilter
             $entries[$dn] = new ListingEntry($this->parent->listing, 'template_'.$type, $dn, $entry, $row++);
           }
         } catch (FusionDirectoryException $e) {
-          msg_dialog::display(
-            sprintf(
-              _('Could not search for "%s" templates'),
-              $this->types['filter_type_'.$type]['infos']['name']
-            ),
-            $e->getMessage(),
-            WARNING_DIALOG
+          $warning = new FusionDirectoryWarning(
+            nl2br(htmlescape(sprintf(
+              _("Could not search for \"%s\" templates:\n%s"),
+              $this->types['filter_type_'.$type]['infos']['name'],
+              $e->getMessage()
+            ))),
+            0,
+            $e
           );
+          $warning->display();
         }
       }
 
@@ -250,14 +252,16 @@ class managementFilter
 
         $ldapEntries = $this->filterEntries($ldapEntries);
       } catch (FusionDirectoryException $e) {
-        msg_dialog::display(
-          sprintf(
-            _('Could not search for "%s"'),
-            $this->types['filter_type_'.$type]['infos']['name']
-          ),
-          $e->getMessage(),
-          WARNING_DIALOG
+        $warning = new FusionDirectoryWarning(
+          nl2br(htmlescape(sprintf(
+            _("Could not search for \"%s\":\n%s"),
+            $this->types['filter_type_'.$type]['infos']['name'],
+            $e->getMessage()
+          ))),
+          0,
+          $e
         );
+        $warning->display();
         $this->types['filter_type_'.$type]['show'] = FALSE;
         continue;
       }
diff --git a/include/management/class_managementListing.inc b/include/management/class_managementListing.inc
index 0803c7823baf30f79791f1eafc0a3c05ad8a9b84..5c3dce60dd69faf0c3726faab7992e3ec9729313 100644
--- a/include/management/class_managementListing.inc
+++ b/include/management/class_managementListing.inc
@@ -248,7 +248,8 @@ class managementListing
 
       // Check if a wrong base was supplied
       if (!$this->baseSelector->checkLastBaseUpdate()) {
-        msg_dialog::display(_('Error'), msgPool::check_base(), ERROR_DIALOG);
+        $error = new FusionDirectoryError(msgPool::check_base());
+        $error->display();
       }
 
       // Save base
diff --git a/include/management/snapshot/class_SnapshotHandler.inc b/include/management/snapshot/class_SnapshotHandler.inc
index 2c6f1618f67f1fe89f0bc10475aed3fafc67d05d..2bd2a7dfe0ded09521cb6ac92ad216eb61bca716 100644
--- a/include/management/snapshot/class_SnapshotHandler.inc
+++ b/include/management/snapshot/class_SnapshotHandler.inc
@@ -228,9 +228,17 @@ class SnapshotHandler
     /* Create object */
     $data = '';
     foreach ($dns as $tmp_dn) {
-      $data .= $ldap->generateLdif($tmp_dn, '(!(objectClass=gosaDepartment))', 'sub');
-      if (!$ldap->success()) {
-        msg_dialog::display(_('LDAP error'), msgPool::ldaperror($ldap->get_error(), $tmp_dn, '', get_class()), LDAP_ERROR);
+      try {
+        $data .= $ldap->generateLdif($tmp_dn, '(!(objectClass=gosaDepartment))', 'sub');
+      } catch (LDIFExportException $e) {
+        $error = new FusionDirectoryError(
+          htmlescape(sprintf(
+            _('Failed to create snapshot: %s'),
+            $e->getMessage()
+          ))
+        );
+        $error->display();
+        return;
       }
     }
 
@@ -265,7 +273,8 @@ class SnapshotHandler
     $ldap->add($target);
 
     if (!$ldap->success()) {
-      msg_dialog::display(_('LDAP error'), msgPool::ldaperror($ldap->get_error(), $new_base, '', get_class()), LDAP_ERROR);
+      $error = new FusionDirectoryLdapError($new_dn, LDAP_ADD, $ldap->get_error(), $ldap->get_errno());
+      $error->display();
     }
     logging::log('snapshot', 'create', $new_dn, array_keys($target), $ldap->get_error());
   }
@@ -282,7 +291,8 @@ class SnapshotHandler
     $ldap->cd($config->current['BASE']);
     $ldap->rmdir_recursive($dn);
     if (!$ldap->success()) {
-      msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn), LDAP_ERROR);
+      $error = new FusionDirectoryLdapError($dn, LDAP_DEL, $ldap->get_error(), $ldap->get_errno());
+      $error->display();
     }
     logging::log('snapshot', 'delete', $dn, [], $ldap->get_error());
   }
@@ -393,11 +403,13 @@ class SnapshotHandler
       /* Prepare import string */
       $data = gzuncompress($attrs['gosaSnapshotData'][0]);
       if ($data === FALSE) {
-        msg_dialog::display(_('Error'), _('There was a problem uncompressing snapshot data'), ERROR_DIALOG);
+        $error = new FusionDirectoryError(htmlescape(_('There was a problem uncompressing snapshot data')));
+        $error->display();
         return FALSE;
       }
     } else {
-      msg_dialog::display(_('Error'), _('Snapshot data could not be fetched'), ERROR_DIALOG);
+      $error = new FusionDirectoryError(htmlescape(_('Snapshot data could not be fetched')));
+      $error->display();
       return FALSE;
     }
 
@@ -406,12 +418,14 @@ class SnapshotHandler
       $ldap->import_complete_ldif($data, FALSE, FALSE);
       logging::log('snapshot', 'restore', $dn, [], $ldap->get_error());
       if (!$ldap->success()) {
-        msg_dialog::display(_('LDAP error'), msgPool::ldaperror($ldap->get_error(), $dn, '', get_class()), LDAP_ERROR);
+        $error = new FusionDirectoryLdapError($dn, NULL, $ldap->get_error(), $ldap->get_errno());
+        $error->display();
         return FALSE;
       }
       return $attrs['gosaSnapshotDN'][0];
     } catch (LDIFImportException $e) {
-      msg_dialog::display(_('LDAP error'), $e->getMessage(), ERROR_DIALOG);
+      $error = new FusionDirectoryError($e->getMessage(), 0, $e);
+      $error->display();
       logging::log('snapshot', 'restore', $dn, [], $e->getMessage());
       return FALSE;
     }
diff --git a/include/password-methods/class_passwordMethod.inc b/include/password-methods/class_passwordMethod.inc
index 95f961922c2aed5f77c05f007b9371efa1e5971d..22a99730dcefedeaddd745f941da4ad50155deb4 100644
--- a/include/password-methods/class_passwordMethod.inc
+++ b/include/password-methods/class_passwordMethod.inc
@@ -212,7 +212,8 @@ abstract class passwordMethod
         msg_dialog::displayChecks($errors);
       }
     } else {
-      msg_dialog::display(_('LDAP error'), msgPool::ldaperror($ldap->get_error(), $dn, LDAP_MOD), LDAP_ERROR);
+      $error = new FusionDirectoryLdapError($dn, LDAP_MOD, $ldap->get_error(), $ldap->get_errno());
+      $error->display();
     }
     return $ldap->success();
   }
diff --git a/include/password-methods/class_passwordMethodSasl.inc b/include/password-methods/class_passwordMethodSasl.inc
index 5311992bba19262106a2cb7efa27015df7e0bf6e..ba7b9a2d5d47a37b69b37c1289401d3ad82518b1 100644
--- a/include/password-methods/class_passwordMethodSasl.inc
+++ b/include/password-methods/class_passwordMethodSasl.inc
@@ -63,7 +63,8 @@ class passwordMethodSasl extends passwordMethod
         $attrs = $ldap->fetch();
         $this->uid = $attrs[$attr][0];
       } else {
-        msg_dialog::display(_('Error'), sprintf(_('Cannot change password, unknown user "%s"'), $dn), ERROR_DIALOG);
+        $error = new FusionDirectoryError(htmlescape(sprintf(_('Cannot change password, unknown user "%s"'), $dn)));
+        $error->display();
       }
     }
   }
@@ -93,7 +94,8 @@ class passwordMethodSasl extends passwordMethod
   {
     if (empty($this->exop)) {
       if (empty($this->realm)) {
-        msg_dialog::display(_('Error'), _('You need to fill saslRealm or saslExop in the configuration screen in order to use SASL'), ERROR_DIALOG);
+        $error = new FusionDirectoryError(htmlescape(_('You need to fill saslRealm or saslExop in the configuration screen in order to use SASL')));
+        $error->display();
       }
       return '{SASL}'.($locked ? '!' : '').$this->uid.'@'.$this->realm;
     } else {
diff --git a/include/password-methods/class_passwordMethodSsha.inc b/include/password-methods/class_passwordMethodSsha.inc
index f93b8028c236b31121284ed6d6ca7941044a3d6e..6cc0c01c85187eb21df73e239d74180acc3234e7 100644
--- a/include/password-methods/class_passwordMethodSsha.inc
+++ b/include/password-methods/class_passwordMethodSsha.inc
@@ -81,7 +81,8 @@ class passwordMethodSsha extends passwordMethod
     } elseif (function_exists('mhash')) {
       $nhash = mhash(MHASH_SHA1, $pwd.$salt);
     } else {
-      msg_dialog::display(_('Configuration error'), msgPool::missingext('mhash'), ERROR_DIALOG);
+      $error = new FusionDirectoryError(msgPool::missingext('mhash'));
+      $error->display();
       return FALSE;
     }
     return ($nhash == $hash);
diff --git a/include/simpleplugin/attributes/class_BaseSelectorAttribute.inc b/include/simpleplugin/attributes/class_BaseSelectorAttribute.inc
index 757dd4a8e544729f189396186ed91b900a01c6c5..326dcf38f59f7720b99af106889d9d2852c4df0c 100644
--- a/include/simpleplugin/attributes/class_BaseSelectorAttribute.inc
+++ b/include/simpleplugin/attributes/class_BaseSelectorAttribute.inc
@@ -86,10 +86,11 @@ class BaseSelectorAttribute extends Attribute
       /* Refresh base */
       if (!$this->baseSelector->update()) {
         if ($this->plugin->dn == 'new') {
-          msg_dialog::display(_('Error'), msgPool::permCreate(), ERROR_DIALOG);
+          $error = new SimplePluginPermissionError($this, msgPool::permCreate());
         } else {
-          msg_dialog::display(_('Error'), msgPool::permMove($this->plugin->dn), ERROR_DIALOG);
+          $error = new SimplePluginPermissionError($this, msgPool::permMove($this->plugin->dn));
         }
+        $error->display();
       }
       if ($this->value != $this->baseSelector->getBase()) {
         $this->setValue($this->baseSelector->getBase());
@@ -105,16 +106,16 @@ class BaseSelectorAttribute extends Attribute
     } else {
       /* Check if we are allowed to create/move this user */
       if (($this->orig_dn == 'new') && !$this->plugin->acl_is_createable($this->value)) {
-        return msgPool::permCreate();
+        return new SimplePluginPermissionError($this, msgPool::permCreate());
       } elseif (
         ($this->orig_dn != 'new') &&
         ($this->plugin->dn != $this->orig_dn) &&
         !$this->plugin->acl_is_moveable($this->value)) {
-        return msgPool::permMove($this->plugin->dn);
+        return new SimplePluginPermissionError($this, msgPool::permMove($this->plugin->dn));
       }
       // Check if a wrong base was supplied
       if (!$this->baseSelector->checkLastBaseUpdate()) {
-        return msgPool::check_base();
+        return new SimplePluginCheckError($this, msgPool::check_base());
       }
     }
   }
diff --git a/include/simpleplugin/attributes/class_FileAttribute.inc b/include/simpleplugin/attributes/class_FileAttribute.inc
index f391b8349dcc5815cf150fa1134b3a47b4131131..429dcec3455b2602b2091d49e8bb93fb26b8954a 100644
--- a/include/simpleplugin/attributes/class_FileAttribute.inc
+++ b/include/simpleplugin/attributes/class_FileAttribute.inc
@@ -30,13 +30,34 @@ class FileAttribute extends Attribute
     $this->postValue = $this->value;
     if (!empty($_FILES[$this->getHtmlId()]['name']) && $this->isVisible()) {
       if ($_FILES[$this->getHtmlId()]['size'] <= 0) {
-        msg_dialog::display(_("Error"), sprintf(_("Cannot read uploaded file: %s"), _("file is empty")), ERROR_DIALOG);
+        $error = new SimplePluginError(
+          $this,
+          htmlescape(sprintf(
+            _('Cannot read uploaded file: %s'),
+            _('file is empty')
+          ))
+        );
+        $error->display();
       } elseif (!file_exists($_FILES[$this->getHtmlId()]['tmp_name'])) {
         // Is there a tmp file, which we can use ?
-        msg_dialog::display(_("Error"), sprintf(_("Cannot read uploaded file: %s"), _("file not found")), ERROR_DIALOG);
-      } elseif (!$handle = @fopen($_FILES[$this->getHtmlId()]['tmp_name'], "r")) {
+        $error = new SimplePluginError(
+          $this,
+          htmlescape(sprintf(
+            _('Cannot read uploaded file: %s'),
+            _('file not found')
+          ))
+        );
+        $error->display();
+      } elseif (!$handle = @fopen($_FILES[$this->getHtmlId()]['tmp_name'], 'r')) {
         // Can we open the tmp file, for reading
-        msg_dialog::display(_("Error"), sprintf(_("Cannot read uploaded file: %s"), _("file not readable")), ERROR_DIALOG);
+        $error = new SimplePluginError(
+          $this,
+          htmlescape(sprintf(
+            _('Cannot read uploaded file: %s'),
+            _('file not readable')
+          ))
+        );
+        $error->display();
       } else {
         // Everything just fine :)
 
@@ -374,22 +395,20 @@ class ImageAttribute extends FileAttribute
       } catch (ImagickException $e) {
         /* Store the exception to return it in deserializeValue() */
         $this->imagickException = $e;
-        msg_dialog::display(
-          _('Error'),
-          sprintf(
-            _('Cannot set "%s" value, it contains invalid data: %s'),
-            ($this->getLabel() != '' ? $this->getLabel() : $this->getLdapName()),
-            $e->getMessage()
-          ),
-          ERROR_DIALOG
+        $error = new SimplePluginError(
+          $this,
+          SimplePluginCheckError::invalidValue($e->getMessage()),
+          0,
+          $e
         );
+        $error->display();
       }
     } else {
-      msg_dialog::display(
-        _('Error'),
-        _('Cannot save user picture, FusionDirectory requires the PHP module "imagick" to be installed!'),
-        ERROR_DIALOG
+      $error = new SimplePluginError(
+        $this,
+        htmlescape(_('Cannot save user picture, FusionDirectory requires the PHP module "imagick" to be installed!'))
       );
+      $error->display();
     }
   }
 
diff --git a/include/simpleplugin/attributes/class_SetAttribute.inc b/include/simpleplugin/attributes/class_SetAttribute.inc
index c82c4faa2255813b66427be1ef3d4489cca866cf..f0ea7b8e3b0c54737bd1742773a131c137e0db0a 100644
--- a/include/simpleplugin/attributes/class_SetAttribute.inc
+++ b/include/simpleplugin/attributes/class_SetAttribute.inc
@@ -667,10 +667,9 @@ class OrderedArrayAttribute extends SetAttribute
       $this->attribute->applyPostValue();
       if ($error = $this->attribute->check()) {
         if (is_string($error)) {
-          msg_dialog::display(sprintf(_('Invalid value for %s'), $this->getLabel()), $error);
-        } else {
-          $error->display();
+          $error = new SimplePluginCheckError($this, $error);
         }
+        $error->display();
       } else {
         $this->addPostValue($this->attribute->getValue());
       }
@@ -820,7 +819,8 @@ class SubNodesAttribute extends OrderedArrayAttribute
       }
       $ldap->add($attrs);
       if (!$ldap->success()) {
-        msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, LDAP_ADD, get_class()), LDAP_ERROR);
+        $error = new SimplePluginLdapError($this, $dn, LDAP_ADD, $ldap->get_error(), $ldap->get_errno());
+        $error->display();
       }
     }
   }
diff --git a/include/simpleplugin/class_simpleTabs.inc b/include/simpleplugin/class_simpleTabs.inc
index 0a83ea57959c23cac2c9def7c5c38e7ca3a2a409..767442acaea701bfb7381d5971a08cb5b7e6ae85 100644
--- a/include/simpleplugin/class_simpleTabs.inc
+++ b/include/simpleplugin/class_simpleTabs.inc
@@ -65,10 +65,12 @@ class simpleTabs
     $this->dn           = $dn;
 
     if (!count($data)) {
-      $data[] = ['CLASS' => 'plugin','NAME' => 'Error'];
-      msg_dialog::display(_('Error'),
-        sprintf(_('No plugin definitions found to initialize "%s", please check your configuration file.'), get_class($this)),
-        ERROR_DIALOG);
+      throw new FusionDirectoryException(
+        sprintf(
+          _('No plugin definitions found to initialize "%s", please check your configuration file.'),
+          get_class($this)
+        )
+      );
     }
 
     $baseobject         = NULL;
@@ -321,7 +323,8 @@ class simpleTabs
   function delete ()
   {
     if (!$this->getBaseObject()->acl_is_removeable()) {
-      msg_dialog::display(_('Permission'), msgPool::permDelete($this->getBaseObject()->dn), ERROR_DIALOG);
+      $error = new SimplePluginPermissionError($this, msgPool::permDelete($this->getBaseObject()->dn));
+      $error->display();
       return FALSE;
     }
 
diff --git a/plugins/addons/dashboard/class_dashboard.inc b/plugins/addons/dashboard/class_dashboard.inc
index 39b533a2226ddea09e4a9de80a44a99068eb441b..13cac88f2f4002538b930eaf06c547eff5efd58a 100644
--- a/plugins/addons/dashboard/class_dashboard.inc
+++ b/plugins/addons/dashboard/class_dashboard.inc
@@ -74,11 +74,16 @@ class dashboard extends simplePlugin
         $nb = 0;
       } catch (FusionDirectoryException $e) {
         $nb = 0;
-        msg_dialog::display(
-          _('LDAP error'),
-          sprintf(_('Statistics for type "%s" could not be computed because of the following error: %s'), $type, $e->getMessage()),
-          ERROR_DIALOG
+        $error = new FusionDirectoryError(
+          htmlescape(sprintf(
+            _('Statistics for type "%s" could not be computed because of the following error: %s'),
+            $type,
+            $e->getMessage()
+          )),
+          0,
+          $e
         );
+        $error->display();
       }
       if ($nb == 0) {
         continue;
diff --git a/plugins/addons/dashboard/class_dashboardPasswords.inc b/plugins/addons/dashboard/class_dashboardPasswords.inc
index 88115738d93584983ffb591fdf13d632690fb437..888bfdf8288cdff1f0989affa756777cbf9049db 100644
--- a/plugins/addons/dashboard/class_dashboardPasswords.inc
+++ b/plugins/addons/dashboard/class_dashboardPasswords.inc
@@ -62,11 +62,15 @@ class dashboardPassword extends simplePlugin
     try {
       $users = objects::ls('user', ['userPassword' => '1', 'dn' => 'raw'], NULL, '', TRUE);
     } catch (LDAPFailureException $e) {
-      msg_dialog::display(
-        _('LDAP error'),
-        sprintf(_('Password statistics could not be computed because of the following LDAP error: %s'), $e->getMessage()),
-        ERROR_DIALOG
+      $error = new FusionDirectoryError(
+        htmlescape(sprintf(
+          _('Password statistics could not be computed because of the following LDAP error: %s'),
+          $e->getMessage()
+        )),
+        0,
+        $e
       );
+      $error->display();
       $users = [];
     }
     $nb_accounts = count($users);
diff --git a/plugins/addons/dashboard/class_dashboardUsers.inc b/plugins/addons/dashboard/class_dashboardUsers.inc
index 84b45686bd2200c48dd04ec049857b8e96aadb37..b7a3856c774e661a89972f1f7bc37cb8faf6436c 100644
--- a/plugins/addons/dashboard/class_dashboardUsers.inc
+++ b/plugins/addons/dashboard/class_dashboardUsers.inc
@@ -86,11 +86,15 @@ class dashboardUsers extends simplePlugin
       $nb_posix_accounts  = count(objects::ls('user', NULL, NULL, '(objectClass=posixAccount)',     TRUE));
       $nb_samba_accounts  = count(objects::ls('user', NULL, NULL, '(objectClass=sambaSamAccount)',  TRUE));
     } catch (LDAPFailureException $e) {
-      msg_dialog::display(
-        _('LDAP error'),
-        sprintf(_('User statistics could not be computed because of the following LDAP error: %s'), $e->getMessage()),
-        ERROR_DIALOG
+      $error = new FusionDirectoryError(
+        htmlescape(sprintf(
+          _('User statistics could not be computed because of the following LDAP error: %s'),
+          $e->getMessage()
+        )),
+        0,
+        $e
       );
+      $error->display();
       $nb_accounts        = 0;
       $nb_mail_accounts   = 0;
       $nb_posix_accounts  = 0;
@@ -117,11 +121,15 @@ class dashboardUsers extends simplePlugin
       $nb_mail_groups   = count(objects::ls('group', NULL, NULL, '(objectClass=fdGroupMail)',     TRUE));
       $nb_samba_groups  = count(objects::ls('group', NULL, NULL, '(objectClass=sambaGroupMapping)', TRUE));
     } catch (LDAPFailureException $e) {
-      msg_dialog::display(
-        _('LDAP error'),
-        sprintf(_('Group statistics could not be computed because of the following LDAP error: %s'), $e->getMessage()),
-        ERROR_DIALOG
+      $error = new FusionDirectoryError(
+        htmlescape(sprintf(
+          _('Group statistics could not be computed because of the following LDAP error: %s'),
+          $e->getMessage()
+        )),
+        0,
+        $e
       );
+      $error->display();
       $nb_groups        = 0;
       $nb_mail_groups   = 0;
       $nb_samba_groups  = 0;
@@ -181,18 +189,26 @@ class dashboardUsers extends simplePlugin
       }
       $users = objects::ls('user', $attributes, NULL, '(shadowExpire=*)', TRUE);
     } catch (LDAPFailureException $e) {
-      msg_dialog::display(
-        _('LDAP error'),
-        sprintf(_('Expired user information could not be computed because of the following LDAP error: %s'), $e->getMessage()),
-        ERROR_DIALOG
+      $error = new FusionDirectoryError(
+        htmlescape(sprintf(
+          _('Expired user information could not be computed because of the following LDAP error: %s'),
+          $e->getMessage()
+        )),
+        0,
+        $e
       );
+      $error->display();
       $users = [];
     } catch (FusionDirectoryException $e) {
-      msg_dialog::display(
-        _('Error'),
-        sprintf(_('Expired user information could not be computed because of the following error: %s'), $e->getMessage()),
-        ERROR_DIALOG
+      $error = new FusionDirectoryError(
+        htmlescape(sprintf(
+          _('Expired user information could not be computed because of the following error: %s'),
+          $e->getMessage()
+        )),
+        0,
+        $e
       );
+      $error->display();
       $users = [];
     }
 
diff --git a/plugins/admin/acl/class_aclManagement.inc b/plugins/admin/acl/class_aclManagement.inc
index 783d8250137d879cb3d88a397e4b697cec8ed7e5..1c9a6dfadd0cfabbab7436f856f7c649d01127c4 100644
--- a/plugins/admin/acl/class_aclManagement.inc
+++ b/plugins/admin/acl/class_aclManagement.inc
@@ -107,7 +107,8 @@ class aclAssignmentCreationDialog extends simplePlugin
     try {
       $this->management->newEntryConfirmed($this->baseDn);
     } catch (NonExistingLdapNodeException $e) {
-      msg_dialog::displayChecks([_('The dn you entered could not be found in the LDAP')]);
+      $error = new FusionDirectoryError(htmlescape(_('The dn you entered could not be found in the LDAP')), 0, $e);
+      $error->display();
       return TRUE;
     }
     return FALSE;
diff --git a/plugins/admin/groups/class_groupManagement.inc b/plugins/admin/groups/class_groupManagement.inc
index 34a9c8b37962fa99e4590b9ef9a445cd1035454a..e1ce7c4ecd33d69657705a04699602d6a3afda63 100644
--- a/plugins/admin/groups/class_groupManagement.inc
+++ b/plugins/admin/groups/class_groupManagement.inc
@@ -131,10 +131,22 @@ class groupManagement extends management
             if (isset($attrs['macAddress'][0])) {
               $mac[] = $attrs['macAddress'][0];
             } else {
-              msg_dialog::display(_('Action canceled'), sprintf(_('System %s has no mac address defined, cannot trigger action'), $member), ERROR_DIALOG);
+              $error = new FusionDirectoryError(
+                htmlescape(sprintf(
+                  _('System %s has no mac address defined, cannot trigger action'),
+                  $member
+                ))
+              );
+              $error->display();
             }
           } else {
-            msg_dialog::display(_('Action canceled'), sprintf(_('Could not find system %s, cannot trigger action'), $member), ERROR_DIALOG);
+            $error = new FusionDirectoryError(
+              htmlescape(sprintf(
+                _('Could not find system %s, cannot trigger action'),
+                $member
+              ))
+            );
+            $error->display();
           }
         }
       }
@@ -152,7 +164,13 @@ class groupManagement extends management
     if ($triggered && in_array($event, ['reinstall','update'])) {
       foreach ($mac as $key => $mac_address) {
         if ($o_queue->is_currently_installing($mac_address)) {
-          msg_dialog::display(_('Action canceled'), sprintf(_('System %s is currently installing'), $dn), ERROR_DIALOG);
+          $error = new FusionDirectoryError(
+            htmlescape(sprintf(
+              _('System %s is currently installing, cannot trigger action'),
+              $dn
+            ))
+          );
+          $error->display();
           unset($mac[$key]);
         }
       }
@@ -170,7 +188,8 @@ class groupManagement extends management
       if ($triggered) {
         $res = $o_queue->append($this->dialogObject);
         if ($o_queue->is_error()) {
-          msg_dialog::display(_('Infrastructure service'), msgPool::siError($o_queue->get_error()), ERROR_DIALOG);
+          $error = new FusionDirectoryError(msgPool::siError($o_queue->get_error()));
+          $error->display();
         } else {
           if (is_array($res) && count($res) > 1) {
             msg_dialog::display(_('Action triggered'), sprintf(_('Action called without error (results were "%s")'), implode(', ', $res)), INFO_DIALOG);
@@ -201,7 +220,8 @@ class groupManagement extends management
       $o_queue = new supportDaemon();
       $o_queue->append($this->dialogObject);
       if ($o_queue->is_error()) {
-        msg_dialog::display(_('Infrastructure service'), msgPool::siError($o_queue->get_error()), ERROR_DIALOG);
+        $error = new FusionDirectoryError(msgPool::siError($o_queue->get_error()));
+        $error->display();
       }
       $this->closeDialogs();
     }
diff --git a/plugins/admin/groups/class_ogroup.inc b/plugins/admin/groups/class_ogroup.inc
index 7aa7ec6203543f9994b5842d99639db8fbc6cd8a..78acd1f6742d17b280861d002d4c9bbf734fa469 100644
--- a/plugins/admin/groups/class_ogroup.inc
+++ b/plugins/admin/groups/class_ogroup.inc
@@ -277,12 +277,12 @@ class ogroup extends simplePlugin
       $removingMembers  = array_diff($savedMembers, $userMembers);
       foreach ($addingMembers as $dn) {
         if (strpos($ui->get_permissions($dn, 'user/userRoles', 'groupsMembership', $this->acl_skip_write()), 'w') === FALSE) {
-          $errors[] = msgPool::permModify($dn, 'groupsMembership');
+          $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($dn, 'groupsMembership'));
         }
       }
       foreach ($removingMembers as $dn) {
         if (strpos($ui->get_permissions($dn, 'user/userRoles', 'groupsMembership', $this->acl_skip_write()), 'w') === FALSE) {
-          $errors[] = msgPool::permModify($dn, 'groupsMembership');
+          $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($dn, 'groupsMembership'));
         }
       }
     }
diff --git a/plugins/admin/groups/class_roleGeneric.inc b/plugins/admin/groups/class_roleGeneric.inc
index c2cec1e254cde8b38bf2df03fd90d42654871758..399d0d6968f9b0516e7c918b79634a4b473ae69f 100644
--- a/plugins/admin/groups/class_roleGeneric.inc
+++ b/plugins/admin/groups/class_roleGeneric.inc
@@ -125,12 +125,12 @@ class roleGeneric extends simplePlugin
       $removingOccupants  = array_diff($savedOccupants, $this->roleOccupant);
       foreach ($addingOccupants as $dn) {
         if (strpos($ui->get_permissions($dn, 'user/userRoles', 'rolesMembership', $this->acl_skip_write()), 'w') === FALSE) {
-          $errors[] = msgPool::permModify($dn, 'rolesMembership');
+          $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($dn, 'rolesMembership'));
         }
       }
       foreach ($removingOccupants as $dn) {
         if (strpos($ui->get_permissions($dn, 'user/userRoles', 'rolesMembership', $this->acl_skip_write()), 'w') === FALSE) {
-          $errors[] = msgPool::permModify($dn, 'rolesMembership');
+          $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($dn, 'rolesMembership'));
         }
       }
     }
diff --git a/plugins/admin/users/class_userManagement.inc b/plugins/admin/users/class_userManagement.inc
index 52ac840770b28cc8cb79617cd825ed7704767c4a..041dc47aeaa671dbfce86873249dfd2477ddd17c 100644
--- a/plugins/admin/users/class_userManagement.inc
+++ b/plugins/admin/users/class_userManagement.inc
@@ -82,7 +82,8 @@ class userManagement extends management
       }
     }
     if (count($disallowed)) {
-      msg_dialog::display(_('Permission'), msgPool::permModify($disallowed), INFO_DIALOG);
+      $error = new FusionDirectoryPermissionError(msgPool::permModify($disallowed));
+      $error->display();
     }
 
     // Try to lock/unlock the rest of the entries.
@@ -110,9 +111,14 @@ class userManagement extends management
         if (is_array($hn)) {
           $hn = $hn[0];
         }
-        msg_dialog::display(_('Account locking'),
-            sprintf(_('Password method "%s" does not support locking. Account "%s" has not been locked!'),
-              $hn, $dn), ERROR_DIALOG);
+        $error = new FusionDirectoryError(
+          htmlescape(sprintf(
+            _('Password method "%s" does not support locking. Account "%s" has not been locked!'),
+            $hn,
+            $dn
+          ))
+        );
+        $error->display();
         return;
       }
 
@@ -129,9 +135,14 @@ class userManagement extends management
         if (is_array($hn)) {
           $hn = $hn[0];
         }
-        msg_dialog::display(_('Account locking'),
-            sprintf(_('Locking failed using password method "%s". Account "%s" has not been locked!'),
-              $hn, $dn), ERROR_DIALOG);
+        $error = new FusionDirectoryError(
+          htmlescape(sprintf(
+            _('Locking failed using password method "%s". Account "%s" has not been locked!'),
+            $hn,
+            $dn
+          ))
+        );
+        $error->display();
       }
     }
   }
diff --git a/plugins/personal/roles/class_userRoles.inc b/plugins/personal/roles/class_userRoles.inc
index 80b7f5ceab93ccbe9c1a6ed62cc473509f95d887..700ab6b7a6cb1410c6707ed24f576160ea4c708f 100644
--- a/plugins/personal/roles/class_userRoles.inc
+++ b/plugins/personal/roles/class_userRoles.inc
@@ -202,11 +202,16 @@ class userRoles extends simplePlugin
           } else {
             /* We do not prevent user deletion on error, but still warn the user */
             foreach ($msg as $error) {
-              msg_dialog::display(
-                _('Warning'),
-                sprintf(_('Could not remove membership to group %s: %s'), $ogroupdn, $error),
-                WARNING_DIALOG
+              $warning = new FusionDirectoryWarning(
+                htmlescape(sprintf(
+                  _('Could not remove membership to group %s: %s'),
+                  $ogroupdn,
+                  $error
+                )),
+                0,
+                ($error instanceof Throwable ? $error : NULL)
               );
+              $warning->display();
             }
           }
         } catch (NonExistingLdapNodeException $e) {
@@ -224,11 +229,16 @@ class userRoles extends simplePlugin
           } else {
             /* We do not prevent user deletion on error, but still warn the user */
             foreach ($msg as $error) {
-              msg_dialog::display(
-                _('Warning'),
-                sprintf(_('Could not remove membership to role %s: %s'), $roledn, $error),
-                WARNING_DIALOG
+              $warning = new FusionDirectoryWarning(
+                htmlescape(sprintf(
+                  _('Could not remove membership to role %s: %s'),
+                  $roledn,
+                  $error
+                )),
+                0,
+                ($error instanceof Throwable ? $error : NULL)
               );
+              $warning->display();
             }
           }
         } catch (NonExistingLdapNodeException $e) {
@@ -302,7 +312,7 @@ class userRoles extends simplePlugin
           try {
             $g = objects::open($ogroupdn, 'ogroup');
             if (!in_array($ogroupdn, $this->templateGroups) && !$g->getBaseObject()->attrIsWriteable('member')) {
-              $errors[] = msgPool::permModify($ogroupdn, 'member');
+              $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($ogroupdn, 'member'));
               continue;
             }
             $g->getBaseObject()->attributesAccess['member']->addValue($this->dn, $fake_attrs);
@@ -311,11 +321,11 @@ class userRoles extends simplePlugin
               $this->savedGroupsMembership[] = $ogroupdn;
             } else {
               foreach ($msg as $error) {
-                $errors[] = sprintf(_('Could not add membership to group %s: %s'), $ogroupdn, $error);
+                $errors[] = new SimplePluginError($this, htmlescape(sprintf(_('Could not add membership to group %s: %s'), $ogroupdn, $error)));
               }
             }
           } catch (NonExistingLdapNodeException $e) {
-            $errors[] = $e->getMessage();
+            $errors[] = new SimplePluginError($this, htmlescape($e->getMessage()), 0, $e);
           }
         }
       }
@@ -326,7 +336,7 @@ class userRoles extends simplePlugin
           try {
             $g = objects::open($ogroupdn, 'ogroup');
             if (!$g->getBaseObject()->attrIsWriteable('member')) {
-              $errors[] = msgPool::permModify($ogroupdn, 'member');
+              $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($ogroupdn, 'member'));
               continue;
             }
             $g->getBaseObject()->attributesAccess['member']->searchAndRemove($this->dn);
@@ -335,11 +345,11 @@ class userRoles extends simplePlugin
               unset($this->savedGroupsMembership[$key]);
             } else {
               foreach ($msg as $error) {
-                $errors[] = sprintf(_('Could not remove membership to group %s: %s'), $ogroupdn, $error);
+                $errors[] = new SimplePluginError($this, htmlescape(sprintf(_('Could not remove membership to group %s: %s'), $ogroupdn, $error)));
               }
             }
           } catch (NonExistingLdapNodeException $e) {
-            $errors[] = $e->getMessage();
+            $errors[] = new SimplePluginError($this, htmlescape($e->getMessage()), 0, $e);
           }
         }
       }
@@ -351,7 +361,7 @@ class userRoles extends simplePlugin
           try {
             $r = objects::open($roledn, 'role');
             if (!in_array($roledn, $this->templateRoles) && !$r->getBaseObject()->attrIsWriteable('roleOccupant')) {
-              $errors[] = msgPool::permModify($roledn, 'roleOccupant');
+              $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($roledn, 'roleOccupant'));
               continue;
             }
             $r->getBaseObject()->attributesAccess['roleOccupant']->addValue($this->dn, $fake_attrs);
@@ -360,11 +370,11 @@ class userRoles extends simplePlugin
               $this->savedRolesMembership[] = $roledn;
             } else {
               foreach ($msg as $error) {
-                $errors[] = sprintf(_('Could not add membership to role %s: %s'), $roledn, $error);
+                $errors[] = new SimplePluginError($this, htmlescape(sprintf(_('Could not add membership to role %s: %s'), $roledn, $error)));
               }
             }
           } catch (NonExistingLdapNodeException $e) {
-            $errors[] = $e->getMessage();
+            $errors[] = new SimplePluginError($this, htmlescape($e->getMessage()), 0, $e);
           }
         }
       }
@@ -375,7 +385,7 @@ class userRoles extends simplePlugin
           try {
             $r = objects::open($roledn, 'role');
             if (!$r->getBaseObject()->attrIsWriteable('roleOccupant')) {
-              $errors[] = msgPool::permModify($roledn, 'roleOccupant');
+              $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($roledn, 'roleOccupant'));
               continue;
             }
             $r->getBaseObject()->attributesAccess['roleOccupant']->searchAndRemove($this->dn);
@@ -384,11 +394,11 @@ class userRoles extends simplePlugin
               unset($this->savedRolesMembership[$key]);
             } else {
               foreach ($msg as $error) {
-                $errors[] = sprintf(_('Could not remove membership to role %s: %s'), $roledn, $error);
+                $errors[] = new SimplePluginError($this, htmlescape(sprintf(_('Could not remove membership to role %s: %s'), $roledn, $error)));
               }
             }
           } catch (NonExistingLdapNodeException $e) {
-            $errors[] = $e->getMessage();
+            $errors[] = new SimplePluginError($this, htmlescape($e->getMessage()), 0, $e);
           }
         }
       }
diff --git a/setup/class_setupStepLdap.inc b/setup/class_setupStepLdap.inc
index 4e06a3c24453584625386c19afbe272e074f82c7..7f5d7fa7f09368024225ef9e4d6a73c4a568e381 100644
--- a/setup/class_setupStepLdap.inc
+++ b/setup/class_setupStepLdap.inc
@@ -209,7 +209,15 @@ class setupStepLdap extends setupStep
           if ($check['IS_MUST_HAVE']) {
             $errors[] = sprintf(_("%s\nSchema \"%s\": %s"), $check['MSG'], $check['SCHEMA_FILE'], $check['INFO']);
           } else {
-            msg_dialog::display(_('Warning'), sprintf(_("%s\nSchema \"%s\": %s"), $check['MSG'], $check['SCHEMA_FILE'], $check['INFO']), WARNING_DIALOG);
+            $warning = new FusionDirectoryWarning(
+              nl2br(htmlescape(sprintf(
+                _("%s\nSchema \"%s\": %s"),
+                $check['MSG'],
+                $check['SCHEMA_FILE'],
+                $check['INFO']
+              )))
+            );
+            $warning->display();
           }
         }
       }
diff --git a/setup/class_setupStepMigrate.inc b/setup/class_setupStepMigrate.inc
index 59f3c844dcdb81800e39a9cf4c10265f8f43b573..159a8a45a4160020a5861fa07bd555f8486cae06 100644
--- a/setup/class_setupStepMigrate.inc
+++ b/setup/class_setupStepMigrate.inc
@@ -463,7 +463,8 @@ class setupStepMigrate extends setupStep
       if (isset($this->rootOC_details['mods'])) {
         $res = $ldap->modify($this->rootOC_details['mods']);
         if (!$res) {
-          msg_dialog::display(_('LDAP error'), msgPool::ldaperror($ldap->get_error(), $config->current['BASE'], LDAP_MOD, get_class()), LDAP_ERROR);
+          $error = new FusionDirectoryLdapError($config->current['BASE'], LDAP_MOD, $ldap->get_error(), $ldap->get_errno());
+          $error->display();
         }
         $this->checks['adminAccount']->run();
         return $res;
@@ -716,14 +717,13 @@ class setupStepMigrate extends setupStep
           } else {
             $ldap->cd($attrs['dn']);
             if (!$ldap->modify($new_attrs)) {
-              msg_dialog::display(
-                _('Migration error'),
-                sprintf(
-                  _('Cannot migrate entry "%s":').'<br/><br/><i>%s</i>',
-                  $attrs['dn'], $ldap->get_error()
-                ),
-                ERROR_DIALOG
+              $error = new FusionDirectoryError(
+                nl2br(sprintf(
+                  htmlescape(_("Cannot migrate entry \"%s\":\n\n%s")),
+                  htmlescape($attrs['dn']), '<i>'.htmlescape($ldap->get_error()).'</i>'
+                ))
               );
+              $error->display();
               return FALSE;
             }
           }
@@ -981,14 +981,13 @@ class setupStepMigrate extends setupStep
         $ldap->cd($dn);
         $ldap->add($role);
         if (!$ldap->success()) {
-          msg_dialog::display(
-            _('Migration error'),
-            sprintf(
-              _('Cannot add ACL role "%s":').'<br/><br/><i>%s</i>',
-              $dn, $ldap->get_error()
-            ),
-            ERROR_DIALOG
+          $error = new FusionDirectoryError(
+            nl2br(sprintf(
+              htmlescape(_("Cannot add ACL role \"%s\":\n\n%s")),
+              htmlescape($dn), '<i>'.htmlescape($ldap->get_error()).'</i>'
+            ))
           );
+          $error->display();
           return FALSE;
         }
       }
@@ -1153,7 +1152,8 @@ class setupStepMigrate extends setupStep
     if (isset($_POST['destination'])) {
       $destination_dep = get_ou($ou).$_POST['destination'];
     } else {
-      msg_dialog::display(_('LDAP error'), _('Cannot move entries to the requested department!'), ERROR_DIALOG);
+      $error = new FusionDirectoryError(htmlescape(_('Cannot move entries to the requested department!')));
+      $error->display();
       return FALSE;
     }