diff --git a/contrib/openldap/core-fd.schema b/contrib/openldap/core-fd.schema
index 070a1ba9f090a47f4662ae17ae86fc0e49f5469b..f7c7ea36371d4704919b9bdb7e9cfd336907f9eb 100644
--- a/contrib/openldap/core-fd.schema
+++ b/contrib/openldap/core-fd.schema
@@ -2,7 +2,7 @@
 ## core-fd.schema - Needed by FusionDirectory for its basic functionalities
 ##
 
-# Last OID used for attributes  : 1.3.6.1.4.1.38414.62.1.67 05/02/24 #
+# Last OID used for attributes  : 1.3.6.1.4.1.38414.62.1.68 12/03/24 #
 # Last OID used for objectClass : 1.3.6.1.4.1.38414.62.2.11 29/01/24 #
 
 ##### Attributes from gosa ######
@@ -73,6 +73,12 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.51 NAME 'fdSnapshotDataSource'
   SUBSTR caseIgnoreSubstringsMatch
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
 
+attributetype ( 1.3.6.1.4.1.38414.62.1.68 NAME 'fdSnapshotHash'
+  DESC 'FusionDirectory - hash of the current snapShot allowing diff verification with MD5'
+  EQUALITY caseIgnoreMatch
+  SUBSTR caseIgnoreSubstringsMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+
 ##### Subscriptions Attributes ######
 
 attributetype ( 1.3.6.1.4.1.38414.62.11.1 NAME 'fdSubscriptionStartDate'
@@ -504,7 +510,7 @@ objectclass ( 1.3.6.1.4.1.10098.1.2.1.19.19 NAME 'gosaSnapshotObject'
   DESC 'GOsa - Container object for undo and snapshot data'
   SUP top STRUCTURAL
   MUST ( gosaSnapshotTimestamp $ gosaSnapshotDN $ gosaSnapshotData $ fdSnapshotDataSource )
-  MAY  ( fdSnapshotObjectType $ description ) )
+  MAY  ( fdSnapshotObjectType $ description $ fdSnapshotHash) )
 
 ### New FusionDirectory Objectclass ###
 
diff --git a/include/management/class_management.inc b/include/management/class_management.inc
index 27f806e8c115addc6ebab784c8b89f6e73d0e7e5..d613a0c2c65c3b9a34112baec3d8a4b2de645a6b 100644
--- a/include/management/class_management.inc
+++ b/include/management/class_management.inc
@@ -21,6 +21,7 @@
 /*!
  * \brief Management base class
  */
+
 class management implements FusionDirectoryDialog
 {
   /* Object types we are currently managing */
@@ -38,16 +39,16 @@ class management implements FusionDirectoryDialog
   protected $skipCpHandler    = FALSE;
 
   /* Snapshots */
-  protected $snapHandler        = NULL;
-  public static $skipSnapshots  = FALSE;
+  protected $snapHandler       = NULL;
+  public static $skipSnapshots = FALSE;
 
   // The currently used object(s) (e.g. in edit, removal)
-  protected $currentDn  = '';
+  protected $currentDn = '';
   protected $currentDns = [];
 
   // The last used object(s).
-  protected $previousDn   = '';
-  protected $previousDns  = [];
+  protected $previousDn = '';
+  protected $previousDns = [];
 
   // The opened object.
   /**
@@ -57,8 +58,8 @@ class management implements FusionDirectoryDialog
   protected $dialogObject = NULL;
 
   // The last opened object.
-  protected $last_tabObject     = NULL;
-  protected $last_dialogObject  = NULL;
+  protected $last_tabObject    = NULL;
+  protected $last_dialogObject = NULL;
 
   protected $renderCache;
 
@@ -66,7 +67,7 @@ class management implements FusionDirectoryDialog
   public $title;
   public $icon;
 
-  protected $actions        = [];
+  protected $actions = [];
   protected $actionHandlers = [];
 
   protected $exporters = [];
@@ -83,23 +84,23 @@ class management implements FusionDirectoryDialog
   /* Default columns */
   public static $columns = [
     ['ObjectTypeColumn', []],
-    ['LinkColumn',       ['attributes' => 'nameAttr',    'label' => 'Name']],
-    ['LinkColumn',       ['attributes' => 'description', 'label' => 'Description']],
-    ['ActionsColumn',    ['label' => 'Actions']],
+    ['LinkColumn', ['attributes' => 'nameAttr', 'label' => 'Name']],
+    ['LinkColumn', ['attributes' => 'description', 'label' => 'Description']],
+    ['ActionsColumn', ['label' => 'Actions']],
   ];
 
   function __construct (
     $objectTypes = FALSE,
     array $filterElementDefinitions = [
-      ['TabFilterElement',  []],
+      ['TabFilterElement', []],
     ]
   )
   {
     global $config, $class_mapping;
 
     if ($objectTypes === FALSE) {
-      $plInfos = pluglist::pluginInfos(get_class($this));
-      $objectTypes  = $plInfos['plManages'];
+      $plInfos     = pluglist::pluginInfos(get_class($this));
+      $objectTypes = $plInfos['plManages'];
     }
 
     if (!preg_match('/^geticon/', $this->icon)) {
@@ -145,12 +146,12 @@ class management implements FusionDirectoryDialog
 
   protected function setUpListing ()
   {
-    $this->listing  = new managementListing($this);
+    $this->listing = new managementListing($this);
   }
 
   protected function setUpFilter (array $filterElementDefinitions)
   {
-    $this->filter   = new managementFilter($this, NULL, $filterElementDefinitions);
+    $this->filter = new managementFilter($this, NULL, $filterElementDefinitions);
   }
 
   protected function setUpHeadline ()
@@ -175,26 +176,26 @@ class management implements FusionDirectoryDialog
     }
 
     foreach ($this->objectTypes as $type) {
-      $infos  = objects::infos($type);
-      $img    = 'geticon.php?context=actions&icon=document-new&size=16';
+      $infos = objects::infos($type);
+      $img   = 'geticon.php?context=actions&icon=document-new&size=16';
       if (isset($infos['icon'])) {
         $img = $infos['icon'];
       }
       $createMenu[] = new Action(
-        'new_'.$type, $infos['name'], $img,
+        'new_' . $type, $infos['name'], $img,
         '0', 'newEntry',
-        [$infos['aclCategory'].'/'.$infos['mainTab'].'/c']
+        [$infos['aclCategory'] . '/' . $infos['mainTab'] . '/c']
       );
       if (!static::$skipTemplates) {
-        $templateMenu[] = new Action(
-          'new_template_'.$type, $infos['name'], $img,
+        $templateMenu[]     = new Action(
+          'new_template_' . $type, $infos['name'], $img,
           '0', 'newEntryTemplate',
-          [$infos['aclCategory'].'/template/c']
+          [$infos['aclCategory'] . '/template/c']
         );
         $fromTemplateMenu[] = new Action(
-          'template_apply_'.$type, $infos['name'], $img,
+          'template_apply_' . $type, $infos['name'], $img,
           '0', 'newEntryFromTemplate',
-          [$infos['aclCategory'].'/template/r', $infos['aclCategory'].'/'.$infos['mainTab'].'/c']
+          [$infos['aclCategory'] . '/template/r', $infos['aclCategory'] . '/' . $infos['mainTab'] . '/c']
         );
       }
     }
@@ -287,8 +288,8 @@ class management implements FusionDirectoryDialog
       $action = archivedObject::getManagementAction($this->objectTypes, 'archiveRequested');
       if ($action !== NULL) {
         $this->registerAction($action);
-        $this->registerAction(new HiddenAction('archiveConfirmed',  'archiveConfirmed'));
-        $this->registerAction(new HiddenAction('archiveCancel',     'cancelEdit'));
+        $this->registerAction(new HiddenAction('archiveConfirmed', 'archiveConfirmed'));
+        $this->registerAction(new HiddenAction('archiveCancel', 'cancelEdit'));
       }
     }
 
@@ -333,13 +334,13 @@ class management implements FusionDirectoryDialog
     }
 
     /* Actions from footer are not in any menus and do not need a label */
-    $this->registerAction(new HiddenAction('apply',           'applyChanges'));
-    $this->registerAction(new HiddenAction('save',            'saveChanges'));
-    $this->registerAction(new HiddenAction('cancel',          'cancelEdit'));
-    $this->registerAction(new HiddenAction('cancelDelete',    'cancelEdit'));
+    $this->registerAction(new HiddenAction('apply', 'applyChanges'));
+    $this->registerAction(new HiddenAction('save', 'saveChanges'));
+    $this->registerAction(new HiddenAction('cancel', 'cancelEdit'));
+    $this->registerAction(new HiddenAction('cancelDelete', 'cancelEdit'));
     $this->registerAction(new HiddenAction('removeConfirmed', 'removeConfirmed'));
     if (!$this->skipConfiguration) {
-      $this->registerAction(new HiddenAction('configure',       'configureDialog'));
+      $this->registerAction(new HiddenAction('configure', 'configureDialog'));
     }
   }
 
@@ -420,8 +421,8 @@ class management implements FusionDirectoryDialog
   function handleAction (array $action)
   {
     // Start action
-    if (isset($action['subaction']) && isset($this->actionHandlers[$action['action'].'_'.$action['subaction']])) {
-      return $this->actionHandlers[$action['action'].'_'.$action['subaction']]->execute($this, $action);
+    if (isset($action['subaction']) && isset($this->actionHandlers[$action['action'] . '_' . $action['subaction']])) {
+      return $this->actionHandlers[$action['action'] . '_' . $action['subaction']]->execute($this, $action);
     } elseif (isset($this->actionHandlers[$action['action']])) {
       return $this->actionHandlers[$action['action']]->execute($this, $action);
     }
@@ -434,7 +435,7 @@ class management implements FusionDirectoryDialog
       if (isset($this->tabObject->by_object[$tab])) {
         $this->tabObject->current = $tab;
       } else {
-        trigger_error('Unknown tab: '.$tab);
+        trigger_error('Unknown tab: ' . $tab);
       }
       return TRUE;
     }
@@ -466,16 +467,16 @@ class management implements FusionDirectoryDialog
   public function render (): string
   {
     if ($this->renderCache === NULL) {
-      if ($this->tabObject instanceOf simpleTabs) {
+      if ($this->tabObject instanceof simpleTabs) {
         /* Display tab object */
-        $display = $this->tabObject->render();
-        $display .= $this->getTabFooter();
-        $this->renderCache = $this->getHeader().$display;
+        $display           = $this->tabObject->render();
+        $display           .= $this->getTabFooter();
+        $this->renderCache = $this->getHeader() . $display;
       } elseif (is_object($this->dialogObject)) {
         /* Display dialog object */
-        $display = $this->dialogObject->render();
-        $display .= $this->getTabFooter();
-        $this->renderCache = $this->getHeader().$display;
+        $display           = $this->dialogObject->render();
+        $display           .= $this->getTabFooter();
+        $this->renderCache = $this->getHeader() . $display;
       } else {
         /* Display list */
         $this->renderCache = $this->renderList();
@@ -491,12 +492,12 @@ class management implements FusionDirectoryDialog
   protected function execute ()
   {
     // Ensure that html posts and gets are kept even if we see a 'Entry islocked' dialog.
-    session::set('LOCK_VARS_TO_USE', ['/^act$/','/^listing/','/^PID$/']);
+    session::set('LOCK_VARS_TO_USE', ['/^act$/', '/^listing/', '/^PID$/']);
 
     /* Display the copy & paste dialog, if it is currently open */
     $ret = $this->copyPasteHandler();
     if ($ret) {
-      return $this->getHeader().$ret;
+      return $this->getHeader() . $ret;
     }
 
     // Handle actions (POSTs and GETs)
@@ -506,7 +507,7 @@ class management implements FusionDirectoryDialog
       try {
         $str = $this->handleAction($action);
         if (!empty($str)) {
-          return $this->getHeader().$str;
+          return $this->getHeader() . $str;
         }
       } catch (FusionDirectoryException $e) {
         $error = new FusionDirectoryError(htmlescape($e->getMessage()), 0, $e);
@@ -515,7 +516,7 @@ class management implements FusionDirectoryDialog
     }
 
     /* Save tab or dialog object */
-    if ($this->tabObject instanceOf simpleTabs) {
+    if ($this->tabObject instanceof simpleTabs) {
       $this->tabObject->readPost();
       $this->tabObject->update();
     } elseif (is_object($this->dialogObject)) {
@@ -548,15 +549,15 @@ class management implements FusionDirectoryDialog
 
     $smarty = get_smarty();
     $smarty->assign('usePrototype', 'true');
-    $smarty->assign('LIST',         $listRender);
-    $smarty->assign('FILTER',       $filterRender);
-    $smarty->assign('ACTIONS',      $actionMenu);
-    $smarty->assign('SIZELIMIT',    $ui->getSizeLimitHandler()->renderWarning());
-    $smarty->assign('NAVIGATION',   $this->listing->renderNavigation($this->skipConfiguration));
-    $smarty->assign('BASE',         $this->listing->renderBase());
-    $smarty->assign('HEADLINE',     $this->headline);
-
-    return $this->getHeader().$smarty->fetch(get_template_path('management/management.tpl'));
+    $smarty->assign('LIST', $listRender);
+    $smarty->assign('FILTER', $filterRender);
+    $smarty->assign('ACTIONS', $actionMenu);
+    $smarty->assign('SIZELIMIT', $ui->getSizeLimitHandler()->renderWarning());
+    $smarty->assign('NAVIGATION', $this->listing->renderNavigation($this->skipConfiguration));
+    $smarty->assign('BASE', $this->listing->renderBase());
+    $smarty->assign('HEADLINE', $this->headline);
+
+    return $this->getHeader() . $smarty->fetch(get_template_path('management/management.tpl'));
   }
 
   protected function renderFilter (): string
@@ -631,15 +632,15 @@ class management implements FusionDirectoryDialog
     $smarty->assign('headline_image', $this->icon);
 
     if (is_object($this->tabObject) && ($this->currentDn != '')) {
-      return '<div class="pluginfo">'.$this->currentDn."</div>\n";
+      return '<div class="pluginfo">' . $this->currentDn . "</div>\n";
     }
     return '';
   }
 
   function openTabObject ($object)
   {
-    $this->tabObject          = $object;
-    $this->tabObject->parent  = &$this;
+    $this->tabObject         = $object;
+    $this->tabObject->parent = &$this;
   }
 
   /*!
@@ -648,23 +649,23 @@ class management implements FusionDirectoryDialog
    */
   public function closeDialogs ()
   {
-    $this->previousDn   = $this->currentDn;
-    $this->currentDn    = '';
-    $this->previousDns  = $this->currentDns;
-    $this->currentDns   = [];
-
-    $this->last_tabObject     = $this->tabObject;
-    $this->tabObject          = NULL;
-    $this->last_dialogObject  = $this->dialogObject;
-    $this->dialogObject       = NULL;
+    $this->previousDn  = $this->currentDn;
+    $this->currentDn   = '';
+    $this->previousDns = $this->currentDns;
+    $this->currentDns  = [];
+
+    $this->last_tabObject    = $this->tabObject;
+    $this->tabObject         = NULL;
+    $this->last_dialogObject = $this->dialogObject;
+    $this->dialogObject      = NULL;
   }
 
   protected function listAclCategories (): array
   {
     $cat = [];
     foreach ($this->objectTypes as $type) {
-      $infos  = objects::infos($type);
-      $cat[]  = $infos['aclCategory'];
+      $infos = objects::infos($type);
+      $cat[] = $infos['aclCategory'];
     }
     return array_unique($cat);
   }
@@ -675,7 +676,7 @@ class management implements FusionDirectoryDialog
   protected function showTabFooter (): bool
   {
     // Do not display tab footer for non tab objects
-    if (!($this->tabObject instanceOf simpleTabs)) {
+    if (!($this->tabObject instanceof simpleTabs)) {
       return FALSE;
     }
 
@@ -822,7 +823,7 @@ class management implements FusionDirectoryDialog
     foreach ($this->currentDns as $dn) {
       $entry = $this->listing->getEntry($dn);
       if ($entry === NULL) {
-        trigger_error('Could not find '.$dn.', action canceled');
+        trigger_error('Could not find ' . $dn . ', action canceled');
         $this->currentDns = [];
         return;
       }
@@ -873,7 +874,7 @@ class management implements FusionDirectoryDialog
 
     $objects = [];
     foreach ($this->currentDns as $dn) {
-      $entry  = $this->listing->getEntry($dn);
+      $entry = $this->listing->getEntry($dn);
       if ($entry->isTemplate()) {
         $error = new FusionDirectoryError(htmlescape(_('Archiving a template is not possible')));
         $error->display();
@@ -881,12 +882,12 @@ class management implements FusionDirectoryDialog
         $this->currentDns = [];
         return;
       }
-      $infos  = objects::infos($entry->getTemplatedType());
+      $infos     = objects::infos($entry->getTemplatedType());
       $objects[] = [
-        'name'  => $entry[$infos['nameAttr']][0],
-        'dn'    => $dn,
-        'icon'  => $infos['icon'],
-        'type'  => $infos['name']
+        'name' => $entry[$infos['nameAttr']][0],
+        'dn'   => $dn,
+        'icon' => $infos['icon'],
+        'type' => $infos['name']
       ];
     }
 
@@ -944,7 +945,7 @@ class management implements FusionDirectoryDialog
 
     $entry = $this->listing->getEntry($target);
     if ($entry === NULL) {
-      trigger_error('Could not find '.$target.', open canceled');
+      trigger_error('Could not find ' . $target . ', open canceled');
       return;
     }
 
@@ -960,7 +961,7 @@ class management implements FusionDirectoryDialog
     logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Edit entry initiated');
     if (isset($action['subaction'])
       && ($this->handleSubAction($action) === FALSE)) {
-      trigger_error('Was not able to handle subaction: '.$action['subaction']);
+      trigger_error('Was not able to handle subaction: ' . $action['subaction']);
     }
   }
 
@@ -970,7 +971,7 @@ class management implements FusionDirectoryDialog
    */
   function cancelEdit ()
   {
-    if (($this->tabObject instanceOf simpleTabs) && ($this->dialogObject instanceOf templateDialog)) {
+    if (($this->tabObject instanceof simpleTabs) && ($this->dialogObject instanceof templateDialog)) {
       $this->handleTemplateApply(TRUE);
       return;
     }
@@ -985,10 +986,10 @@ class management implements FusionDirectoryDialog
    */
   function saveChanges ()
   {
-    if ($this->tabObject instanceOf simpleTabs) {
+    if ($this->tabObject instanceof simpleTabs) {
       $this->tabObject->readPost();
       $this->tabObject->update();
-      if ($this->dialogObject instanceOf templateDialog) {
+      if ($this->dialogObject instanceof templateDialog) {
         $this->handleTemplateApply();
       } else {
         $msgs = $this->tabObject->save();
@@ -1008,7 +1009,7 @@ class management implements FusionDirectoryDialog
    */
   function applyChanges ()
   {
-    if ($this->tabObject instanceOf simpleTabs) {
+    if ($this->tabObject instanceof simpleTabs) {
       $this->tabObject->readPost();
       $this->tabObject->update();
       $msgs = $this->tabObject->save();
@@ -1044,7 +1045,7 @@ class management implements FusionDirectoryDialog
           $disallowed[] = $dn;
         }
       } catch (NonExistingObjectTypeException $e) {
-        trigger_error('Unknown object type received :'.$e->getMessage());
+        trigger_error('Unknown object type received :' . $e->getMessage());
       }
     }
     if (count($disallowed)) {
@@ -1064,16 +1065,16 @@ class management implements FusionDirectoryDialog
 
       $objects = [];
       foreach ($this->currentDns as $dn) {
-        $entry  = $this->listing->getEntry($dn);
-        $infos  = objects::infos($entry->getTemplatedType());
+        $entry = $this->listing->getEntry($dn);
+        $infos = objects::infos($entry->getTemplatedType());
         if ($entry->isTemplate()) {
           $infos['nameAttr'] = 'cn';
         }
         $objects[] = [
-          'name'  => $entry[$infos['nameAttr']][0],
-          'dn'    => $dn,
-          'icon'  => $infos['icon'],
-          'type'  => $infos['name']
+          'name' => $entry[$infos['nameAttr']][0],
+          'dn'   => $dn,
+          'icon' => $infos['icon'],
+          'type' => $infos['name']
         ];
       }
 
@@ -1099,6 +1100,7 @@ class management implements FusionDirectoryDialog
     global $ui;
     logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDns, 'Entry deletion confirmed');
 
+    $snapshotHandler = new SnapshotHandler();
     foreach ($this->currentDns as $dn) {
       $entry = $this->listing->getEntry($dn);
       if (empty($entry)) {
@@ -1113,10 +1115,16 @@ class management implements FusionDirectoryDialog
 
         // Remove the lock for the current object.
         Lock::deleteByObject($this->currentDn);
+
+        // Remove related snapshots
+        $dnSnapshotsList = $snapshotHandler->getSnapshots($this->currentDn, TRUE);
+        foreach ($dnSnapshotsList as $snap) {
+          $snapshotHandler->removeSnapshot($snap['dn']);
+        }
       } else {
         $error = new FusionDirectoryPermissionError(msgPool::permDelete($dn));
         $error->display();
-        logging::log('security', 'management/'.get_class($this), $dn, [], 'Tried to trick deletion.');
+        logging::log('security', 'management/' . get_class($this), $dn, [], 'Tried to trick deletion.');
       }
     }
 
@@ -1222,8 +1230,8 @@ class management implements FusionDirectoryDialog
 
     if (empty($action['targets'])) {
       // No target, open the restore removed object dialog.
-      $this->currentDn  = $this->listing->getBase();
-      $aclCategories    = $this->listAclCategories();
+      $this->currentDn = $this->listing->getBase();
+      $aclCategories   = $this->listAclCategories();
     } else {
       // Display the restore points for a given object.
       $this->currentDn = $action['targets'][0];
@@ -1251,7 +1259,7 @@ class management implements FusionDirectoryDialog
   function export (array $action)
   {
     if (!isset($this->exporters[$action['action']])) {
-      trigger_error('Unknown exporter '.$action['action']);
+      trigger_error('Unknown exporter ' . $action['action']);
       return;
     }
     $exporter = $this->exporters[$action['action']];
@@ -1267,8 +1275,8 @@ class management implements FusionDirectoryDialog
   {
     $bases = [];
     foreach ($this->objectTypes as $type) {
-      $infos  = objects::infos($type);
-      $bases[] = $infos['ou'].$this->listing->getBase();
+      $infos   = objects::infos($type);
+      $bases[] = $infos['ou'] . $this->listing->getBase();
     }
 
     // No bases specified? Try base
@@ -1285,7 +1293,7 @@ class management implements FusionDirectoryDialog
   function getAllDeletedSnapshots (): array
   {
     $bases = $this->getSnapshotBases();
-    $tmp = [];
+    $tmp   = [];
     foreach ($bases as $base) {
       $tmp = array_merge($tmp, $this->snapHandler->getAllDeletedSnapshots($base));
     }
@@ -1352,7 +1360,7 @@ class management implements FusionDirectoryDialog
       $this->closeDialogs();
       if ($dn !== FALSE) {
         $this->listing->focusDn($dn);
-        $entry = $this->listing->getEntry($dn);
+        $entry           = $this->listing->getEntry($dn);
         $this->currentDn = $dn;
         Lock::add($this->currentDn);
 
diff --git a/include/management/snapshot/class_SnapshotHandler.inc b/include/management/snapshot/class_SnapshotHandler.inc
index 61744b61dd46db5920a9eff8eac9ff2915ce1ae0..c95078c5a82d78595771f81549b56f5897ab1082 100644
--- a/include/management/snapshot/class_SnapshotHandler.inc
+++ b/include/management/snapshot/class_SnapshotHandler.inc
@@ -254,6 +254,7 @@ class SnapshotHandler
     $target['description']          = $description;
     $target['fdSnapshotObjectType'] = $objectType;
     $target['fdSnapshotDataSource'] = $snapshotSource;
+    $target['fdSnapshotHash'] = md5($data);
 
     /* Insert the new snapshot
        But we have to check first, if the given gosaSnapshotTimestamp
@@ -380,7 +381,7 @@ class SnapshotHandler
     $ldap->cd($new_base);
     $ldap->search(
       '(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN='.ldap_escape_f($dn).'))',
-      ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType'],
+      ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType','fdSnapshotHash'],
       'one'
     );
 
@@ -416,7 +417,7 @@ class SnapshotHandler
     $ldap->cd($new_base);
     $ldap->search(
       '(objectClass=gosaSnapshotObject)',
-      ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType'],
+      ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType', 'fdSnapshotHash'],
       'one'
     );
     while ($entry = $ldap->fetch(TRUE)) {
diff --git a/plugins/personal/generic/class_user.inc b/plugins/personal/generic/class_user.inc
index 08f4f2053bf3fe8230c6e64064b54c325b369f20..e633565c3a69fe680dd66b8692144c1647cd4665 100644
--- a/plugins/personal/generic/class_user.inc
+++ b/plugins/personal/generic/class_user.inc
@@ -23,8 +23,8 @@ class PostalAddressAttribute extends TextAreaAttribute
   function inputValue ($ldapValue)
   {
     return str_replace(
-      ['$',  '\24','\5C'],
-      ["\n", '$',  '\\'],
+      ['$', '\24', '\5C'],
+      ["\n", '$', '\\'],
       $ldapValue
     );
   }
@@ -36,7 +36,7 @@ class PostalAddressAttribute extends TextAreaAttribute
       '$',
       str_replace(
         ['\\', '$'],
-        ['\5C','\24'],
+        ['\5C', '\24'],
         $this->getValue()
       )
     );
@@ -55,7 +55,7 @@ class user extends simplePlugin
       'plIcon'        => 'geticon.php?context=applications&icon=user-info&size=48',
       'plSmallIcon'   => 'geticon.php?context=applications&icon=user-info&size=16',
       'plSelfModify'  => TRUE,
-      'plObjectClass' => ['inetOrgPerson','organizationalPerson','person'],
+      'plObjectClass' => ['inetOrgPerson', 'organizationalPerson', 'person'],
       'plFilter'      => '(objectClass=inetOrgPerson)',
       'plObjectType'  => ['user' => [
         'name'        => _('User'),
@@ -65,10 +65,10 @@ class user extends simplePlugin
         'icon'        => 'geticon.php?context=types&icon=user&size=16',
         'ou'          => get_ou('userRDN'),
       ]],
-      'plForeignKeys'  => [
-        'manager' => ['user','dn','manager=%oldvalue%','*']
+      'plForeignKeys' => [
+        'manager' => ['user', 'dn', 'manager=%oldvalue%', '*']
       ],
-      'plSearchAttrs' => ['uid','description'],
+      'plSearchAttrs' => ['uid', 'description'],
 
       'plProvidedAcls' => array_merge(
         parent::generatePlProvidedAcls(static::getAttributesInfo()),
@@ -82,9 +82,9 @@ class user extends simplePlugin
     global $config;
     $languages = Language::getList(TRUE);
     asort($languages);
-    $languages = array_merge(['' => ''], $languages);
+    $languages      = array_merge(['' => ''], $languages);
     $attributesInfo = [
-      'perso' => [
+      'perso'        => [
         'name'  => _('Personal information'),
         'icon'  => 'geticon.php?context=types&icon=user&size=16',
         'attrs' => [
@@ -115,7 +115,7 @@ class user extends simplePlugin
           ),
         ]
       ],
-      'contact' => [
+      'contact'      => [
         'name'  => _('Organizational contact information'),
         'icon'  => 'geticon.php?context=types&icon=contact&size=16',
         'attrs' => [
@@ -161,7 +161,7 @@ class user extends simplePlugin
           ),
         ]
       ],
-      'account' => [
+      'account'      => [
         'name'  => _('Account information'),
         'icon'  => 'geticon.php?context=applications&icon=ldap&size=16',
         'attrs' => [
@@ -181,7 +181,7 @@ class user extends simplePlugin
           ),
         ]
       ],
-      'homecontact' => [
+      'homecontact'  => [
         'name'  => _('Personal contact information'),
         'icon'  => 'geticon.php?context=types&icon=contact&size=16',
         'attrs' => [
@@ -259,6 +259,7 @@ class user extends simplePlugin
 
   function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE)
   {
+    global $config;
     parent::__construct($dn, $object, $parent, $mainTab);
 
     if ($this->is_template && !$this->initially_was_account) {
@@ -272,8 +273,17 @@ class user extends simplePlugin
     $filename = './plugins/users/images/default.jpg';
     $fd       = fopen($filename, 'rb');
     $this->attributesAccess['jpegPhoto']->setPlaceholder(fread($fd, filesize($filename)));
-
     $this->was_locked = $this->attributesAccess['userPassword']->isLocked();
+
+    // Do not apply automatic snap on templates nor if the DN is not yet processed (new creation from template)
+    if ($this->is_template !== TRUE && $this->dn !== 'new') {
+      // Verification is snapshot is enabled and automatic.
+      if (isset($config->current['ENABLEAUTOMATICSNAPSHOTS']) && isset($config->current['ENABLESNAPSHOTS'])) {
+        if (strtolower($config->current['ENABLEAUTOMATICSNAPSHOTS']) === 'true' && strtolower($config->current['ENABLESNAPSHOTS']) === 'true') {
+          $this->generateAutomaticSnapshot();
+        }
+      }
+    }
   }
 
   function resetCopyInfos ()
@@ -285,14 +295,14 @@ class user extends simplePlugin
   private function update_cn ()
   {
     global $config;
-    $pattern  = $config->get_cfg_value('CnPattern', '%givenName% %sn%');
+    $pattern = $config->get_cfg_value('CnPattern', '%givenName% %sn%');
     $this->attributesAccess['cn']->setValue($this->applyPattern($pattern));
   }
 
   private function applyPattern ($pattern)
   {
-    $fields   = templateHandling::listFields($pattern);
-    $attrs    = [];
+    $fields = templateHandling::listFields($pattern);
+    $attrs  = [];
     foreach ($fields as $field) {
       if (in_array($field, $this->attributes)) {
         $attrs[$field] = $this->$field;
@@ -307,7 +317,7 @@ class user extends simplePlugin
           }
         }
       }
-      trigger_error('Could not find field '.$field.' in any tab!');
+      trigger_error('Could not find field ' . $field . ' in any tab!');
     }
 
     return templateHandling::parseString($pattern, $attrs);
@@ -318,13 +328,13 @@ class user extends simplePlugin
     global $config;
 
     if ($this->is_template) {
-      return 'cn='.ldap_escape_dn($this->_template_cn).',ou=templates,'.get_ou('userRDN').$this->base;
+      return 'cn=' . ldap_escape_dn($this->_template_cn) . ',ou=templates,' . get_ou('userRDN') . $this->base;
     }
 
     $this->update_cn();
     $attribute = $config->get_cfg_value('accountPrimaryAttribute', 'uid');
 
-    return $this->create_unique_dn($attribute, get_ou('userRDN').$this->base);
+    return $this->create_unique_dn($attribute, get_ou('userRDN') . $this->base);
   }
 
   public function render (): string
@@ -365,8 +375,8 @@ class user extends simplePlugin
     }
 
     if (!$this->is_template && $this->was_locked && $this->attributesAccess['userPassword']->hasChanged()) {
-      $methods  = passwordMethod::get_available_methods();
-      $pmethod  = new $methods[$this->attributesAccess['userPassword']->getMethod()]($this->dn);
+      $methods = passwordMethod::get_available_methods();
+      $pmethod = new $methods[$this->attributesAccess['userPassword']->getMethod()]($this->dn);
       $pmethod->lock_account($this->dn);
     }
 
@@ -383,24 +393,34 @@ class user extends simplePlugin
       session::set('ui', $ui);
       session::set('Last_init_lang', 'update');
     }
-    // Verification is snapshot is enabled and automatic.
-    // Note : string values are stored in that array.
-    if (isset($config->current['ENABLEAUTOMATICSNAPSHOTS']) && isset($config->current['ENABLESNAPSHOTS'])) {
-      if (strtolower($config->current['ENABLEAUTOMATICSNAPSHOTS']) === 'true' && strtolower($config->current['ENABLESNAPSHOTS']) === 'true' ) {
-        $this->generateAutomaticSnapshot();
-      }
-    }
     return parent::post_save();
   }
 
-  /*
-   * Create the snapshot object in case of automated snapshot.
+  /**
+   * @return void
+   * Note : Create a snapshot of current data before save, verifying if data have changed before taking a snap.
    */
   public function generateAutomaticSnapshot ()
   {
-    $snapshotHandler = new SnapshotHandler();
-    $snapshotHandler->createSnapshot($this->dn, 'automatic snapshot', 'USER', 'FD');
-    $snapshotHandler->verifySnapshotRetention($this->dn);
+    // Get the hash of current data before modifications.
+    global $config;
+    $ldap            = $config->get_ldap_link();
+    $currentSnapHash = md5($ldap->generateLdif($this->dn, '(!(objectClass=gosaDepartment))', 'sub'));
+
+    // Verify if current snap hash already exist in the list of existing snapshots - not taking a snap if it exists.
+    $snapshotHandler   = new SnapshotHandler();
+    $existingSnapshots = $snapshotHandler->getAvailableSnapsShots($this->dn);
+    $sameHashExist     = FALSE;
+    foreach ($existingSnapshots as $snap) {
+      if (!empty($snap['fdSnapshotHash'][0]) && $snap['fdSnapshotHash'][0] === $currentSnapHash) {
+        $sameHashExist = TRUE;
+      }
+    }
+    // Create the snapshot
+    if ($sameHashExist === FALSE) {
+      $snapshotHandler->createSnapshot($this->dn, 'automatic snapshot', 'USER', 'FD');
+      $snapshotHandler->verifySnapshotRetention($this->dn);
+    }
   }
 
   function adapt_from_template (array $attrs, array $skip = [])
@@ -430,7 +450,7 @@ class user extends simplePlugin
 
     $ldap = $config->get_ldap_link();
     $ldap->cat($userdn, ['pwdPolicySubentry', 'pwdHistory', 'pwdChangedTime', 'userPassword']);
-    $attrs = $ldap->fetch(TRUE);
+    $attrs     = $ldap->fetch(TRUE);
     $ppolicydn = '';
     if (isset($attrs['pwdPolicySubentry'][0])) {
       $ppolicydn = $attrs['pwdPolicySubentry'][0];
@@ -482,7 +502,7 @@ class user extends simplePlugin
       if (isset($policy['pwdMinAge'][0]) && isset($attrs['pwdChangedTime'][0])) {
         $date = LdapGeneralizedTime::fromString($attrs['pwdChangedTime'][0]);
         $date->setTimezone(timezone::utc());
-        $now  = new DateTime('now', timezone::utc());
+        $now = new DateTime('now', timezone::utc());
         if ($now->getTimeStamp() < $date->getTimeStamp() + $policy['pwdMinAge'][0]) {
           return sprintf(_('You must wait %d seconds before changing your password again'), $policy['pwdMinAge'][0] - ($now->getTimeStamp() - $date->getTimeStamp()));
         }
@@ -494,7 +514,7 @@ class user extends simplePlugin
         unset($attrs['pwdHistory']['count']);
         foreach ($attrs['pwdHistory'] as $pwdHistory) {
           $pwdHistory = explode('#', $pwdHistory, 4);
-          $method = passwordMethod::get_method($pwdHistory[3], $user);
+          $method     = passwordMethod::get_method($pwdHistory[3], $user);
           if (($method !== NULL) && $method->checkPassword($new_password, $pwdHistory[3])) {
             return _('Password is in history of old passwords');
           }