diff --git a/include/class_CopyPasteHandler.inc b/include/class_CopyPasteHandler.inc
index feef7e8c8545655e98ea4fcc0d5e8b0124d6533a..4fad18fd63e87720c4e032340ec45c14dc2e2676 100644
--- a/include/class_CopyPasteHandler.inc
+++ b/include/class_CopyPasteHandler.inc
@@ -27,7 +27,7 @@
 /*!
  * \brief This class contains all function to copy and paste
  */
-class CopyPasteHandler
+class CopyPasteHandler implements FusionDirectoryDialog
 {
   var $current = FALSE;
 
@@ -158,11 +158,7 @@ class CopyPasteHandler
     return $entry;
   }
 
-  /*!
-   * \brief Displays a dialog which allows the user to fix all dependencies of this object.
-   *      Create unique names, ids, or what ever
-   */
-  function execute ()
+  public function update(): bool
   {
     $ui = get_userinfo();
 
@@ -208,7 +204,6 @@ class CopyPasteHandler
 
     /* Save objects that can be pasted directly */
     if (count($this->clean_objects)) {
-      $this->save_object();
       foreach ($this->clean_objects as $key => $entry) {
         $this->current = $entry;
         $errors = $this->current['object']->save();
@@ -227,7 +222,8 @@ class CopyPasteHandler
 
     /* Save edited entry and force loading new one */
     if (isset($this->current['object'])) {
-      $this->current['object']->save_object();
+      $this->current['object']->readPost();
+      $this->current['object']->update();
       /* Save current object if edition is finished */
       if (!$this->current['object']->dialogOpened() && isset($_POST['edit_finish'])) {
         $errors = $this->current['object']->save();
@@ -240,9 +236,17 @@ class CopyPasteHandler
       }
     }
 
+    return TRUE;
+  }
+
+  /*!
+   * \brief Displays a dialog which allows the user to fix all dependencies of this object.
+   *      Create unique names, ids, or what ever
+   */
+  public function render (): string
+  {
     /* Display a list of all pastable entries */
     if ($this->current || count($this->objects_to_fix)) {
-      $this->save_object();
       if (!$this->current) {
         $key = key($this->objects_to_fix);
         if ($key !== NULL) {
@@ -252,7 +256,7 @@ class CopyPasteHandler
         }
       }
       if ($this->current) {
-        $display = $this->current['object']->execute();
+        $display = $this->current['object']->render();
         if (!$this->current['object']->dialogOpened()) {
           // Display ok, (apply) and cancel buttons
           $display .= '<p class="plugbottom">'."\n";
@@ -286,11 +290,10 @@ class CopyPasteHandler
     return $this->lastdn;
   }
 
-
   /*!
    * \brief Save new values posted by copy & paste dialog
    */
-  function save_object ()
+  public function readPost ()
   {
     if (isset($_POST['abort_current_cut-copy_operation'])) {
       $this->current = FALSE;
diff --git a/include/class_config.inc b/include/class_config.inc
index b5db3a0aeee6a9e0d6b1232a54049b4cce6d0360..881d1c45fe5bc71af6660cc9508e2dbfe0d14e31 100644
--- a/include/class_config.inc
+++ b/include/class_config.inc
@@ -462,7 +462,7 @@ class config
 
     add_lock($dn, $ui->dn);
     $config_plugin = objects::open($dn, 'configuration');
-    $config_plugin->save_object();
+    $config_plugin->update();
     $config_plugin->save();
     del_lock($dn);
   }
diff --git a/include/class_passwordRecovery.inc b/include/class_passwordRecovery.inc
index 199aaff55ccd38dfcf991f050659a155a1d936cc..b142b1d2c28dd0c81c04fd165183e5d961b0a14b 100644
--- a/include/class_passwordRecovery.inc
+++ b/include/class_passwordRecovery.inc
@@ -71,7 +71,7 @@ class passwordRecovery extends standAlonePage
     }
   }
 
-  function save_object ()
+  function readPost ()
   {
     if (!$this->activated) {
       return;
@@ -105,7 +105,7 @@ class passwordRecovery extends standAlonePage
 
   function execute ()
   {
-    $this->save_object();
+    $this->readPost();
 
     /* Do we need to show error messages? */
     if (count($this->message) != 0) {
@@ -406,7 +406,7 @@ class passwordRecovery extends standAlonePage
     ];
 
     /* Is there any problem with entered passwords? */
-    $userTabs->save_object();
+    $userTabs->update();
     $errors = $userTabs->save();
     if (!empty($errors)) {
       $this->message = $errors;
diff --git a/include/class_template.inc b/include/class_template.inc
index cf2b0369ae1b5cae8e5e683f6f52864dc7718bf8..f971eb40ea9105be31b350ca16cd7e0c50f20e22 100644
--- a/include/class_template.inc
+++ b/include/class_template.inc
@@ -24,7 +24,7 @@
  */
 
 /*! \brief Class for applying a template */
-class template
+class template implements FusionDirectoryDialog
 {
   protected $type;
   protected $dn;
@@ -240,19 +240,27 @@ class template
     }
   }
 
-  function save_object ()
+  public function readPost ()
   {
     foreach ($this->tabObject->by_object as $plugin) {
-      $plugin->save_object();
+      $plugin->readPost();
     }
   }
 
+  public function update (): bool
+  {
+    foreach ($this->tabObject->by_object as $plugin) {
+      $plugin->update();
+    }
+    return TRUE;
+  }
+
   public function dialogOpened ()
   {
     return $this->tabObject->dialogOpened();
   }
 
-  function execute ()
+  public function render (): string
   {
     $smarty   = get_smarty();
     $sections = [];
@@ -263,21 +271,8 @@ class template
         continue;
       }
       if ($plugin->is_modal_dialog()) {
-        if ($plugin instanceof simplePlugin) {
-          $dialogResult = $plugin->dialog->execute();
-          if ($dialogResult === FALSE) {
-            $plugin->closeDialog();
-          } else {
-            $this->tabObject->current = $class;
-            return $dialogResult;
-          }
-        } else {
-          $dialogResult = $plugin->execute();
-          if ($plugin->is_modal_dialog()) {
-            $this->tabObject->current = $class;
-            return $dialogResult;
-          }
-        }
+        $this->tabObject->current = $class;
+        return $plugin->render();
       }
       $attributesRendered = [];
       foreach ($this->attributes[$class] as $attr) {
diff --git a/include/functions.inc b/include/functions.inc
index 735f4341c16326f8c59a807287498fe88da5bc50..6217fa5d70c438fa063f0e6324d7c05b1972ef17 100644
--- a/include/functions.inc
+++ b/include/functions.inc
@@ -1712,7 +1712,7 @@ function change_password ($dn, $password, $hash = "")
     $userTab->userPassword,
     $userTab->attributesAccess['userPassword']->isLocked()
   ];
-  $userTabs->save_object();
+  $userTabs->update();
   $error = $userTabs->save();
   if (!empty($error)) {
     return $error;
diff --git a/include/interface_FusionDirectoryDialog.inc b/include/interface_FusionDirectoryDialog.inc
new file mode 100644
index 0000000000000000000000000000000000000000..eefe293ee893fcae61f741ba3b0d19beeb848d25
--- /dev/null
+++ b/include/interface_FusionDirectoryDialog.inc
@@ -0,0 +1,42 @@
+<?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.
+*/
+
+/*!
+ * \file interface_FusionDirectoryDialog.inc
+ * Source code for the interface FusionDirectoryDialog
+ */
+
+/*! \brief This interface should be implemented by all dialog classes in FusionDirectory
+ */
+interface FusionDirectoryDialog
+{
+  /*! \brief Interpret POST content
+   */
+  public function readPost ();
+
+  /*! \brief Update state and return FALSE if the dialog was closed
+   */
+  public function update (): bool;
+
+  /*! \brief Render the dialog and returns the HTML code
+   */
+  public function render (): string;
+}
diff --git a/include/management/class_ManagementConfigurationDialog.inc b/include/management/class_ManagementConfigurationDialog.inc
index 56f0be70e8229dccfd6d043f38afbb29f9833a73..136260d204f99c96a6ce523e4334dff10985721b 100644
--- a/include/management/class_ManagementConfigurationDialog.inc
+++ b/include/management/class_ManagementConfigurationDialog.inc
@@ -190,17 +190,17 @@ class ManagementConfigurationDialog extends ManagementDialog
     }
   }
 
-  function execute (): string
+  public function render (): string
   {
     global $config, $ui;
 
     $smarty = get_smarty();
     $smarty->assign('ManagementConfigurationACL', 'rw');
     $smarty->assign('fdManagementConfigACL', $ui->get_permissions(CONFIGRDN.$config->current['BASE'], 'configuration/configInLdap', 'fdManagementConfig', $this->readOnly()));
-    return parent::execute();
+    return parent::render();
   }
 
-  function save (): array
+  public function save (): array
   {
     global $config;
     $columnInfos  = [];
diff --git a/include/management/class_ManagementDialog.inc b/include/management/class_ManagementDialog.inc
index bbf1c9bf5b3e8921f207f9d5bd153784f41a3611..e1c44bd8cff493bafff5cd19c715ef56bc1ede25 100644
--- a/include/management/class_ManagementDialog.inc
+++ b/include/management/class_ManagementDialog.inc
@@ -2,7 +2,7 @@
 /*
   This code is part of FusionDirectory (http://www.fusiondirectory.org/)
 
-  Copyright (C) 2017-2019  FusionDirectory
+  Copyright (C) 2017-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
@@ -32,17 +32,17 @@ class ManagementDialog extends simplePlugin
     parent::__construct($dn, $object, $parent, $mainTab, $attributesInfo);
   }
 
-  function save_object ()
+  public function readPost ()
   {
-    parent::save_object();
+    parent::readPost();
     if (isset($_POST[$this->post_cancel])) {
-      $this->handle_cancel();
+      $this->handleCancel();
     } elseif (isset($_POST[$this->post_finish])) {
-      $this->handle_finish();
+      $this->handleFinish();
     }
   }
 
-  protected function handle_finish ()
+  protected function handleFinish ()
   {
     $msgs = $this->check();
     if (count($msgs)) {
@@ -60,15 +60,15 @@ class ManagementDialog extends simplePlugin
     }
   }
 
-  protected function handle_cancel ()
+  protected function handleCancel ()
   {
     $this->parent->remove_lock();
     $this->parent->closeDialogs();
   }
 
-  function execute (): string
+  public function render (): string
   {
-    return parent::execute().$this->getFooter();
+    return parent::render().$this->getFooter();
   }
 
   /*! \brief  Returns the plugin footer (save cancel), displayed in the template.
diff --git a/include/management/class_management.inc b/include/management/class_management.inc
index 4ac1951e7aa08511b7598b685096615a74c14ab4..cf027386148d12ce77bce6424235e0d50d8cb85a 100644
--- a/include/management/class_management.inc
+++ b/include/management/class_management.inc
@@ -21,7 +21,7 @@
 /*!
  * \brief Management base class
  */
-class management
+class management implements FusionDirectoryDialog
 {
   /* Object types we are currently managing */
   public $objectTypes;
@@ -57,6 +57,8 @@ class management
   protected $last_tabObject     = NULL;
   protected $last_dialogObject  = NULL;
 
+  protected $renderCache;
+
   public $headline;
   public $title;
   public $icon;
@@ -423,11 +425,54 @@ class management
     return FALSE;
   }
 
+  /* For management we have to render directly in readPost in some cases */
+  public function readPost ()
+  {
+    $this->renderCache = $this->execute();
+  }
+
+  public function update (): bool
+  {
+    if ($this->renderCache === NULL) {
+      if (!$this->dialogOpened()) {
+        // Update list
+        $this->listing->update();
+
+        // Init snapshot list for renderSnapshotActions
+        if (is_object($this->snapHandler)) {
+          $this->snapHandler->initSnapshotCache($this->listing->getBase());
+        }
+      }
+    }
+    return TRUE;
+  }
+
+  public function render (): string
+  {
+    if ($this->renderCache === NULL) {
+      if ($this->tabObject instanceOf simpleTabs) {
+        /* Display tab object */
+        $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;
+      } else {
+        /* Display list */
+        $this->renderCache = $this->renderList();
+      }
+    }
+    return $this->renderCache;
+  }
+
   /*!
    * \brief  Execute this plugin
    *          Handle actions/events, locking, snapshots, dialogs, tabs,...
    */
-  function execute (): string
+  protected function execute ()
   {
     // Ensure that html posts and gets are kept even if we see a 'Entry islocked' dialog.
     $vars = ['/^act$/','/^listing/','/^PID$/'];
@@ -456,10 +501,12 @@ class management
 
     /* Save tab or dialog object */
     if ($this->tabObject instanceOf simpleTabs) {
-      $this->tabObject->save_object();
-    } elseif (is_object($this->dialogObject) && method_exists($this->dialogObject, 'save_object')) {
+      $this->tabObject->readPost();
+      $this->tabObject->update();
+    } elseif (is_object($this->dialogObject)) {
       try {
-        $this->dialogObject->save_object();
+        $this->dialogObject->readPost();
+        $this->dialogObject->update();
       } catch (FusionDirectoryException $e) {
         $error = new FusionDirectoryError(htmlescape($e->getMessage()), 0, $e);
         $error->display();
@@ -467,30 +514,7 @@ class management
       }
     }
 
-    /* Display tab object */
-    if ($this->tabObject instanceOf simpleTabs) {
-      $display = $this->tabObject->execute();
-      $display .= $this->getTabFooter();
-      return $this->getHeader().$display;
-    }
-
-    /* Display dialog object */
-    if (is_object($this->dialogObject) && method_exists($this->dialogObject, 'execute')) {
-      $display = $this->dialogObject->execute();
-      $display .= $this->getTabFooter();
-      return $this->getHeader().$display;
-    }
-
-    // Update list
-    $this->listing->update();
-
-    // Init snapshot list for renderSnapshotActions
-    if (is_object($this->snapHandler)) {
-      $this->snapHandler->initSnapshotCache($this->listing->getBase());
-    }
-
-    // Display list
-    return $this->renderList();
+    return NULL;
   }
 
   function renderList (): string
@@ -684,7 +708,7 @@ class management
         $this->tabObject      = NULL;
         $this->currentDn      = array_shift($this->currentDns);
         $this->dialogObject->setNextTarget($this->currentDn);
-        $this->dialogObject->save_object();
+        $this->dialogObject->readPost();
       }
     }
   }
@@ -868,21 +892,20 @@ class management
    */
   function saveChanges ()
   {
-    if (($this->tabObject instanceOf simpleTabs) && ($this->dialogObject instanceOf templateDialog)) {
-      $this->tabObject->save_object();
-      $this->handleTemplateApply();
-      return;
-    }
     if ($this->tabObject instanceOf simpleTabs) {
-      $this->tabObject->save_object();
-      $msgs = $this->tabObject->save();
-      if (count($msgs)) {
-        msg_dialog::displayChecks($msgs);
-        return;
+      $this->tabObject->readPost();
+      $this->tabObject->update();
+      if ($this->dialogObject instanceOf templateDialog) {
+        $this->handleTemplateApply();
       } else {
-        logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDns, 'Entry saved');
-        $this->remove_lock();
-        $this->closeDialogs();
+        $msgs = $this->tabObject->save();
+        if (count($msgs)) {
+          msg_dialog::displayChecks($msgs);
+        } else {
+          logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDns, 'Entry saved');
+          $this->remove_lock();
+          $this->closeDialogs();
+        }
       }
     }
   }
@@ -893,7 +916,8 @@ class management
   function applyChanges ()
   {
     if ($this->tabObject instanceOf simpleTabs) {
-      $this->tabObject->save_object();
+      $this->tabObject->readPost();
+      $this->tabObject->update();
       $msgs = $this->tabObject->save();
       if (count($msgs)) {
         msg_dialog::displayChecks($msgs);
@@ -1027,7 +1051,7 @@ class management
     }
 
     // Save user input
-    $this->cpHandler->save_object();
+    $this->cpHandler->readPost();
 
     // Add entries to queue
     if (($action['action'] == 'copy') || ($action['action'] == 'cut')) {
@@ -1052,7 +1076,8 @@ class management
 
     // Display any c&p dialogs, eg. object modifications required before pasting.
     if ($this->cpPastingStarted && $this->cpHandler->entries_queued()) {
-      $data = $this->cpHandler->execute();
+      $this->cpHandler->update();
+      $data = $this->cpHandler->render();
       if (!empty($data)) {
         return $data;
       }
@@ -1289,7 +1314,9 @@ class management
         $managementObject = session::get($classname);
       }
       /* Execute and display */
-      $display = $managementObject->execute();
+      $managementObject->readPost();
+      $managementObject->update();
+      $display = $managementObject->render();
 
       /* Store the object in the session */
       session::set($classname, $managementObject);
diff --git a/include/management/class_templateDialog.inc b/include/management/class_templateDialog.inc
index 7a4d555c1b2c526984680b73a534941ee6c4fc1e..9bfc503c5ef23ea95c9d1b778808314ab4c1796e 100644
--- a/include/management/class_templateDialog.inc
+++ b/include/management/class_templateDialog.inc
@@ -21,13 +21,14 @@
 /*!
  * \brief Template dialog handling
  */
-class templateDialog
+class templateDialog implements FusionDirectoryDialog
 {
   protected $management;
   protected $type;
   protected $template = NULL;
   protected $templates;
   protected $target = NULL;
+  protected $closed = FALSE;
 
   protected $tabObject;
 
@@ -49,18 +50,21 @@ class templateDialog
     $this->target = $target;
   }
 
-  function save_object ()
+  function readPost ()
   {
     if (isset($_POST[$this->post_cancel])) {
-      return $this->handle_cancel();
+      $this->handleCancel();
+      return;
     }
 
     if (($this->target === NULL) &&
         is_object($this->template) &&
         (isset($_POST[$this->post_finish]) || isset($_GET[$this->post_finish]))
         ) {
-      $this->template->save_object();
-      return $this->handle_finish();
+      $this->template->readPost();
+      $this->template->update();
+      $this->handleFinish();
+      return;
     }
 
     if (
@@ -75,19 +79,19 @@ class templateDialog
       unset($_POST['template']);
     }
     if (is_object($this->template)) {
+      $this->template->readPost();
+      $this->template->update();
       if ($this->target !== NULL) {
         $this->management->openTabObject($this->template->apply($this->target));
         $this->management->handleTemplateApply();
-        return FALSE;
+        return;
       } else {
-        $this->template->save_object();
         if (empty($this->template->getNeeded())) {
-          return $this->handle_finish();
+          $this->handleFinish();
+          return;
         }
       }
     }
-
-    return TRUE;
   }
 
   function setNextTarget ($target)
@@ -96,11 +100,11 @@ class templateDialog
     $this->template->reset();
   }
 
-  function execute ()
+  public function render (): string
   {
     $smarty = get_smarty();
     if (is_object($this->template)) {
-      $templateOutput = $this->template->execute();
+      $templateOutput = $this->template->render();
       if ($this->template->dialogOpened()) {
         return $templateOutput;
       } else {
@@ -113,17 +117,15 @@ class templateDialog
     return $display;
   }
 
-  function handle_finish ()
+  protected function handleFinish ()
   {
     $this->management->closeDialogs();
     $this->management->openTabObject($this->template->apply());
-    return FALSE;
   }
 
-  function handle_cancel ()
+  protected function handleCancel ()
   {
     $this->management->remove_lock();
     $this->management->closeDialogs();
-    return FALSE;
   }
 }
diff --git a/include/management/snapshot/class_SnapshotCreateDialog.inc b/include/management/snapshot/class_SnapshotCreateDialog.inc
index 545076bd9846fc1df0b328715aff08e676c2f08f..89aebcf3c702e8c2b320db91919e69378cac64ed 100644
--- a/include/management/snapshot/class_SnapshotCreateDialog.inc
+++ b/include/management/snapshot/class_SnapshotCreateDialog.inc
@@ -108,10 +108,10 @@ class SnapshotCreateDialog extends ManagementDialog
     return parent::renderAttributes($readOnly);
   }
 
-  function execute (): string
+  public function update (): bool
   {
     $this->timestamp  = date(_('Y-m-d, H:i:s'));
-    return parent::execute();
+    return parent::update();
   }
 
   function save (): array
diff --git a/include/management/snapshot/class_SnapshotRestoreDialog.inc b/include/management/snapshot/class_SnapshotRestoreDialog.inc
index a2a1ed0bf3417d3d4c8aad8609ed093a23ce2480..3efec4422ceaa1f1009e1bb3f2ee47d75f5c7400 100644
--- a/include/management/snapshot/class_SnapshotRestoreDialog.inc
+++ b/include/management/snapshot/class_SnapshotRestoreDialog.inc
@@ -86,7 +86,7 @@ class SnapshotRestoreDialog extends ManagementDialog
     }
   }
 
-  function execute (): string
+  function render (): string
   {
     global $ui;
     if ($this->dialog == 'delete') {
@@ -131,7 +131,7 @@ class SnapshotRestoreDialog extends ManagementDialog
         $acl .= 'd';
       }
       $smarty->assign('SnapshotHandlerACL', $acl);
-      $str = parent::execute();
+      $str = parent::render();
       $str .= '<p class="plugbottom">'.
              '  <input type="submit" formnovalidate="formnovalidate" name="'.$this->post_cancel.'" value="'.msgPool::backButton().'"/>'.
              '</p>';
@@ -166,7 +166,7 @@ class SnapshotRestoreDialog extends ManagementDialog
     $this->snapDn = $dn;
   }
 
-  function save_object ()
+  function readPost ()
   {
     if ($this->dialog == 'delete') {
       if (isset($_POST['delete_confirmed'])) {
@@ -183,7 +183,7 @@ class SnapshotRestoreDialog extends ManagementDialog
         $this->closeDialog();
       }
     } else {
-      parent::save_object();
+      parent::readPost();
     }
   }
 
diff --git a/include/simpleplugin/attributes/dialog/class_GenericDialog.inc b/include/simpleplugin/attributes/dialog/class_GenericDialog.inc
index de44a1295d55fdcb2f14a79484da26c9b0972162..576ae739d9bde14e53434c0dc43d9c8e584b7154 100644
--- a/include/simpleplugin/attributes/dialog/class_GenericDialog.inc
+++ b/include/simpleplugin/attributes/dialog/class_GenericDialog.inc
@@ -1,6 +1,7 @@
 <?php
 /*
   This code is part of FusionDirectory (http://www.fusiondirectory.org/)
+
   Copyright (C) 2012-2020  FusionDirectory
 
   This program is free software; you can redistribute it and/or modify
@@ -20,9 +21,9 @@
 
 /*! \brief Generic dialog base class
  */
-class GenericDialog
+abstract class GenericDialog implements FusionDirectoryDialog
 {
-  protected $dialogClass = "";
+  protected $dialogClass = '';
   protected $dialog;
   protected $attribute;
 
@@ -35,30 +36,33 @@ class GenericDialog
     $this->dialog     = new $this->dialogClass();
   }
 
-  function execute ()
+  public function readPost ()
   {
     if (isset($_POST[$this->post_cancel])) {
-      return $this->handle_cancel();
-    }
-    if (isset($_POST[$this->post_finish]) || isset($_GET[$this->post_finish])) {
-      return $this->handle_finish();
+      $this->handleCancel();
+    } elseif (isset($_POST[$this->post_finish]) || isset($_GET[$this->post_finish])) {
+      $this->handleFinish();
     }
-    return $this->dialog_execute();
   }
 
-  function dialog_execute ()
+  public function update(): bool
   {
-    return $this->dialog->execute();
+    if (isset($this->dialog)) {
+      return $this->dialog->update();
+    } else {
+      return FALSE;
+    }
   }
 
-  function handle_finish ()
+  public function render(): string
   {
-    trigger_error('empty function');
-    return FALSE;
+    return $this->dialog->render();
   }
 
-  function handle_cancel ()
+  abstract protected function handleFinish ();
+
+  protected function handleCancel ()
   {
-    return FALSE;
+    unset($this->dialog);
   }
 }
diff --git a/include/simpleplugin/attributes/dialog/class_GenericSelectManagementDialog.inc b/include/simpleplugin/attributes/dialog/class_GenericSelectManagementDialog.inc
index 811a6156a6061e01049d5ae907e1ca84edd01254..2cb89a67784fc6e0815f56806249c60c4bcd834c 100644
--- a/include/simpleplugin/attributes/dialog/class_GenericSelectManagementDialog.inc
+++ b/include/simpleplugin/attributes/dialog/class_GenericSelectManagementDialog.inc
@@ -24,13 +24,13 @@ class GenericSelectManagementDialog extends GenericDialog
 {
   protected $dialogClass = 'selectManagement';
 
-  function __construct ($simplePlugin, $attribute)
+  public function __construct ($simplePlugin, $attribute)
   {
     $this->attribute  = $attribute;
     $this->dialog     = new $this->dialogClass(...$this->attribute->getSelectManagementParameters());
   }
 
-  function handle_finish ()
+  protected function handleFinish ()
   {
     $result = $this->dialog->detectPostActions();
     if (isset($result['targets'])) {
@@ -45,6 +45,6 @@ class GenericSelectManagementDialog extends GenericDialog
         $this->attribute->addValue($dn, $entry);
       }
     }
-    return FALSE;
+    unset($this->dialog);
   }
 }
diff --git a/include/simpleplugin/attributes/dialog/class_GenericSimplePluginDialog.inc b/include/simpleplugin/attributes/dialog/class_GenericSimplePluginDialog.inc
index 6f5505e53b910966cb7b5d2cd19e965aa7993fb8..5b6b6a5905b83bdfe91450da41c23616a4c049c3 100644
--- a/include/simpleplugin/attributes/dialog/class_GenericSimplePluginDialog.inc
+++ b/include/simpleplugin/attributes/dialog/class_GenericSimplePluginDialog.inc
@@ -25,7 +25,7 @@ class GenericSimplePluginDialog extends GenericDialog
 {
   protected $initialDialogValue = NULL;
 
-  function __construct ($simplePlugin, $attribute, $value = [])
+  public function __construct ($simplePlugin, $attribute, $value = [])
   {
     $this->attribute  = $attribute;
     $this->dialog     = new $this->dialogClass('new');
@@ -46,7 +46,12 @@ class GenericSimplePluginDialog extends GenericDialog
     }
   }
 
-  function buttons ()
+  public function render(): string
+  {
+    return parent::render().$this->buttons();
+  }
+
+  protected function buttons ()
   {
     return '<div style="width:100%; text-align:right; clear:both; float:none;">'.
            '  <input type="submit" name="'.$this->post_finish.'" value="'.msgPool::saveButton().'"/>&nbsp;'.
@@ -54,19 +59,14 @@ class GenericSimplePluginDialog extends GenericDialog
            '</div>';
   }
 
-  function dialog_execute ()
-  {
-    $this->dialog->save_object();
-    return $this->dialog->execute().$this->buttons();
-  }
-
-  function handle_finish ()
+  protected function handleFinish ()
   {
-    $this->dialog->save_object();
+    $this->dialog->readPost();
+    $this->dialog->update();
     $msgs = $this->dialog->check();
     if (count($msgs)) {
       msg_dialog::displayChecks($msgs);
-      return $this->dialog->execute().$this->buttons();
+      return;
     }
 
     $value = [];
@@ -75,14 +75,14 @@ class GenericSimplePluginDialog extends GenericDialog
     }
 
     $this->attribute->addValue('', $value);
-    return FALSE;
+    unset($this->dialog);
   }
 
