diff --git a/ihtml/themes/breezy/islocked.tpl b/ihtml/themes/breezy/islocked.tpl
index b2e6e89212b44041438d955ab209a03ef834549b..28600e7de365162d8efc69d30cae56e08f0f12b2 100644
--- a/ihtml/themes/breezy/islocked.tpl
+++ b/ihtml/themes/breezy/islocked.tpl
@@ -9,7 +9,7 @@
       <b>{t}Warning{/t}:</b> {t}The following entries are locked:{/t}
       <ul>
         {foreach from=$locks item=lock}
-          <li>{t 1=$lock.object 2=$lock.user 3=$lock.timestamp|date_format:"%Y-%m-%d, %H:%M:%S"}"%1" has been locked by "%2" since %3{/t}</li>
+          <li>{t 1=$lock->objectDn 2=$lock->userDn 3=$lock->timestamp|date_format:"%Y-%m-%d, %H:%M:%S"}"%1" has been locked by "%2" since %3{/t}</li>
         {/foreach}
       </ul>
     </p>
diff --git a/include/class_Lock.inc b/include/class_Lock.inc
new file mode 100644
index 0000000000000000000000000000000000000000..3b1a110d18ebc91f02bcdbbf7a9efd0f2688658f
--- /dev/null
+++ b/include/class_Lock.inc
@@ -0,0 +1,339 @@
+<?php
+/*
+  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
+  Copyright (C) 2003-2010  Cajus Pollmeier
+  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
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+class Lock
+{
+  public $dn;
+  public $objectDn;
+  public $userDn;
+  public $timestamp;
+
+  public function __construct (string $dn, string $objectDn, string $userDn, DateTime $timestamp)
+  {
+    $this->dn         = $dn;
+    $this->objectDn   = $objectDn;
+    $this->userDn     = $userDn;
+    $this->timestamp  = $timestamp;
+  }
+
+  /*!
+   *  \brief Add a lock for object(s)
+   *
+   * Adds a lock by the specified user for one ore multiple objects.
+   * If the lock for that object already exists, an error is triggered.
+   *
+   * \param array $object The object or array of objects to lock
+   *
+   * \param string $user  The user who shall own the lock
+   */
+  public static function add ($object, string $user = NULL)
+  {
+    global $config, $ui;
+
+    /* Remember which entries were opened as read only, because we
+        don't need to remove any locks for them later */
+    if (!session::is_set('LOCK_CACHE')) {
+      session::set('LOCK_CACHE', ['']);
+    }
+    if (is_array($object)) {
+      foreach ($object as $obj) {
+        static::add($obj, $user);
+      }
+      return;
+    }
+
+    if ($user === NULL) {
+      $user = $ui->dn;
+    }
+
+    $cache = &session::get_ref('LOCK_CACHE');
+    if (isset($_POST['open_readonly'])) {
+      $cache['READ_ONLY'][$object] = TRUE;
+      return;
+    }
+    if (isset($cache['READ_ONLY'][$object])) {
+      unset($cache['READ_ONLY'][$object]);
+    }
+
+    /* Just a sanity check... */
+    if (empty($object) || empty($user)) {
+      throw new FusionDirectoryError(htmlescape(_('Error while adding a lock. Contact the developers!')));
+    }
+
+    /* Check for existing entries in lock area */
+    $ldap = $config->get_ldap_link();
+    $ldap->cd(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
+    $ldap->search('(&(objectClass=fdLockEntry)(fdUserDn='.ldap_escape_f($user).')(fdObjectDn='.base64_encode($object).'))',
+        ['fdUserDn']);
+    if ($ldap->get_errno() == 32) {
+      /* No such object, means the locking branch is missing, create it */
+      $ldap->cd($config->current['BASE']);
+      try {
+        $ldap->create_missing_trees(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
+      } catch (FusionDirectoryError $error) {
+        $error->display();
+      }
+      $ldap->cd(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
+      $ldap->search('(&(objectClass=fdLockEntry)(fdUserDn='.ldap_escape_f($user).')(fdObjectDn='.base64_encode($object).'))',
+        ['fdUserDn']);
+    }
+    if (!$ldap->success()) {
+      throw new FusionDirectoryError(
+        sprintf(
+          htmlescape(_('Cannot create locking information in LDAP tree. Please contact your administrator!')).
+            '<br><br>'.htmlescape(_('LDAP server returned: %s')),
+          '<br><br><i>'.htmlescape($ldap->get_error()).'</i>'
+        )
+      );
+    }
+
+    /* Add lock if none present */
+    if ($ldap->count() == 0) {
+      $attrs  = [];
+      $name   = md5($object);
+      $dn     = 'cn='.$name.','.get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
+      $ldap->cd($dn);
+      $attrs = [
+        'objectClass'     => 'fdLockEntry',
+        'cn'              => $name,
+        'fdUserDn'        => $user,
+        'fdObjectDn'      => base64_encode($object),
+        'fdLockTimestamp' => LdapGeneralizedTime::toString(new DateTime('now')),
+      ];
+      $ldap->add($attrs);
+      if (!$ldap->success()) {
+        throw new FusionDirectoryLdapError($dn, LDAP_ADD, $ldap->get_error(), $ldap->get_errno());
+      }
+    }
+  }
+
+  /*!
+   * \brief Remove a lock for object(s)
+   *
+   * Remove a lock for object(s)
+   *
+   * \param mixed $object object or array of objects for which a lock shall be removed
+   */
+  public static function deleteByObject ($object)
+  {
+    global $config;
+
+    if (is_array($object)) {
+      foreach ($object as $obj) {
+        static::deleteByObject($obj);
+      }
+      return;
+    }
+
+    /* Sanity check */
+    if ($object == '') {
+      return;
+    }
+
+    /* If this object was opened in read only mode then
+        skip removing the lock entry, there wasn't any lock created.
+      */
+    if (session::is_set('LOCK_CACHE')) {
+      $cache = &session::get_ref('LOCK_CACHE');
+      if (isset($cache['READ_ONLY'][$object])) {
+        unset($cache['READ_ONLY'][$object]);
+        return;
+      }
+    }
+
+    /* Check for existance and remove the entry */
+    $ldap = $config->get_ldap_link();
+    $dn   = get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
+    $ldap->cd($dn);
+    $ldap->search('(&(objectClass=fdLockEntry)(fdObjectDn='.base64_encode($object).'))', ['fdObjectDn']);
+    if (!$ldap->success()) {
+      throw new FusionDirectoryLdapError($dn, LDAP_SEARCH, $ldap->get_error(), $ldap->get_errno());
+    } elseif ($attrs = $ldap->fetch()) {
+      $ldap->rmdir($attrs['dn']);
+      if (!$ldap->success()) {
+        throw new FusionDirectoryLdapError($attrs['dn'], LDAP_DEL, $ldap->get_error(), $ldap->get_errno());
+      }
+    }
+  }
+
+  /*!
+   * \brief Remove all locks owned by a specific userdn
+   *
+   * For a given userdn remove all existing locks. This is usually
+   * called on logout.
+   *
+   * \param string $userdn the subject whose locks shall be deleted
+   */
+  public static function deleteByUser (string $userdn)
+  {
+    global $config;
+
+    /* Get LDAP ressources */
+    $ldap = $config->get_ldap_link();
+    $ldap->cd(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
+
+    /* Remove all objects of this user, drop errors silently in this case. */
+    $ldap->search('(&(objectClass=fdLockEntry)(fdUserDn='.ldap_escape_f($userdn).'))', ['fdUserDn']);
+    while ($attrs = $ldap->fetch()) {
+      $ldap->rmdir($attrs['dn']);
+    }
+  }
+
+  /*!
+   * \brief Get locks for objects
+   *
+   * \param mixed $objects Array of dns for which a lock will be searched or dn of a single object
+   *
+   * \param boolean $allow_readonly TRUE if readonly access should be permitted,
+   * FALSE if not (default).
+   *
+   * \return A numbered array containing all found locks as an array with key 'object'
+   * and key 'user', or FALSE if an error occured.
+   */
+  public static function get ($objects, bool $allow_readonly = FALSE): array
+  {
+    global $config;
+
+    if (is_array($objects) && (count($objects) == 1)) {
+      $objects = reset($objects);
+    }
+    if (is_array($objects)) {
+      if ($allow_readonly) {
+        throw new FusionDirectoryException('Read only is not possible for several objects');
+      }
+      $filter = '(&(objectClass=fdLockEntry)(|';
+      foreach ($objects as $obj) {
+        $filter .= '(fdObjectDn='.base64_encode($obj).')';
+      }
+      $filter .= '))';
+    } else {
+      if ($allow_readonly && isset($_POST['open_readonly'])) {
+        /* If readonly is allowed and asked and there is only one object, bypass lock detection */
+        return [];
+      }
+      $filter = '(&(objectClass=fdLockEntry)(fdObjectDn='.base64_encode($objects).'))';
+    }
+
+    /* Get LDAP link, check for presence of the lock entry */
+    $ldap = $config->get_ldap_link();
+    $dn   = get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
+    $ldap->cd($dn);
+    $ldap->search($filter, ['fdUserDn','fdObjectDn', 'fdLockTimestamp']);
+    if (!$ldap->success()) {
+      throw new FusionDirectoryLdapError($dn, LDAP_SEARCH, $ldap->get_error(), $ldap->get_errno());
+    }
+
+    $locks = [];
+    while ($attrs = $ldap->fetch()) {
+      $locks[] = new Lock(
+        $attrs['dn'],
+        base64_decode($attrs['fdObjectDn'][0]),
+        $attrs['fdUserDn'][0],
+        LdapGeneralizedTime::fromString($attrs['fdLockTimestamp'][0])
+      );
+    }
+
+    if (!is_array($objects) && (count($locks) > 1)) {
+      /* Hmm. We're removing broken LDAP information here and issue a warning. */
+      $warning = new FusionDirectoryWarning(htmlescape(_('Found multiple locks for object to be locked. This should not happen - cleaning up multiple references.')));
+      $warning->display();
+
+      /* Clean up these references now... */
+      foreach ($locks as $lock) {
+        $ldap->rmdir($lock->dn);
+      }
+
+      return [];
+    }
+
+    return $locks;
+  }
+
+  /*!
+   * \brief Generate a lock message
+   *
+   * This message shows a warning to the user, that a certain object is locked
+   * and presents some choices how the user can proceed. By default this
+   * is 'Cancel' or 'Edit anyway', but depending on the function call
+   * its possible to allow readonly access, too.
+   *
+   * Example usage:
+   * \code
+   * if ($locks = Lock::get($this->dn)) {
+   *   return Lock::genLockedMessage($locks, TRUE);
+   * }
+   * \endcode
+   *
+   * \param string $locks the locks as returned by Lock::get
+   *
+   * \param boolean $allowReadonly TRUE if readonly access should be permitted,
+   * FALSE if not (default).
+   *
+   * \param string $action Label of the action button, "Edit anyway" by default. Will be escaped.
+   *
+   */
+  public static function genLockedMessage (array $locks, bool $allowReadonly = FALSE, string $action = NULL): string
+  {
+    /* Save variables from LOCK_VARS_TO_USE in session - for further editing */
+    if (session::is_set('LOCK_VARS_TO_USE') && count(session::get('LOCK_VARS_TO_USE'))) {
+      $LOCK_VARS_USED_GET       = [];
+      $LOCK_VARS_USED_POST      = [];
+      $LOCK_VARS_USED_REQUEST   = [];
+      $LOCK_VARS_TO_USE         = session::get('LOCK_VARS_TO_USE');
+
+      foreach ($LOCK_VARS_TO_USE as $name) {
+        if (empty($name)) {
+          continue;
+        }
+
+        foreach ($_POST as $Pname => $Pvalue) {
+          if (preg_match($name, $Pname)) {
+            $LOCK_VARS_USED_POST[$Pname] = $_POST[$Pname];
+          }
+        }
+
+        foreach ($_GET as $Pname => $Pvalue) {
+          if (preg_match($name, $Pname)) {
+            $LOCK_VARS_USED_GET[$Pname] = $_GET[$Pname];
+          }
+        }
+
+        foreach ($_REQUEST as $Pname => $Pvalue) {
+          if (preg_match($name, $Pname)) {
+            $LOCK_VARS_USED_REQUEST[$Pname] = $_REQUEST[$Pname];
+          }
+        }
+      }
+      session::set('LOCK_VARS_TO_USE',        []);
+      session::set('LOCK_VARS_USED_GET',      $LOCK_VARS_USED_GET);
+      session::set('LOCK_VARS_USED_POST',     $LOCK_VARS_USED_POST);
+      session::set('LOCK_VARS_USED_REQUEST',  $LOCK_VARS_USED_REQUEST);
+    }
+
+    /* Prepare and show template */
+    $smarty = get_smarty();
+    $smarty->assign('allow_readonly', $allowReadonly);
+    $smarty->assign('action',         ($action ?? _('Edit anyway')));
+    $smarty->assign('locks',          $locks);
+
+    return $smarty->fetch(get_template_path('islocked.tpl'));
+  }
+}
diff --git a/include/class_config.inc b/include/class_config.inc
index 40cb70773fc9123b06a3c31254ceff87b2ce6738..7c719addbdb90801e29609868415be8a285ae901 100644
--- a/include/class_config.inc
+++ b/include/class_config.inc
@@ -460,11 +460,11 @@ class config
       }
     }
 
-    add_lock($dn, $ui->dn);
+    Lock::add($dn);
     $config_plugin = objects::open($dn, 'configuration');
     $config_plugin->update();
     $config_plugin->save();
-    del_lock($dn);
+    Lock::deleteByObject($dn);
   }
 
   function load_inldap_config ()
