diff --git a/include/class_msg_dialog.inc b/include/class_msg_dialog.inc
index 4da11d9e80d948d1b3a8c4b72ac512a5aee2ea3a..e6b0b6c287aca8420393a3ce9d3a77976fdddcda 100644
--- a/include/class_msg_dialog.inc
+++ b/include/class_msg_dialog.inc
@@ -133,7 +133,11 @@ class msg_dialog
   public static function displayChecks ($messages)
   {
     foreach ($messages as $error) {
-      static::display(_('Error'), $error, ERROR_DIALOG);
+      if ($error instanceof FusionDirectoryError) {
+        static::display(...$error->computeMsgDialogParameters());
+      } else {
+        static::display(_('Error'), $error, ERROR_DIALOG);
+      }
     }
   }
 
diff --git a/include/errors/class_FusionDirectoryError.inc b/include/errors/class_FusionDirectoryError.inc
new file mode 100644
index 0000000000000000000000000000000000000000..4fcd6a58645358fa8f11ec4288a4adb5c09b4d5c
--- /dev/null
+++ b/include/errors/class_FusionDirectoryError.inc
@@ -0,0 +1,33 @@
+<?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 FusionDirectoryError
+    \brief Parent class for all errors in FusionDirectory
+*/
+class FusionDirectoryError extends Error
+{
+  protected $htmlMessage;
+
+  public function __construct (string $htmlMessage = '', int $code = 0, Throwable $previous = NULL)
+  {
+    $this->htmlMessage = $htmlMessage;
+    parent::__construct(htmlunescape(strip_tags($htmlMessage)), $code, $previous);
+  }
+}
diff --git a/include/errors/class_SimplePluginCheckError.inc b/include/errors/class_SimplePluginCheckError.inc
new file mode 100644
index 0000000000000000000000000000000000000000..35899f1b11367b778fd2814a38fc81e8df24159d
--- /dev/null
+++ b/include/errors/class_SimplePluginCheckError.inc
@@ -0,0 +1,77 @@
+<?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 SimplePluginCheckError
+    \brief Error returned by check method of SimplePlugin
+*/
+class SimplePluginCheckError extends FusionDirectoryError
+{
+  protected $tab;
+  protected $attribute;
+
+  public function __construct (?object $origin, string $htmlMessage = '', int $code = 0, Throwable $previous = NULL)
+  {
+    if ($origin instanceof Attribute) {
+      $this->attribute  = $origin;
+      $this->tab        = $origin->getParent();
+    } else {
+      $this->attribute = NULL;
+      if ($origin instanceof SimpleTab) {
+        $this->tab = $origin;
+      } else {
+        $this->tab = NULL;
+      }
+    }
+
+    parent::__construct($htmlMessage, $code, $previous);
+  }
+
+  public function computeMsgDialogParameters (): array
+  {
+    $html = '';
+
+    if (isset($this->tab)) {
+      $html .= htmlescape($this->tab->parent->getBaseObject()->dn.' > ');
+      $html .= htmlescape($this->tab->parent->by_name[get_class($this->tab)].' > ');
+    }
+
+    if (isset($this->attribute)) {
+      $label = $this->attribute->getLabel();
+      if (empty($label)) {
+        $html .= '<i>'.htmlescape($this->attribute->getLdapName()).'</i>';
+      } else {
+        $html .= htmlescape($label);
+      }
+      $example = $this->attribute->getExample();
+    }
+
+    $html .= '<br/><br/>';
+
+    $html .= $this->htmlMessage;
+
+    /* Stylize example */
+    if (!empty($example)) {
+      $html .= '<br/><br/><i>'.sprintf(_('Example: %s'), htmlescape($example)).'</i> ';
+    }
+
+
+    return [_('Error'), $html, ERROR_DIALOG];
+  }
+}
diff --git a/include/functions.inc b/include/functions.inc
index 4a97774bf39783c67b11484cc269bed10903c587..a756a6ff828970afa5aa78cffc1d3fa6a6c9331e 100644
--- a/include/functions.inc
+++ b/include/functions.inc
@@ -1722,19 +1722,21 @@ function send_binary_content ($data, $name, $type = "application/octet-stream")
   exit();
 }
 
-
-function reverse_html_entities ($str, $type = ENT_QUOTES, $charset = "UTF-8")
+/*!
+ * \brief Escape string for HTML output
+ */
+function htmlescape (string $str): string
 {
-  if (is_string($str)) {
-    return htmlentities($str, $type, $charset);
-  } elseif (is_array($str)) {
-    foreach ($str as $name => $value) {
-      $str[$name] = reverse_html_entities($value, $type, $charset);
-    }
-  }
-  return $str;
+  return htmlentities($str, ENT_COMPAT | ENT_HTML5, 'UTF-8');
 }
 