-  function handle_cancel ()
+  protected function handleCancel ()
   {
     if ($this->initialDialogValue !== NULL) {
       $this->attribute->addValue('', $this->initialDialogValue);
     }
-    return FALSE;
+    unset($this->dialog);
   }
 }
diff --git a/include/simpleplugin/attributes/dialog/class_GenericSingleSelectManagementDialog.inc b/include/simpleplugin/attributes/dialog/class_GenericSingleSelectManagementDialog.inc
index 6d06a0be0ac4b289ee6dbb972549036fff8c8ede..3a4a07fc639f52cd1163e5be16dd9d65e03004dc 100644
--- a/include/simpleplugin/attributes/dialog/class_GenericSingleSelectManagementDialog.inc
+++ b/include/simpleplugin/attributes/dialog/class_GenericSingleSelectManagementDialog.inc
@@ -1,6 +1,7 @@
 <?php
 /*
   This code is part of FusionDirectory (http://www.fusiondirectory.org/)
+
   Copyright (C) 2012-2020  FusionDirectory
 
   This program is free software; you can redistribute it and/or modify
@@ -22,7 +23,7 @@
  */
 class GenericSingleSelectManagementDialog extends GenericSelectManagementDialog
 {
-  function handle_finish ()
+  protected function handleFinish ()
   {
     $result = $this->dialog->detectPostActions();
     if (isset($result['targets']) && count($result['targets'])) {
@@ -30,6 +31,6 @@ class GenericSingleSelectManagementDialog extends GenericSelectManagementDialog
       $entry  = $this->dialog->listing->getEntry($dn);
       $this->attribute->handleDialogResult($dn, $entry);
     }
-    return FALSE;
+    unset($this->dialog);
   }
 }