diff --git a/include/functions.inc b/include/functions.inc
index d3469719f5bdb3b6a16bff6186df94ac4ad07ffd..5646e059132e60cef9353a276c450576f49bd889 100644
--- a/include/functions.inc
+++ b/include/functions.inc
@@ -317,284 +317,6 @@ function fusiondirectory_log ($message)
   syslog(LOG_INFO, "FusionDirectory $username: $message");
 }
 
-/*!
- *  \brief Add a lock for object(s)
- *
- * Adds a lock by the specified user for one ore multiple objects.
- * If the lock for that object already exists, an error is triggered.
- *
- * \param array $object The object or array of objects to lock
- *
- * \param string $user  The user who shall own the lock
- */
-function add_lock ($object, $user)
-{
-  global $config;
-
-  /* Remember which entries were opened as read only, because we
-      don't need to remove any locks for them later.
-   */
-  if (!session::is_set('LOCK_CACHE')) {
-    session::set('LOCK_CACHE', ['']);
-  }
-  if (is_array($object)) {
-    foreach ($object as $obj) {
-      add_lock($obj, $user);
-    }
-    return;
-  }
-
-  $cache = &session::get_ref('LOCK_CACHE');
-  if (isset($_POST['open_readonly'])) {
-    $cache['READ_ONLY'][$object] = TRUE;
-    return;
-  }
-  if (isset($cache['READ_ONLY'][$object])) {
-    unset($cache['READ_ONLY'][$object]);
-  }
-
-  /* Just a sanity check... */
-  if (empty($object) || empty($user)) {
-    $error = new FusionDirectoryError(htmlescape(_('Error while adding a lock. Contact the developers!')));
-    $error->display();
-    return;
-  }
-
-  /* Check for existing entries in lock area */
-  $ldap = $config->get_ldap_link();
-  $ldap->cd(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
-  $ldap->search('(&(objectClass=fdLockEntry)(fdUserDn='.ldap_escape_f($user).')(fdObjectDn='.base64_encode($object).'))',
-      ['fdUserDn']);
-  if ($ldap->get_errno() == 32) {
-    /* No such object, means the locking branch is missing, create it */
-    $ldap->cd($config->current['BASE']);
-    try {
-      $ldap->create_missing_trees(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
-    } catch (FusionDirectoryError $error) {
-      $error->display();
-    }
-    $ldap->cd(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
-    $ldap->search('(&(objectClass=fdLockEntry)(fdUserDn='.ldap_escape_f($user).')(fdObjectDn='.base64_encode($object).'))',
-      ['fdUserDn']);
-  }
-  if (!$ldap->success()) {
-    $error = new FusionDirectoryError(
-      sprintf(
-        htmlescape(_('Cannot create locking information in LDAP tree. Please contact your administrator!')).
-          '<br><br>'.htmlescape(_('LDAP server returned: %s')),
-        '<br><br><i>'.htmlescape($ldap->get_error()).'</i>'
-      )
-    );
-    $error->display();
-    return;
-  }
-
-  /* Add lock if none present */
-  if ($ldap->count() == 0) {
-    $attrs  = [];
-    $name   = md5($object);
-    $dn     = 'cn='.$name.','.get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
-    $ldap->cd($dn);
-    $attrs = [
-      'objectClass'     => 'fdLockEntry',
-      'fdUserDn'        => $user,
-      'fdObjectDn'      => base64_encode($object),
-      'cn'              => $name,
-      'fdLockTimestamp' => LdapGeneralizedTime::toString(new DateTime('now')),
-    ];
-    $ldap->add($attrs);
-    if (!$ldap->success()) {
-      $error = new FusionDirectoryLdapError($dn, LDAP_ADD, $ldap->get_error(), $ldap->get_errno());
-      $error->display();
-      return;
-    }
-  }
-}
-
-
-/*!
- * \brief Remove a lock for object(s)
- *
- * Remove a lock for object(s)
- *
- * \param mixed $object object or array of objects for which a lock shall be removed
- */
-function del_lock ($object)
-{
-  global $config;
-
-  if (is_array($object)) {
-    foreach ($object as $obj) {
-      del_lock($obj);
-    }
-    return;
-  }
-
-  /* Sanity check */
-  if ($object == '') {
-    return;
-  }
-
-  /* If this object was opened in read only mode then
-      skip removing the lock entry, there wasn't any lock created.
-    */
-  if (session::is_set('LOCK_CACHE')) {
-    $cache = &session::get_ref('LOCK_CACHE');
-    if (isset($cache['READ_ONLY'][$object])) {
-      unset($cache['READ_ONLY'][$object]);
-      return;
-    }
-  }
-
-  /* Check for existance and remove the entry */
-  $ldap = $config->get_ldap_link();
-  $dn   = get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
-  $ldap->cd($dn);
-  $ldap->search('(&(objectClass=fdLockEntry)(fdObjectDn='.base64_encode($object).'))', ['fdObjectDn']);
-  $attrs = $ldap->fetch();
-  if (!$ldap->success()) {
-    $error = new FusionDirectoryLdapError($dn, LDAP_SEARCH, $ldap->get_error(), $ldap->get_errno());
-    $error->display();
-    return;
-  } elseif (!empty($attrs['dn'])) {
-    $ldap->rmdir($attrs['dn']);
-  }
-}
-
-
-/*!
- * \brief Remove all locks owned by a specific userdn
- *
- * For a given userdn remove all existing locks. This is usually
- * called on logout.
- *
- * \param string $userdn the subject whose locks shall be deleted
- */
-function del_user_locks ($userdn)
-{
-  global $config;
-
-  /* Get LDAP ressources */
-  $ldap = $config->get_ldap_link();
-  $ldap->cd(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
-
-  /* Remove all objects of this user, drop errors silently in this case. */
-  $ldap->search('(&(objectClass=fdLockEntry)(fdUserDn='.ldap_escape_f($userdn).'))', ['fdUserDn']);
-  while ($attrs = $ldap->fetch()) {
-    $ldap->rmdir($attrs['dn']);
-  }
-}
-
-
-/*!
- * \brief Get a lock for a specific object
- *
- * Searches for a lock on a given object.
- *
- * \param string $object subject whose locks are to be searched
- *
- * \return string Returns the dn of the user who owns the lock or '' if no lock is found
- * or FALSE if an error occured.
- */
-function get_lock ($object)
-{
-  /* Sanity check */
-  if ($object == '') {
-    $error = new FusionDirectoryError(htmlescape(_('Error while adding a lock. Contact the developers!')));
-    $error->display();
-    return FALSE;
-  }
-
-  /* Allow readonly access, the plugin constructor will restrict the acls */
-  if (isset($_POST['open_readonly'])) {
-    return '';
-  }
-
-  $locks = get_locks($object);
-  if ($locks === FALSE) {
-    return FALSE;
-  } elseif (empty($locks)) {
-    return '';
-  } else {
-    return $locks[0]['user'];
-  }
-}
-
-
-/*!
- * \brief Get locks for objects
- *
- * Similar as get_lock(), but for multiple objects.
- *
- * \param mixed $objects Array of dns for which a lock will be searched or dn of a single object
- *
- * \param boolean $allow_readonly TRUE if readonly access should be permitted,
- * FALSE if not (default).
- *
- * \return A numbered array containing all found locks as an array with key 'object'
- * and key 'user', or FALSE if an error occured.
- */
-function get_locks ($objects, $allow_readonly = FALSE)
-{
-  global $config;
-
-  if (is_array($objects) && (count($objects) == 1)) {
-    $objects = reset($objects);
-  }
-  if (is_array($objects)) {
-    if ($allow_readonly) {
-      trigger_error('Read only is not possible for several objects');
-    }
-    $filter = '(&(objectClass=fdLockEntry)(|';
-    foreach ($objects as $obj) {
-      $filter .= '(fdObjectDn='.base64_encode($obj).')';
-    }
-    $filter .= '))';
-  } else {
-    if ($allow_readonly && isset($_POST['open_readonly'])) {
-      /* If readonly is allowed and asked and there is only one object, bypass lock detection */
-      return [];
-    }
-    $filter = '(&(objectClass=fdLockEntry)(fdObjectDn='.base64_encode($objects).'))';
-  }
-
-  /* Get LDAP link, check for presence of the lock entry */
-  $ldap = $config->get_ldap_link();
-  $dn   = get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
-  $ldap->cd($dn);
-  $ldap->search($filter, ['fdUserDn','fdObjectDn', 'fdLockTimestamp']);
-  if (!$ldap->success()) {
-    $error = new FusionDirectoryLdapError($dn, LDAP_SEARCH, $ldap->get_error(), $ldap->get_errno());
-    $error->display();
-    return FALSE;
-  }
-
-  $locks = [];
-  while ($attrs = $ldap->fetch()) {
-    $locks[] = [
-      'dn'        => $attrs['dn'],
-      'object'    => base64_decode($attrs['fdObjectDn'][0]),
-      'user'      => $attrs['fdUserDn'][0],
-      'timestamp' => LdapGeneralizedTime::fromString($attrs['fdLockTimestamp'][0]),
-    ];
-  }
-
-  if (!is_array($objects) && (count($locks) > 1)) {
-    /* Hmm. We're removing broken LDAP information here and issue a warning. */
-    $warning = new FusionDirectoryWarning(htmlescape(_('Found multiple locks for object to be locked. This should not happen - cleaning up multiple references.')));
-    $warning->display();
-
-    /* Clean up these references now... */
-    foreach ($locks as $lock) {
-      $ldap->rmdir($lock['dn']);
-    }
-
-    return FALSE;
-  }
-
-  return $locks;
-}
-
 /*!
  * \brief Return the current userinfo object
  *
diff --git a/include/login/class_LoginMethod.inc b/include/login/class_LoginMethod.inc
index 06d7c4a1a94158a018e41a615ffecb5dd8354303..1798665fd80ca53a4ee5c8ee6ea7f26107a4cd20 100644
--- a/include/login/class_LoginMethod.inc
+++ b/include/login/class_LoginMethod.inc
@@ -122,7 +122,7 @@ class LoginMethod
     global $ui, $config, $plist, $message, $smarty;
 
     /* Remove all locks of this user */
-    del_user_locks($ui->dn);
+    Lock::deleteByUser($ui->dn);
 
     /* Save userinfo and plugin structure */
     session::set('ui', $ui);
diff --git a/include/management/class_management.inc b/include/management/class_management.inc
index 0ef3b4ab94bef474bbae81675e40cdf0e390763d..a0b3b0f8d322a3280be2dd611a2f00de3b195a2a 100644
--- a/include/management/class_management.inc
+++ b/include/management/class_management.inc
@@ -488,8 +488,7 @@ class management implements FusionDirectoryDialog
   protected function execute ()
   {
     // Ensure that html posts and gets are kept even if we see a 'Entry islocked' dialog.
-    $vars = ['/^act$/','/^listing/','/^PID$/'];
-    session::set('LOCK_VARS_TO_USE', $vars);
+    session::set('LOCK_VARS_TO_USE', ['/^act$/','/^listing/','/^PID$/']);
 
     /* Display the copy & paste dialog, if it is currently open */
     $ret = $this->copyPasteHandler();
@@ -606,10 +605,10 @@ class management implements FusionDirectoryDialog
   public function removeLocks ()
   {
     if (!empty($this->currentDn) && ($this->currentDn != 'new')) {
-      del_lock($this->currentDn);
+      Lock::deleteByObject($this->currentDn);
     }
     if (count($this->currentDns)) {
-      del_lock($this->currentDns);
+      Lock::deleteByObject($this->currentDns);
     }
   }
 
@@ -718,7 +717,7 @@ class management implements FusionDirectoryDialog
       if (!$cancel) {
         logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Template applied!');
       }
-      del_lock($this->currentDn);
+      Lock::deleteByObject($this->currentDn);
       if (empty($this->currentDns)) {
         $this->closeDialogs();
       } else {
@@ -807,12 +806,12 @@ class management implements FusionDirectoryDialog
     $this->currentDns = $action['targets'];
 
     // check locks
-    if ($locks = get_locks($this->currentDns)) {
-      return static::genLockedMessage($locks, FALSE, _('Apply anyway'));
+    if ($locks = Lock::get($this->currentDns)) {
+      return Lock::genLockedMessage($locks, FALSE, _('Apply anyway'));
     }
 
     // Add locks
-    add_lock($this->currentDns, $ui->dn);
+    Lock::add($this->currentDns);
 
     // Detect type and check that all targets share the same type
     $type = NULL;
@@ -862,12 +861,12 @@ class management implements FusionDirectoryDialog
     logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $action['targets'], 'Entry archive requested');
 
     // Check locks
-    if ($locks = get_locks($this->currentDns)) {
-      return static::genLockedMessage($locks, FALSE, _('Archive anyway'));
+    if ($locks = Lock::get($this->currentDns)) {
+      return Lock::genLockedMessage($locks, FALSE, _('Archive anyway'));
     }
 
     // Add locks
-    add_lock($this->currentDns, $ui->dn);
+    Lock::add($this->currentDns);
 
     $objects = [];
     foreach ($this->currentDns as $dn) {
@@ -907,7 +906,7 @@ class management implements FusionDirectoryDialog
       } else {
         msg_dialog::displayChecks($errors);
       }
-      del_lock($dn);
+      Lock::deleteByObject($dn);
     }
 
     if ($success > 0) {
@@ -948,10 +947,10 @@ class management implements FusionDirectoryDialog
 
     // Get the dn of the object and create lock
     $this->currentDn = $target;
-    if ($locks = get_locks($this->currentDn, TRUE)) {
-      return static::genLockedMessage($locks, TRUE);
+    if ($locks = Lock::get($this->currentDn, TRUE)) {
+      return Lock::genLockedMessage($locks, TRUE);
     }
-    add_lock($this->currentDn, $ui->dn);
+    Lock::add($this->currentDn);
 
     // Open object
     $this->openTabObject(objects::open($this->currentDn, $entry->getTemplatedType()));
@@ -1053,12 +1052,12 @@ class management implements FusionDirectoryDialog
     // We've at least one entry to delete.
     if (count($this->currentDns)) {
       // Check locks
-      if ($locks = get_locks($this->currentDns)) {
-        return static::genLockedMessage($locks, FALSE, _('Delete anyway'));
+      if ($locks = Lock::get($this->currentDns)) {
+        return Lock::genLockedMessage($locks, FALSE, _('Delete anyway'));
       }
 
       // Add locks
-      add_lock($this->currentDns, $ui->dn);
+      Lock::add($this->currentDns);
 
       $objects = [];
       foreach ($this->currentDns as $dn) {
@@ -1110,7 +1109,7 @@ class management implements FusionDirectoryDialog
         msg_dialog::displayChecks($errors);
 
         // Remove the lock for the current object.
-        del_lock($this->currentDn);
+        Lock::deleteByObject($this->currentDn);
       } else {
         $error = new FusionDirectoryPermissionError(msgPool::permDelete($dn));
         $error->display();
@@ -1350,7 +1349,7 @@ class management implements FusionDirectoryDialog
         $this->listing->focusDn($dn);
         $entry = $this->listing->getEntry($dn);
         $this->currentDn = $dn;
-        add_lock($this->currentDn, $ui->dn);
+        Lock::add($this->currentDn);
 
         // Open object
         $this->openTabObject(objects::open($this->currentDn, $entry->getTemplatedType()));
@@ -1379,76 +1378,6 @@ class management implements FusionDirectoryDialog
     }
   }
 
-  /*!
-   * \brief Generate a lock message
-   *
-   * This message shows a warning to the user, that a certain object is locked
-   * and presents some choices how the user can proceed. By default this
-   * is 'Cancel' or 'Edit anyway', but depending on the function call
-   * its possible to allow readonly access, too.
-   *
-   * Example usage:
-   * \code
-   * if ($locks = get_locks($this->dn)) {
-   *   return management::genLockedMessage($locks, TRUE);
-   * }
-   * \endcode
-   *
-   * \param string $locks the locks as returned by get_locks
-   *
-   * \param boolean $allowReadonly TRUE if readonly access should be permitted,
-   * FALSE if not (default).
-   *
-   * \param string $action Label of the action button, "Edit anyway" by default. Will be escaped.
-   *
-   */
-  function genLockedMessage (array $locks, bool $allowReadonly = FALSE, string $action = NULL): string
-  {
-    /* Save variables from LOCK_VARS_TO_USE in session - for further editing */
-    if (session::is_set('LOCK_VARS_TO_USE') && count(session::get('LOCK_VARS_TO_USE'))) {
-      $LOCK_VARS_USED_GET       = [];
-      $LOCK_VARS_USED_POST      = [];
-      $LOCK_VARS_USED_REQUEST   = [];
-      $LOCK_VARS_TO_USE         = session::get('LOCK_VARS_TO_USE');
-
-      foreach ($LOCK_VARS_TO_USE as $name) {
-        if (empty($name)) {
-          continue;
-        }
-
-        foreach ($_POST as $Pname => $Pvalue) {
-          if (preg_match($name, $Pname)) {
-            $LOCK_VARS_USED_POST[$Pname] = $_POST[$Pname];
-          }
-        }
-
-        foreach ($_GET as $Pname => $Pvalue) {
-          if (preg_match($name, $Pname)) {
-            $LOCK_VARS_USED_GET[$Pname] = $_GET[$Pname];
-          }
-        }
-
-        foreach ($_REQUEST as $Pname => $Pvalue) {
-          if (preg_match($name, $Pname)) {
-            $LOCK_VARS_USED_REQUEST[$Pname] = $_REQUEST[$Pname];
-          }
-        }
-      }
-      session::set('LOCK_VARS_TO_USE',        []);
-      session::set('LOCK_VARS_USED_GET',      $LOCK_VARS_USED_GET);
-      session::set('LOCK_VARS_USED_POST',     $LOCK_VARS_USED_POST);
-      session::set('LOCK_VARS_USED_REQUEST',  $LOCK_VARS_USED_REQUEST);
-    }
-
-    /* Prepare and show template */
-    $smarty = get_smarty();
-    $smarty->assign('allow_readonly', $allowReadonly);
-    $smarty->assign('action',         ($action ?? _('Edit anyway')));
-    $smarty->assign('locks',          $locks);
-
-    return $smarty->fetch(get_template_path('islocked.tpl'));
-  }
-
   static function mainInc ($classname = NULL, $objectTypes = FALSE)
   {
     global $remove_lock, $cleanup, $display;
diff --git a/include/simpleplugin/class_simplePlugin.inc b/include/simpleplugin/class_simplePlugin.inc
index 988d86084de6eb5b8b15c1a6d6e19e584aa509e6..55dc16356ab4fe3e73d2ca1a2105a0b976257f51 100644
--- a/include/simpleplugin/class_simplePlugin.inc
+++ b/include/simpleplugin/class_simplePlugin.inc
@@ -2205,7 +2205,7 @@ class simplePlugin implements SimpleTab
       && ($remove_lock || (isset($_POST['edit_cancel']) && session::is_set('edit')))
       && session::is_set($classname)) {
       /* Remove locks created by this plugin */
-      del_lock($entry_dn);
+      Lock::deleteByObject($entry_dn);
     }
 
     /* Remove this plugin from session */
@@ -2249,12 +2249,12 @@ class simplePlugin implements SimpleTab
         /* Enter edit mode? */
         if ((isset($_POST['edit'])) && (!session::is_set('edit'))) {
           /* Check locking */
-          if ($locks = get_locks($entry_dn)) {
-            session::set('LOCK_VARS_TO_USE', ["/^edit$/", "/^plug$/"]);
-            $lock_msg = management::genLockedMessage($locks);
+          if ($locks = Lock::get($entry_dn)) {
+            session::set('LOCK_VARS_TO_USE', ['/^edit$/', '/^plug$/']);
+            $lock_msg = Lock::genLockedMessage($locks);
           } else {
             /* Lock the current entry */
-            add_lock($entry_dn, $ui->dn);
+            Lock::add($entry_dn);
             session::set('edit', TRUE);
           }
         }
@@ -2266,7 +2266,7 @@ class simplePlugin implements SimpleTab
 
           /* No errors, save object */
           if (count($errors) == 0) {
-            del_lock($entry_dn);
+            Lock::deleteByObject($entry_dn);
             session::un_set('edit');
 
             /* Remove from session */