From 94eaa6ba6b387dcde667d6d7423e4298c52c8e7f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=B4me=20Chilliet?= <come.chilliet@fusiondirectory.org>
Date: Thu, 27 Aug 2020 16:18:12 +0200
Subject: [PATCH] :sparkles: feat(core) Big refactor of dialog system
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This replaces save_object and execute methods by 3 methods:
readPost - Reads POST data
update - Update object state
render - Render HTML UI

The point is to avoid reading POST and rendering HTML when this is not
 needed (when doing stuff through the webservice for instance).

It’s also more consisent across FD with all classes handling some kind
 of dialog implementing the new interface FusionDirectoryDialog which
 makes sure these 3 methods are implemented.

issue #6072
---
 include/class_CopyPasteHandler.inc            |  27 ++--
 include/class_config.inc                      |   2 +-
 include/class_passwordRecovery.inc            |   6 +-
 include/class_template.inc                    |  33 ++---
 include/functions.inc                         |   2 +-
 include/interface_FusionDirectoryDialog.inc   |  42 ++++++
 .../class_ManagementConfigurationDialog.inc   |   6 +-
 include/management/class_ManagementDialog.inc |  18 +--
 include/management/class_management.inc       | 121 +++++++++++-------
 include/management/class_templateDialog.inc   |  34 ++---
 .../snapshot/class_SnapshotCreateDialog.inc   |   4 +-
 .../snapshot/class_SnapshotRestoreDialog.inc  |   8 +-
 .../attributes/dialog/class_GenericDialog.inc |  34 ++---
 .../class_GenericSelectManagementDialog.inc   |   6 +-
 .../class_GenericSimplePluginDialog.inc       |  28 ++--
 ...ss_GenericSingleSelectManagementDialog.inc |   5 +-
 include/simpleplugin/class_multiPlugin.inc    |  34 ++---
 include/simpleplugin/class_simplePlugin.inc   |  64 ++++++---
 include/simpleplugin/class_simpleService.inc  |   4 +-
 include/simpleplugin/class_simpleTabs.inc     |  82 +++++++-----
 include/simpleplugin/interface_SimpleTab.inc  |   4 +-
 .../admin/acl/class_ACLsAssignmentDialog.inc  |  22 ++--
 .../acl/class_aclAssignmentDialogWindow.inc   |   4 +-
 plugins/admin/acl/class_aclManagement.inc     |  54 ++++----
 .../admin/aclrole/class_aclEditionDialog.inc  |  58 +++++----
 .../admin/groups/class_groupManagement.inc    |   3 +-
 plugins/admin/groups/tabs_ogroups.inc         |   6 +-
 plugins/config/class_configInLdap.inc         |   6 +-
 .../generic/references/class_reference.inc    |   4 +-
 plugins/personal/generic/class_user.inc       |   4 +-
 plugins/personal/roles/class_userRoles.inc    |   7 +-
 setup/class_setup.inc                         |  23 ++--
 setup/class_setupStepChecks.inc               |  16 +--
 setup/class_setupStepConfig.inc               |   4 +-
 setup/class_setupStepFinish.inc               |  41 +++---
 setup/class_setupStepLanguage.inc             |  25 ++--
 setup/class_setupStepLdap.inc                 |   6 +-
 setup/class_setupStepMigrate.inc              |  75 ++++++-----
 setup/class_setupStepWelcome.inc              |  25 ++--
 39 files changed, 560 insertions(+), 387 deletions(-)
 create mode 100644 include/interface_FusionDirectoryDialog.inc

diff --git a/include/class_CopyPasteHandler.inc b/include/class_CopyPasteHandler.inc
index feef7e8c8..4fad18fd6 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 b5db3a0ae..881d1c45f 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 199aaff55..b142b1d2c 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 cf2b0369a..f971eb40e 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 735f4341c..6217fa5d7 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 000000000..eefe293ee
--- /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 56f0be70e..136260d20 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 bbf1c9bf5..e1c44bd8c 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 4ac1951e7..cf0273861 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 7a4d555c1..9bfc503c5 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 545076bd9..89aebcf3c 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 a2a1ed0bf..3efec4422 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 de44a1295..576ae739d 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 811a6156a..2cb89a677 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 6f5505e53..5b6b6a590 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 6d06a0be0..3a4a07fc6 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 8b011d9b3..a6cfbcb9c 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 bdfa83503..e72552b7e 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 29187cc28..47fc2ff3d 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 950d9556f..d6330eb0d 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 d8428e6d8..76374a893 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 4251eff59..ef6c11837 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 ecdd3b6e7..40836188e 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 75f946cbd..174b35f2e 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 e2655aa63..b9e89be4a 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 e6f059193..b9865f750 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 8dc6d4fd1..f600dd382 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 bbb942bce..79942c3ed 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 8432762e4..f4cbc9e17 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 0d9c9928e..4bd5ef1f7 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 700ab6b7a..cd196a1ab 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 7de92fc20..dd16ee291 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 fda940146..92cbcfc44 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 d99974d30..db44f2020 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 389f97250..c359d678f 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 90121d994..9ef9a9323 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 7f5d7fa7f..6deb3a435 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 159a8a45a..e0c11b520 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 4e4f97769..09b6b89a9 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();
   }
 }
-- 
GitLab