diff --git a/include/simpleplugin/class_multiPlugin.inc b/include/simpleplugin/class_multiPlugin.inc
index 8b011d9b330106166c94e237e9e8575fd66e1108..a6cfbcb9c19b6e122f93777043382edffaf67e79 100644
--- a/include/simpleplugin/class_multiPlugin.inc
+++ b/include/simpleplugin/class_multiPlugin.inc
@@ -90,20 +90,34 @@ class multiPlugin extends simplePlugin
     return implode('', array_unique(str_split($acl)));
   }
 
-  function execute (): string
+  public function readPost ()
   {
-    $display = "";
+    foreach ($this->plugin as $plug) {
+      $plug->readPost();
+    }
+  }
+
+  public function update (): boolean
+  {
+    foreach ($this->plugin as $plug) {
+      $plug->update();
+    }
+    return TRUE;
+  }
+
+  public function render (): string
+  {
+    $display = '';
 
     /* Do we represent a valid account? */
     if ($this->parent === NULL) {
       $enabled = FALSE;
-      foreach ($this->plugin as &$plug) {
+      foreach ($this->plugin as $plug) {
         if ($plug->is_account) {
           $enabled = TRUE;
           break;
         }
       }
-      unset($plug);
       if (!$enabled) {
         $display = '<img alt="'._('Error').'" src="geticon.php?context=status&amp;icon=dialog-error&amp;size=16" align="middle"/>&nbsp;<b>'.
                     msgPool::noValidExtension('').'</b>';
@@ -116,22 +130,12 @@ class multiPlugin extends simplePlugin
 
     foreach ($this->plugin as $plug) {
       $plug->read_only = $readOnly;
-      $display .= $plug->execute();
+      $display .= $plug->render();
     }
 
     return $display;
   }
 
-
-  /* Save data to object */
-  function save_object ()
-  {
-    foreach ($this->plugin as &$plug) {
-      $plug->save_object();
-    }
-    unset($plug);
-  }
-
   function check (): array
   {
     $errors = parent::check();
diff --git a/include/simpleplugin/class_simplePlugin.inc b/include/simpleplugin/class_simplePlugin.inc
index bdfa835033c4d4942ce372b4b2e7023268e0ef90..e72552b7ed736fde2d5171757f8dc292294d718a 100644
--- a/include/simpleplugin/class_simplePlugin.inc
+++ b/include/simpleplugin/class_simplePlugin.inc
@@ -27,7 +27,7 @@
 /*! \brief This class is made for easy plugin creation for editing LDAP attributes
  *
  */
-class simplePlugin implements SimpleTab
+class simplePlugin implements SimpleTab, FusionDirectoryDialog
 {
   /*! \brief This attribute store all information about attributes */
   public $attributesInfo;
@@ -640,11 +640,30 @@ class simplePlugin implements SimpleTab
     return $this->read_only;
   }
 
+  function execute (): string
+  {
+    trigger_error('obsolete');
+    $this->update();
+    return $this->render();
+  }
+
+  public function update (): bool
+  {
+    if (is_object($this->dialog)) {
+      $dialogState = $this->dialog->update();
+      if ($dialogState === FALSE) {
+        $this->closeDialog();
+      }
+    }
+
+    return TRUE;
+  }
+
   /*! \brief This function display the plugin and return the html code
    */
-  function execute (): string
+  public function render (): string
   {
-    logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->dn, "execute");
+    logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->dn, 'render');
 
     /* Reset Lock message POST/GET check array, to prevent preg_match errors */
     session::set('LOCK_VARS_TO_USE', []);
@@ -653,17 +672,12 @@ class simplePlugin implements SimpleTab
     session::set('LOCK_VARS_USED_REQUEST', []);
 
     $this->displayPlugin  = TRUE;
-    $this->header         = "";
+    $this->header         = '';
 
     if (is_object($this->dialog)) {
-      $dialogResult = $this->dialog->execute();
-      if ($dialogResult === FALSE) {
-        $this->closeDialog();
-      } else {
-        $this->header         = $dialogResult;
-        $this->displayPlugin  = FALSE;
-        return $this->header;
-      }
+      $this->header         = $this->dialog->render();
+      $this->displayPlugin  = FALSE;
+      return $this->header;
     }
 
     if ($this->displayHeader) {
@@ -928,9 +942,9 @@ class simplePlugin implements SimpleTab
 
   /*! \brief This function allows you to open a dialog
    *
-   *  \param mixed $dialog The dialog object
+   *  \param FusionDirectoryDialog $dialog The dialog object
    */
-  function openDialog ($dialog)
+  function openDialog (FusionDirectoryDialog $dialog)
   {
     $this->dialog = $dialog;
   }
@@ -1140,7 +1154,16 @@ class simplePlugin implements SimpleTab
    */
   function save_object ()
   {
-    logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->dn, 'save_object');
+    trigger_error('obsolete');
+    $this->readPost();
+  }
+
+  /*! \brief This function handle $_POST informations
+   */
+  function readPost ()
+  {
+    logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->dn, 'readPost');
+
     if ($this->displayHeader && isset($_POST[get_class($this).'_modify_state'])) {
       if ($this->is_account && $this->acl_is_removeable()) {
         $this->is_account = FALSE;
@@ -1810,8 +1833,6 @@ class simplePlugin implements SimpleTab
       }
       // For each object of this type
       foreach (array_keys($objects) as $dn) {
-        /* Avoid sending POST to opened objects */
-        $_POST = [];
         // Build the object
         $tabobject = objects::open($dn, $objectType);
         if (preg_match('/^handle_/', $mode)) {
@@ -1837,7 +1858,7 @@ class simplePlugin implements SimpleTab
                   );
                 }
               }
-              $pluginobject->save_object();
+              $pluginobject->update();
             }
           }
           $errors = $tabobject->save();
@@ -2214,7 +2235,8 @@ class simplePlugin implements SimpleTab
 
       /* save changes back to object */
       if (!$edit_mode || session::is_set('edit')) {
-        $tabObject->save_object();
+        $tabObject->readPost();
+        $tabObject->update();
       }
 
       if ($edit_mode) {
@@ -2255,9 +2277,9 @@ class simplePlugin implements SimpleTab
         $display = $lock_msg;
       } else {
         if ($tabs) {
-          $display .= $tabObject->execute();
+          $display .= $tabObject->render();
         } else {
-          $display .= $tabObject->by_object[$classname]->execute();
+          $display .= $tabObject->by_object[$classname]->render();
         }
       }
 
diff --git a/include/simpleplugin/class_simpleService.inc b/include/simpleplugin/class_simpleService.inc
index 29187cc28d7fcca22fa811f2d872a0c02fe64072..47fc2ff3d94a4bc184bc68046b9a6f4c191a0ae9 100644
--- a/include/simpleplugin/class_simpleService.inc
+++ b/include/simpleplugin/class_simpleService.inc
@@ -48,9 +48,9 @@ class simpleService extends simplePlugin
 
   /*! \brief This function display the service and return the html code
    */
-  function execute (): string
+  public function render (): string
   {
-    $str = parent::execute();
+    $str = parent::render();
 
     if (!$this->dialog) {
       $str .= '<p class="plugbottom servicebottom">'.
diff --git a/include/simpleplugin/class_simpleTabs.inc b/include/simpleplugin/class_simpleTabs.inc
index 950d9556ffc2304eaf979dd65c385552b97810c6..d6330eb0d3452363aec3a05c4069d8398b510d3e 100644
--- a/include/simpleplugin/class_simpleTabs.inc
+++ b/include/simpleplugin/class_simpleTabs.inc
@@ -27,7 +27,7 @@
 /*!
  * \brief This class contains all function to manage tabs classes
  */
-class simpleTabs
+class simpleTabs implements FusionDirectoryDialog
 {
   var $dn;
   var $acl;
@@ -194,21 +194,64 @@ class simpleTabs
   }
 
   /*!
-   * \brief Save the tab(s) contents
+   * \brief Save a tabs object
    */
-  function execute ()
+  function save_object ()
   {
-    /* Look for pressed tab button first */
-    foreach ($this->by_object as $class => &$obj) {
-      if (isset($_POST[$class]) || (isset($_POST['arg']) && $_POST['arg'] == "$class")) {
+    trigger_error('obsolete');
+    $this->readPost();
+  }
+
+  function readPost ()
+  {
+    /* Ensure that the currently selected tab is valid. */
+    if (!isset($this->by_name[$this->current])) {
+      $this->current = key($this->by_name);
+    }
+
+    /* Rotate current to last */
+    $this->last = $this->current;
+
+    /* Save last tab */
+    if ($this->last != '') {
+      logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->last, 'Reading POST');
+
+      $this->by_object[$this->last]->readPost();
+    }
+
+    /* Look for pressed tab button */
+    foreach (array_keys($this->by_object) as $class) {
+      if (isset($_POST[$class]) || (isset($_POST['arg']) && ($_POST['arg'] == $class))) {
         $this->current = $class;
         break;
       }
     }
-    unset($obj);
+  }
 
+  function execute ()
+  {
+    trigger_error('obsolete');
+    $this->update();
+    return $this->render();
+  }
+
+  public function update (): bool
+  {
+    if ($this->last != $this->current) {
+      $this->by_object[$this->last]->update();
+    }
+
+    $this->by_object[$this->current]->update();
+
+    return TRUE;
+  }
+
+  /*! \brief This function display the plugin and return the html code
+   */
+  public function render (): string
+  {
     /* Compute tab content */
-    $tabContent = $this->by_object[$this->current]->execute();
+    $tabContent = $this->by_object[$this->current]->render();
 
     /* Build tab line */
     $display = $this->renderTabList($this->dialogOpened());
@@ -221,27 +264,6 @@ class simpleTabs
     return $display;
   }
 
-  /*!
-   * \brief Save a tabs object
-   */
-  function save_object ()
-  {
-    /* Ensure that the currently selected tab is valid. */
-    if (!isset($this->by_name[$this->current])) {
-      $this->current = key($this->by_name);
-    }
-
-    /* Rotate current to last */
-    $this->last = $this->current;
-
-    /* Save last tab */
-    if ($this->last != "") {
-      logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->last, "Saving");
-
-      $this->by_object[$this->last]->save_object();
-    }
-  }
-
   /*!
    * \brief Load tab list if needed
    */
@@ -256,7 +278,7 @@ class simpleTabs
    */
   protected function renderTabList (bool $disabled = FALSE): string
   {
-    $display = "";
+    $display = '';
     if (!$disabled) {
       $display .= '<input type="hidden" name="arg" value=""/>';
     }
diff --git a/include/simpleplugin/interface_SimpleTab.inc b/include/simpleplugin/interface_SimpleTab.inc
index d8428e6d8d6abb82847cc5849d57eba6f97a46f1..76374a89399d18e733652e939eff835c43958a6e 100644
--- a/include/simpleplugin/interface_SimpleTab.inc
+++ b/include/simpleplugin/interface_SimpleTab.inc
@@ -2,7 +2,7 @@
 /*
   This code is part of FusionDirectory (http://www.fusiondirectory.org/)
 
-  Copyright (C) 2018-2019  FusionDirectory
+  Copyright (C) 2018-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
@@ -27,7 +27,7 @@
 /*! \brief This interface is implemented by classes intended to be used as tabs, mainly simplePlugin itself.
  */
 
-interface SimpleTab
+interface SimpleTab extends FusionDirectoryDialog
 {
   /*
    * Public vars expected as well by classes implementing this interface:
diff --git a/plugins/admin/acl/class_ACLsAssignmentDialog.inc b/plugins/admin/acl/class_ACLsAssignmentDialog.inc
index 4251eff5995a7f2a1cb56dfb94c03bb786e3e4ca..ef6c11837dfd158e6d7fb64dd5dd56d37908b20f 100644
--- a/plugins/admin/acl/class_ACLsAssignmentDialog.inc
+++ b/plugins/admin/acl/class_ACLsAssignmentDialog.inc
@@ -1,7 +1,8 @@
 <?php
 /*
   This code is part of FusionDirectory (http://www.fusiondirectory.org/)
-  Copyright (C) 2013-2019  FusionDirectory
+
+  Copyright (C) 2013-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
@@ -44,29 +45,24 @@ class ACLsAssignmentDialog extends GenericDialog
     $this->initialAclValue  = $acl;
   }
 
-  function dialog_execute ()
-  {
-    $this->dialog->save_object();
-    return $this->dialog->execute();
-  }
-
-  function handle_finish ()
+  function handleFinish ()
   {
-    $this->dialog->save_object();
+    $this->dialog->readPost();
+    $this->dialog->update();
     $messages = $this->dialog->check();
     if (!empty($messages)) {
       msg_dialog::displayChecks($messages);
-      return $this->dialog->execute();
+      return;
     }
     $this->attribute->addValue('', $this->dialog->getAclEntry());
-    return FALSE;
+    unset($this->dialog);
   }
 
-  function handle_cancel ()
+  function handleCancel ()
   {
     if ($this->initialAclValue !== NULL) {
       $this->attribute->addValue('', $this->initialAclValue);
     }
-    return FALSE;
+    unset($this->dialog);
   }
 }
diff --git a/plugins/admin/acl/class_aclAssignmentDialogWindow.inc b/plugins/admin/acl/class_aclAssignmentDialogWindow.inc
index ecdd3b6e74efc56870558234dfcb3a3c042000fe..40836188e8e6d7a871eab7a7a64d6a08dd2c2379 100644
--- a/plugins/admin/acl/class_aclAssignmentDialogWindow.inc
+++ b/plugins/admin/acl/class_aclAssignmentDialogWindow.inc
@@ -112,10 +112,10 @@ class aclAssignmentDialogWindow extends simplePlugin
     }
   }
 
-  function execute (): string
+  public function render (): string
   {
     $smarty = get_smarty();
-    $display = parent::execute();
+    $display = parent::render();
     if (!is_object($this->dialog)) {
       $display .= $smarty->fetch('string:'.
         '<p class="plugbottom">'.
diff --git a/plugins/admin/acl/class_aclManagement.inc b/plugins/admin/acl/class_aclManagement.inc
index 75f946cbdc82fe06a1ec7a757782cdbd4c5d94e1..174b35f2e3e8c2d896cbef883ae860cf12a00a57 100644
--- a/plugins/admin/acl/class_aclManagement.inc
+++ b/plugins/admin/acl/class_aclManagement.inc
@@ -1,8 +1,9 @@
 <?php
 /*
   This code is part of FusionDirectory (http://www.fusiondirectory.org/)
+
   Copyright (C) 2003  Cajus Pollmeier
-  Copyright (C) 2011-2018  FusionDirectory
+  Copyright (C) 2011-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
@@ -24,6 +25,7 @@ class aclAssignmentCreationDialog extends simplePlugin
   protected $post_finish = 'add_finish';
   protected $post_cancel = 'add_cancel';
   protected $management;
+  protected $closed = FALSE;
 
   static function plInfo (): array
   {
@@ -68,56 +70,58 @@ class aclAssignmentCreationDialog extends simplePlugin
     $this->attributesAccess['baseDn']->setInLdap(FALSE);
   }
 
-  function save_object ()
+  public function readPost ()
   {
-    parent::save_object();
+    parent::readPost();
     if (isset($_POST[$this->post_cancel])) {
-      return $this->handle_cancel();
-    }
-    if (isset($_POST[$this->post_finish])) {
-      return $this->handle_finish();
+      $this->handleCancel();
+    } elseif (isset($_POST[$this->post_finish])) {
+      $this->handleFinish();
     }
-    return TRUE;
   }
 
-  function save (): array
+  public function update(): bool
   {
+    parent::update();
+    return !$this->closed;
   }
 
-  function execute (): string
+  public function render (): string
   {
-    if ($this->save_object()) {
-      $smarty = get_smarty();
-      $smarty->assign($this->attributesAccess['baseDn']->getAcl().'ACL', 'rwcdm');
-      return parent::execute()."\n".
-      '<p class="plugbottom">'."\n".
-      '  <input type="submit" name="'.$this->post_finish.'" value="'.msgPool::addButton().'"/>&nbsp;'."\n".
-      '  <input type="submit" formnovalidate="formnovalidate" name="'.$this->post_cancel.'" value="'.msgPool::cancelButton().'"/>'."\n".
-      '</p>';
-    }
+    $smarty = get_smarty();
+    $smarty->assign($this->attributesAccess['baseDn']->getAcl().'ACL', 'rwcdm');
+    return parent::render()."\n".
+    '<p class="plugbottom">'."\n".
+    '  <input type="submit" name="'.$this->post_finish.'" value="'.msgPool::addButton().'"/>&nbsp;'."\n".
+    '  <input type="submit" formnovalidate="formnovalidate" name="'.$this->post_cancel.'" value="'.msgPool::cancelButton().'"/>'."\n".
+    '</p>';
   }
 
-  function handle_finish ()
+  public function handleFinish ()
   {
     $msgs = $this->check();
     if (count($msgs)) {
       msg_dialog::displayChecks($msgs);
-      return TRUE;
+      return;
     }
     try {
       $this->management->newEntryConfirmed($this->baseDn);
     } catch (NonExistingLdapNodeException $e) {
       $error = new FusionDirectoryError(htmlescape(_('The dn you entered could not be found in the LDAP')), 0, $e);
       $error->display();
-      return TRUE;
+      return;
     }
-    return FALSE;
+    $this->closed = TRUE;
   }
 
-  function handle_cancel ()
+  function handleCancel ()
   {
     $this->management->closeDialogs();
-    return FALSE;
+  }
+
+  function save (): array
+  {
+    return [];
   }
 }
 
diff --git a/plugins/admin/aclrole/class_aclEditionDialog.inc b/plugins/admin/aclrole/class_aclEditionDialog.inc
index e2655aa63c1046676a0d1dbd520f5cfb1eae04eb..b9e89be4a0fd07b3e78d0e91d2981d7e76132674 100644
--- a/plugins/admin/aclrole/class_aclEditionDialog.inc
+++ b/plugins/admin/aclrole/class_aclEditionDialog.inc
@@ -1,8 +1,9 @@
 <?php
 /*
   This code is part of FusionDirectory (http://www.fusiondirectory.org/)
+
   Copyright (C) 2003  Cajus Pollmeier
-  Copyright (C) 2011-2016  FusionDirectory
+  Copyright (C) 2011-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
@@ -20,7 +21,7 @@
 */
 
 /* ACL categories list */
-class ACLEditionDialog extends GenericDialog
+class ACLEditionDialog implements FusionDirectoryDialog
 {
   protected $initialAclValue;
   protected $dialogState      = 'create';
@@ -41,23 +42,17 @@ class ACLEditionDialog extends GenericDialog
     }
   }
 
-  function handle_finish ()
+  public function readPost ()
   {
-    $this->attribute->addValue('', $this->aclContents);
-    return FALSE;
-  }
+    global $config;
 
-  function handle_cancel ()
-  {
-    if ($this->initialAclValue !== NULL) {
-      $this->attribute->addValue('', $this->initialAclValue);
+    if (isset($_POST[$this->post_cancel])) {
+      $this->handleCancel();
+      return;
+    } elseif (isset($_POST[$this->post_finish]) || isset($_GET[$this->post_finish])) {
+      $this->handleFinish();
+      return;
     }
-    return FALSE;
-  }
-
-  function save_object ()
-  {
-    global $config;
 
     $new_acl = [];
 
@@ -143,11 +138,30 @@ class ACLEditionDialog extends GenericDialog
     }
   }
 
-  function dialog_execute ()
+  public function update(): bool
   {
-    global $config;
+    if ($this->dialogState === 'closed') {
+      return FALSE;
+    }
+  }
 
-    $this->save_object();
+  protected function handleFinish ()
+  {
+    $this->attribute->addValue('', $this->aclContents);
+    $this->dialogState = 'closed';
+  }
+
+  protected function handleCancel ()
+  {
+    if ($this->initialAclValue !== NULL) {
+      $this->attribute->addValue('', $this->initialAclValue);
+    }
+    $this->dialogState = 'closed';
+  }
+
+  public function render(): string
+  {
+    global $config;
 
     /* Create templating instance */
     $smarty = get_smarty();
@@ -232,7 +246,7 @@ class ACLEditionDialog extends GenericDialog
    *
    * \return String containing checkbox
    */
-  function mkchkbx ($name, $text, $state = FALSE)
+  protected function mkchkbx ($name, $text, $state = FALSE)
   {
     $tname = preg_replace('/[^a-z0-9]/i', '_', $name);
     return  '<input id="acl_'.$tname.'" type="checkbox" name="acl_'.$name.'"'.($state ? ' checked="checked"' : '').'/>'."\n".
@@ -247,7 +261,7 @@ class ACLEditionDialog extends GenericDialog
    *
    * \return String containing checkbox
    */
-  function mkrwbx ($name, $state = '')
+  protected function mkrwbx ($name, $state = '')
   {
     $rstate = (preg_match('/r/', $state) ? ' checked="checked"' : '');
     $wstate = (preg_match('/w/', $state) ? ' checked="checked"' : '');
@@ -266,7 +280,7 @@ class ACLEditionDialog extends GenericDialog
    *
    * \return the acl selector form
    */
-  function buildAclSelector ($list)
+  protected function buildAclSelector ($list)
   {
     $display  = '<input type="hidden" name="acl_dummy_0_0_0" value="1"/>';
     $cols     = 3;
diff --git a/plugins/admin/groups/class_groupManagement.inc b/plugins/admin/groups/class_groupManagement.inc
index e6f0591936445bc77ad7deb2760a880238fd7b7a..b9865f750283978b41878a1bc092b2edda613f9d 100644
--- a/plugins/admin/groups/class_groupManagement.inc
+++ b/plugins/admin/groups/class_groupManagement.inc
@@ -210,7 +210,8 @@ class groupManagement extends management
    */
   function saveEventDialog ()
   {
-    $this->dialogObject->save_object();
+    $this->dialogObject->readPost();
+    $this->dialogObject->update();
     $msgs = $this->dialogObject->check();
     if (count($msgs)) {
       msg_dialog::displayChecks($msgs);
diff --git a/plugins/admin/groups/tabs_ogroups.inc b/plugins/admin/groups/tabs_ogroups.inc
index 8dc6d4fd1d2d31a8e07b9039b0b9dee9edba95be..f600dd382da1a1ff6d5c0afd6d0b685380331fa4 100644
--- a/plugins/admin/groups/tabs_ogroups.inc
+++ b/plugins/admin/groups/tabs_ogroups.inc
@@ -193,9 +193,9 @@ class ogrouptabs extends simpleTabs_noSpecial
     return $errors;
   }
 
-  function save_object ($save_current = FALSE)
+  public function update (): bool
   {
-    parent::save_object($save_current);
+    parent::update();
 
     /* Update reference, transfer variables */
     $baseobject = $this->getBaseObject();
@@ -211,5 +211,7 @@ class ogrouptabs extends simpleTabs_noSpecial
         $this->by_object[$name] = $obj;
       }
     }
+
+    return TRUE;
   }
 }
diff --git a/plugins/config/class_configInLdap.inc b/plugins/config/class_configInLdap.inc
index bbb942bce477113d0f5c7773b7993f6d5d5d18cd..79942c3edd16f6e34c056dd7d608376b18196b30 100644
--- a/plugins/config/class_configInLdap.inc
+++ b/plugins/config/class_configInLdap.inc
@@ -550,13 +550,15 @@ class configInLdap extends simplePlugin
     return $messages;
   }
 
-  function save_object ()
+  public function update (): bool
   {
-    parent::save_object();
+    $res = parent::update();
 
     $this->attributesAccess['fdPasswordDefaultHash']->setChoices(
       $this->attributesAccess['fdPasswordAllowedHashes']->getValue()
     );
+
+    return $res;
   }
 
   static function get_themes ()
diff --git a/plugins/generic/references/class_reference.inc b/plugins/generic/references/class_reference.inc
index 8432762e45ef05c4e09f1b29acdbcbacf4dafdd9..f4cbc9e17046e790a32e0a7a83fdbf32bb9aceb0 100644
--- a/plugins/generic/references/class_reference.inc
+++ b/plugins/generic/references/class_reference.inc
@@ -65,14 +65,14 @@ class reference extends simplePlugin
     return 'r';
   }
 
-  function execute (): string
+  public function render (): string
   {
     if ($this->refs === NULL) {
       $this->fillRefs();
     }
     $smarty = get_smarty();
     $smarty->assign('usePrototype', 'true');
-    return parent::execute();
+    return parent::render();
   }
 
   function fillRefs ()
diff --git a/plugins/personal/generic/class_user.inc b/plugins/personal/generic/class_user.inc
index 0d9c9928e92fea432edbad89d7424216b89476f5..4bd5ef1f782db483438dfc722654f1fe667e1ba9 100644
--- a/plugins/personal/generic/class_user.inc
+++ b/plugins/personal/generic/class_user.inc
@@ -327,11 +327,11 @@ class user extends simplePlugin
     return $this->create_unique_dn($attribute, get_ou('userRDN').$this->base);
   }
 
-  function execute (): string
+  public function render (): string
   {
     $smarty = get_smarty();
     $smarty->append('css_files', 'plugins/users/style/user_tab.css');
-    return parent::execute();
+    return parent::render();
   }
 
   protected function shouldSave (): bool
diff --git a/plugins/personal/roles/class_userRoles.inc b/plugins/personal/roles/class_userRoles.inc
index 700ab6b7a6cb1410c6707ed24f576160ea4c708f..cd196a1ab54f3ece9ed753f39397713a1c628a30 100644
--- a/plugins/personal/roles/class_userRoles.inc
+++ b/plugins/personal/roles/class_userRoles.inc
@@ -249,10 +249,13 @@ class userRoles extends simplePlugin
     return [];
   }
 
-  function save_object ()
+  public function update (): bool
   {
-    parent::save_object();
+    $res = parent::update();
+
     $this->is_account = ((count($this->rolesMembership) > 0) || (count($this->groupsMembership) > 0));
+
+    return $res;
   }
 
   protected function shouldSave (): bool
diff --git a/setup/class_setup.inc b/setup/class_setup.inc
index 7de92fc20ffee3da14b066329f8a72774cb8648a..dd16ee29105c32f45cb5139f7ac8cfe144b249c1 100644
--- a/setup/class_setup.inc
+++ b/setup/class_setup.inc
@@ -21,7 +21,7 @@
 
 require_once("class_setupStep.inc");
 
-class setup
+class setup implements FusionDirectoryDialog
 {
   /* Number of setup steps */
   var $i_steps;
@@ -58,7 +58,7 @@ class setup
     }
   }
 
-  function execute ()
+  public function render (): string
   {
     /* Display phpinfo() dialog when $_GET['info'] is set,
      *  but only do this, if user is allowed to use the setup.
@@ -73,15 +73,16 @@ class setup
     $smarty->assign('usePrototype', 'true');
     $this->o_steps[$this->i_previous]->set_active(FALSE);
     $this->o_steps[$this->i_current]->set_active();
-    return $this->o_steps[$this->i_current]->execute();
+    return $this->o_steps[$this->i_current]->render();
   }
 
 
   /* Save posted attributes  */
-  function save_object ()
+  public function readPost ()
   {
-    /* Call save_object for current setup step */
-    $this->o_steps[$this->i_current]->save_object();
+    /* Call readPost and update for current setup step */
+    $this->o_steps[$this->i_current]->readPost();
+    $this->o_steps[$this->i_current]->update();
 
     /* Get attributes from setup step */
     $tmp = $this->o_steps[$this->i_current]->get_attributes();
@@ -148,6 +149,11 @@ class setup
     }
   }
 
+  public function update (): bool
+  {
+    return TRUE;
+  }
+
 
   function disable_steps_from ($start)
   {
@@ -305,8 +311,9 @@ class setup
     $setup = session::get('setup');
 
     /* Execute formular */
-    $setup->save_object();
-    $display = $setup->execute();
+    $setup->readPost();
+    $setup->update();
+    $display = $setup->render();
 
     /* Store changes  in session */
     session::set('setup', $setup);
diff --git a/setup/class_setupStepChecks.inc b/setup/class_setupStepChecks.inc
index fda940146cb6453a794a665f5dd2e32534186b41..92cbcfc4434c2f16dd7b9b187280c178d5a37a85 100644
--- a/setup/class_setupStepChecks.inc
+++ b/setup/class_setupStepChecks.inc
@@ -50,13 +50,6 @@ class setupStepChecks extends setupStep
     $this->s_description  = _('Basic checks for PHP compatibility and extensions');
   }
 
-  /* Execute and display template */
-  function execute (): string
-  {
-    $this->run_checks();
-    return parent::execute();
-  }
-
   /* Execute all checks */
   function run_checks ()
   {
@@ -229,9 +222,12 @@ class setupStepChecks extends setupStep
     $this->config_checks  = $config_checks;
   }
 
-  function save_object ()
+  public function update (): bool
   {
-    parent::save_object();
+    parent::update();
+
+    $this->run_checks();
+
     /* If everything is fine, set this step to completed
     *  and allow switching to next setup step */
     $failed = FALSE;
@@ -244,5 +240,7 @@ class setupStepChecks extends setupStep
       }
     }
     $this->is_completed = !$failed;