+/*!
+ * \brief Unescape string for HTML output, reverse of htmlescape
+ */
+function htmlunescape (string $html): string
+{
+  return html_entity_decode($html, ENT_COMPAT | ENT_HTML5, 'UTF-8');
+}
 
 /*!
  * \brief Encode special string characters
diff --git a/include/simpleplugin/attributes/class_IntAttribute.inc b/include/simpleplugin/attributes/class_IntAttribute.inc
index 496c7cd105e27234251125d7bebb58b6975c9cd5..bcbfc7008d0223d3640ea347ad8805f5d365b59a 100644
--- a/include/simpleplugin/attributes/class_IntAttribute.inc
+++ b/include/simpleplugin/attributes/class_IntAttribute.inc
@@ -26,7 +26,6 @@ class IntAttribute extends Attribute
   protected $min;
   protected $max;
   protected $step = 1;
-  protected $example;
 
   /*! \brief The constructor of IntAttribute
    *
@@ -44,14 +43,16 @@ class IntAttribute extends Attribute
     parent::__construct($label, $description, $ldapName, $required, $defaultValue, $acl);
     $this->min      = ($min === FALSE ? FALSE : $this->inputValue($min));
     $this->max      = ($max === FALSE ? FALSE : $this->inputValue($max));
-    $this->example  = '';
-
-    if (($min !== FALSE) && ($max !== FALSE)) {
-      $this->example = sprintf(_('An integer between %d and %d'), $min, $max);
-    } elseif ($min !== FALSE) {
-      $this->example = sprintf(_('An integer larger than %d'),    $min);
-    } elseif ($max !== FALSE) {
-      $this->example = sprintf(_('An integer smaller than %d'),   $max);
+  }
+
+  public function getExample (): ?string
+  {
+    if (($this->min !== FALSE) && ($this->max !== FALSE)) {
+      return sprintf(_('An integer between %d and %d'), $this->min, $this->max);
+    } elseif ($this->min !== FALSE) {
+      return sprintf(_('An integer larger than %d'),    $this->min);
+    } elseif ($this->max !== FALSE) {
+      return sprintf(_('An integer smaller than %d'),   $this->max);
     }
   }
 
@@ -80,11 +81,13 @@ class IntAttribute extends Attribute
       return $error;
     } elseif ($this->value !== '') {
       if (!is_numeric($this->value)) {
-        return msgPool::invalid($this->getLabel(), $this->value, $this->example);
+        return new SimplePluginCheckError($this, sprintf(_('"%s" is not an number'), $this->getValue()));
       }
-      if ((($this->min !== FALSE) && ($this->value < $this->min))
-      || (($this->max !== FALSE) && ($this->value > $this->max))) {
-        return msgPool::invalid($this->getLabel(), $this->value, $this->example);
+      if (($this->min !== FALSE) && ($this->value < $this->min)) {
+        return new SimplePluginCheckError($this, sprintf(_('%s is smaller than %s'), $this->getValue(), $this->min));
+      }
+      if (($this->max !== FALSE) && ($this->value > $this->max)) {
+        return new SimplePluginCheckError($this, sprintf(_('%s is larger than %s'), $this->getValue(), $this->max));
       }
     }
   }
@@ -156,14 +159,16 @@ class FloatAttribute extends IntAttribute
     parent::__construct($label, $description, $ldapName, $required, $min, $max, $defaultValue, $acl);
 
     $this->step = 0.01;
+  }
 
-    $this->example  = '';
-    if (($min !== FALSE) && ($max !== FALSE)) {
-      $this->example = sprintf(_('A float between %f and %f'), $min, $max);
-    } elseif ($min !== FALSE) {
-      $this->example = sprintf(_('A float larger than %f'),    $min);
-    } elseif ($max !== FALSE) {
-      $this->example = sprintf(_('A float smaller than %f'),   $max);
+  public function getExample (): ?string
+  {
+    if (($this->min !== FALSE) && ($this->max !== FALSE)) {
+      return sprintf(_('A float between %f and %f'),  $this->min, $this->max);
+    } elseif ($this->min !== FALSE) {
+      return sprintf(_('A float larger than %f'),     $this->min);
+    } elseif ($this->max !== FALSE) {
+      return sprintf(_('A float smaller than %f'),    $this->max);
     }
   }
 
diff --git a/include/simpleplugin/attributes/class_StringAttribute.inc b/include/simpleplugin/attributes/class_StringAttribute.inc
index 635de25fd1883260e8825fc09d686cd58437027c..857710e300af8fe58a7576f96854e83f3b1da432 100644
--- a/include/simpleplugin/attributes/class_StringAttribute.inc
+++ b/include/simpleplugin/attributes/class_StringAttribute.inc
@@ -53,6 +53,11 @@ class StringAttribute extends Attribute
     $this->example = $example;
   }
 
+  public function getExample (): ?string
+  {
+    return $this->example;
+  }
+
   function setPattern ($pattern)
   {
     $this->pattern = $pattern;
diff --git a/include/simpleplugin/class_Attribute.inc b/include/simpleplugin/class_Attribute.inc
index 8c1e6878f4d740431b82c39b651992b453fc7a40..747f7beccfc9f8a2b363a0e3536cc8a1cc759977 100644
--- a/include/simpleplugin/class_Attribute.inc
+++ b/include/simpleplugin/class_Attribute.inc
@@ -121,6 +121,11 @@ class Attribute
     $this->manageAttributes($this->getValue());
   }
 
+  function getParent(): ?simplePlugin
+  {
+    return $this->plugin;
+  }
+
   function setIsSubAttribute (bool $bool)
   {
     $this->isSubAttribute = $bool;
@@ -167,6 +172,11 @@ class Attribute
     return $this->inLdap;
   }
 
+  public function getExample (): ?string
+  {
+    return NULL;
+  }
+
   function checkValue ($value)
   {
     /* Should throw InvalidValueException if needed */