+
+    return TRUE;
   }
 }
diff --git a/setup/class_setupStepConfig.inc b/setup/class_setupStepConfig.inc
index d99974d3006786bf016ffa333218d2c0518442dc..db44f2020b68b254250e3eb166b93b7369431c6b 100644
--- a/setup/class_setupStepConfig.inc
+++ b/setup/class_setupStepConfig.inc
@@ -94,10 +94,10 @@ class setupStepConfig extends configInLdap
     return $tmp;
   }
 
-  function save_object ()
+  function update ()
   {
     global $config, $plist;
-    parent::save_object();
+    parent::update();
     $this->is_completed = FALSE;
     $tmp = $this->check();
     if (count($tmp) == 0) {
diff --git a/setup/class_setupStepFinish.inc b/setup/class_setupStepFinish.inc
index 389f97250e992c3c950968815287b33d70caa77a..c359d678f2abbeccfe50149d185fae4562ab3fe8 100644
--- a/setup/class_setupStepFinish.inc
+++ b/setup/class_setupStepFinish.inc
@@ -52,28 +52,10 @@ class setupStepFinish extends setupStep
     return $smarty->fetch(CONFIG_TEMPLATE_DIR.CONFIG_FILE);
   }
 
-  function execute (): string
+  public function readPost ()
   {
-    /* Check if there is currently an active fusiondirectory.conf */
-    $exists   = file_exists(CONFIG_DIR."/".CONFIG_FILE);
-    $err_msg  = '';
-
-    if ($exists && $this->is_world_readable(CONFIG_DIR.'/'.CONFIG_FILE)) {
-      $err_msg = _('Your configuration file is currently world readable. Please update the file permissions!');
-    } elseif (!$exists) {
-      $err_msg = _('The configuration is currently not readable or it does not exists.');
-    }
+    parent::readPost();
 
-    $smarty = get_smarty();
-    $smarty->assign('err_msg',  $err_msg);
-    $smarty->assign('msg2',     sprintf(_('After downloading and placing the file under %s, please make sure that the user the webserver is running with is able to read %s, while other users shouldn\'t.'), CONFIG_DIR, CONFIG_FILE));
-
-    return parent::execute();
-  }
-
-  function save_object ()
-  {
-    parent::save_object();
     $exists = file_exists(CONFIG_DIR.'/'.CONFIG_FILE);
 
     /* Redirect to FusionDirectory login, if :
@@ -92,6 +74,25 @@ class setupStepFinish extends setupStep
     }
   }
 
+  public function render (): string
+  {
+    /* Check if there is currently an active fusiondirectory.conf */
+    $exists   = file_exists(CONFIG_DIR."/".CONFIG_FILE);
+    $err_msg  = '';
+
+    if ($exists && $this->is_world_readable(CONFIG_DIR.'/'.CONFIG_FILE)) {
+      $err_msg = _('Your configuration file is currently world readable. Please update the file permissions!');
+    } elseif (!$exists) {
+      $err_msg = _('The configuration is currently not readable or it does not exists.');
+    }
+
+    $smarty = get_smarty();
+    $smarty->assign('err_msg',  $err_msg);
+    $smarty->assign('msg2',     sprintf(_('After downloading and placing the file under %s, please make sure that the user the webserver is running with is able to read %s, while other users shouldn\'t.'), CONFIG_DIR, CONFIG_FILE));
+
+    return parent::render();
+  }
+
   /* check if given file is world readable */
   function is_world_readable ($file)
   {
diff --git a/setup/class_setupStepLanguage.inc b/setup/class_setupStepLanguage.inc
index 90121d9943949512f6fb9b15500efe571052bed3..9ef9a9323682cfe94a68d1027fd99860fc957744 100644
--- a/setup/class_setupStepLanguage.inc
+++ b/setup/class_setupStepLanguage.inc
@@ -62,24 +62,25 @@ class setupStepLanguage extends setupStep
     $this->msg            = _('At this point, you can select the site wide default language. Choosing "automatic" will use the language requested by the browser. This setting can be overriden per user.');
   }
 
-  function execute (): string
+  public function update (): bool
   {
-    $languages = Language::getList(TRUE);
-    asort($languages);
-    $languages = array_merge(["" => _("Automatic")], $languages);
-    $this->attributesAccess['lang_selected']->setChoices(array_keys($languages), array_values($languages));
-    return parent::execute();
-  }
-
-  function save_object ()
-  {
-    parent::save_object();
+    parent::update();
     $this->is_completed   = TRUE;
 
-    if ($this->lang_selected != "") {
+    if ($this->lang_selected != '') {
       session::set('lang', $this->lang_selected);
     } else {
       session::set('lang', $this->lang);
     }
+    return TRUE;
+  }
+
+  public function render (): string
+  {
+    $languages = Language::getList(TRUE);
+    asort($languages);
+    $languages = array_merge(["" => _("Automatic")], $languages);
+    $this->attributesAccess['lang_selected']->setChoices(array_keys($languages), array_values($languages));
+    return parent::render();
   }
 }
diff --git a/setup/class_setupStepLdap.inc b/setup/class_setupStepLdap.inc
index 7f5d7fa7f09368024225ef9e4d6a73c4a568e381..6deb3a435640a68c5087ff4dae6ac578334f748c 100644
--- a/setup/class_setupStepLdap.inc
+++ b/setup/class_setupStepLdap.inc
@@ -130,11 +130,11 @@ class setupStepLdap extends setupStep
     }
   }
 
-  function save_object ()
+  public function update (): bool
   {
     $base       = $this->base;
     $connection = $this->connection;
-    parent::save_object();
+    parent::update();
     $this->connection = preg_replace("/\/$/", "", $this->connection);
     if (($this->base != $base) || ($this->connection != $connection)) {
       $this->parent->disable_steps_from(($this->parent->step_name_to_id(get_class($this))) + 1);
@@ -152,6 +152,8 @@ class setupStepLdap extends setupStep
     } else {
       $this->is_completed = FALSE;
     }
+
+    return TRUE;
   }
 
   function get_connection_status ()
diff --git a/setup/class_setupStepMigrate.inc b/setup/class_setupStepMigrate.inc
index 159a8a45a4160020a5861fa07bd555f8486cae06..e0c11b52038e51180c3a468cccc361aa5fd09781 100644
--- a/setup/class_setupStepMigrate.inc
+++ b/setup/class_setupStepMigrate.inc
@@ -38,8 +38,9 @@ get_user_list               - Get list of available users
 create_admin
 create_admin_user
 
-execute                     - Generate html output of this plugin
-save_object                 - Save posts
+readPost                    - Save posts
+update                      - Update state
+render                      - Generate html output of this plugin
 array_to_ldif               - Create ldif output of an ldap result array
 
  ****************/
@@ -60,7 +61,7 @@ class CheckFailedException extends FusionDirectoryException
   }
 }
 
-class StepMigrateDialog extends GenericDialog
+class StepMigrateDialog implements FusionDirectoryDialog
 {
   protected $post_cancel = 'dialog_cancel';
   protected $post_finish = 'dialog_confirm';
@@ -78,31 +79,44 @@ class StepMigrateDialog extends GenericDialog
     $this->check      = $check;
   }
 
-  public function dialog_execute ()
+  public function readPost ()
   {
-    if (
+    if (isset($_POST[$this->post_cancel])) {
+      $this->handleCancel();
+    } elseif (isset($_POST[$this->post_finish]) || isset($_GET[$this->post_finish])) {
+      $this->handleFinish();
+    } elseif (
       isset($_POST['dialog_showchanges']) ||
       isset($_POST['dialog_hidechanges']) ||
       isset($_POST['dialog_refresh'])) {
       $this->infos = $this->check->dialog_refresh();
     }
+  }
+
+  public function update(): bool
+  {
+    if (!isset($this->check)) {
+      return FALSE;
+    }
+  }
+
+  public function render(): string
+  {
     $smarty = get_smarty();
     $smarty->assign('infos', $this->infos);
     return $smarty->fetch(get_template_path($this->tplfile, TRUE, dirname(__FILE__)));
   }
 
-  function handle_finish ()
+  protected function handleFinish ()
   {
     if ($this->check->migrate_confirm()) {
-      return FALSE;
-    } else {
-      return $this->dialog_execute();
+      unset($this->check);
     }
   }
 
-  function handle_cancel ()
+  protected function handleCancel ()
   {
-    return FALSE;
+    unset($this->check);
   }
 }
 
@@ -140,7 +154,7 @@ class StepMigrateCheck
     }
   }
 
-  public function save_object ()
+  public function readPost ()
   {
     if (isset($_POST[$this->name.'_create'])) {
       $createFnc = $this->fnc.'_create';
@@ -300,30 +314,33 @@ class setupStepMigrate extends setupStep
     return preg_replace("/\n$/", '', $ret);
   }
 
-  function execute (): string
+  public function readPost ()
   {
     if (ini_get('max_execution_time') < 180) {
       set_time_limit(180);
     }
-    if (empty($this->checks) || isset($_POST['reload'])) {
-      $this->initialize_checks();
-      foreach ($this->checks as $check) {
-        $check->run();
-      }
+    $this->is_completed = TRUE;
+    parent::readPost();
+    if (isset($_POST['reload'])) {
+      $this->checks = [];
+    }
+    foreach ($this->checks as $check) {
+      $check->readPost();
     }
-    return parent::execute();
   }
 
-  function save_object ()
+  public function update (): bool
   {
     if (ini_get('max_execution_time') < 180) {
       set_time_limit(180);
     }
-    $this->is_completed = TRUE;
-    parent::save_object();
-    foreach ($this->checks as $check) {
-      $check->save_object();
+    if (empty($this->checks)) {
+      $this->initialize_checks();
+      foreach ($this->checks as $check) {
+        $check->run();
+      }
     }
+    return parent::update();
   }
 
   /* Check if the root object includes the required object classes, e.g. gosaDepartment is required for ACLs.
@@ -879,13 +896,13 @@ class setupStepMigrate extends setupStep
       $roledn = $tabObject->dn;
     }
 
+    $_POST['dialog_refresh'] = TRUE;
+
     /* Creating user */
     $tabObject = objects::create('user');
-    $_POST['givenName']                   = 'System';
-    $_POST['sn']                          = 'Administrator';
-    $_POST[$tabObject->current.'_posted'] = TRUE;
-    $_POST['dialog_refresh']              = TRUE;
-    $tabObject->save_object();
+    $tabObject->givenName                 = 'System';
+    $tabObject->sn                        = 'Administrator';
+    $tabObject->update();
     $errors = $tabObject->save();
     if (!empty($errors)) {
       msg_dialog::displayChecks($errors);
diff --git a/setup/class_setupStepWelcome.inc b/setup/class_setupStepWelcome.inc
index 4e4f9776915c0ddd3371915dcff7496e4d2311aa..09b6b89a91edf37a28d4500e446b051a36268cdd 100644
--- a/setup/class_setupStepWelcome.inc
+++ b/setup/class_setupStepWelcome.inc
@@ -52,19 +52,11 @@ class setupStepWelcome extends setupStep
     $this->s_description  = _('The welcome message');
   }
 
-  function execute (): string
+  public function update (): bool
   {
-    $smarty = get_smarty();
-    $smarty->assign('auth_id', session_id());
+    parent::update();
 
-    $smarty->assign('path', $this->authPath);
-    return parent::execute();
-  }
-
-  function save_object ()
-  {
-    parent::save_object();
-    $id = "";
+    $id = '';
 
     /* Get auth ID from file */
     if (file_exists($this->authPath) && is_readable($this->authPath)) {
@@ -78,5 +70,16 @@ class setupStepWelcome extends setupStep
     } else {
       $this->is_completed = FALSE;
     }
+
+    return TRUE;
+  }
+
+  public function render (): string
+  {
+    $smarty = get_smarty();
+    $smarty->assign('auth_id', session_id());
+
+    $smarty->assign('path', $this->authPath);
+    return parent::render();
   }
 }