diff --git a/README.md b/README.md
index 602df33cecfc8ec37fd791e3257221e66f120cb3..7215591a4f31f1946029442de291eb9ccf9beea1 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ By participating, you are expected to uphold our [Code of Conduct](https://gitla
 * If you have a suggestion for the [documentation](https://docs.indiscale.com/caosdb-pylib/),
 the preferred way is also a merge request as describe above (the documentation resides in `src/doc`).
 However, you can also create an issue for it. 
-* You can also contact us at **info (AT) caosdb.de** and join the
+* You can also contact us at **info (AT) caosdb.org** and join the
   CaosDB community on
   [#caosdb:matrix.org](https://matrix.to/#/!unwwlTfOznjEnMMXxf:matrix.org).
 
diff --git a/README_SETUP.md b/README_SETUP.md
index 48928d6c3f2c878a8d8b268b36ed2cdeba7f8014..01eea85188078ae6f2fe226e89e5c227497b4bd0 100644
--- a/README_SETUP.md
+++ b/README_SETUP.md
@@ -109,6 +109,8 @@ Now would be a good time to continue with the [tutorials](tutorials/index).
 - Run a specific test function: e.g. `tox -- unittests/test_schema.py::test_config_files`
 
 ## Documentation ##
+We use sphinx to create the documentation. Docstrings in the code should comply
+with the Googly style (see link below).
 
 Build documentation in `build/` with `make doc`.
 
@@ -118,5 +120,11 @@ Build documentation in `build/` with `make doc`.
 - `sphinx-autoapi`
 - `recommonmark`
 
+### How to contribute ###
+
+- [Google Style Python Docstrings](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
+- [Google Style Python Docstrings 2nd reference](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings)
+- [References to other documentation](https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#role-external)
+
 ### Troubleshooting ###
 If the client is to be executed directly from the `/src` folder, an initial `.\setup.py install --user` must be called.
diff --git a/pytest.ini b/pytest.ini
deleted file mode 100644
index ca6aad829a3e0607292cf69b8b1d4b7f7758993e..0000000000000000000000000000000000000000
--- a/pytest.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[pytest]
-testpaths=unittests
-addopts=-x -vv --cov=caosdb
diff --git a/setup.py b/setup.py
index 35d6d69a83338a0dc543b4b6438b56c1372b7237..fca246ed7004a25e7eedc594a4e7eceb68ee295a 100755
--- a/setup.py
+++ b/setup.py
@@ -174,7 +174,7 @@ def setup_package():
         python_requires='>=3.8',
         package_dir={'': 'src'},
         install_requires=['lxml>=4.6.3',
-                          "requests[socks]>=2.28.1",
+                          "requests[socks]>=2.26",
                           "python-dateutil>=2.8.2",
                           'PyYAML>=5.4.1',
                           'future',
diff --git a/src/caosdb/__init__.py b/src/caosdb/__init__.py
index 7e06885fe495c1e8c4ccc99b7d0c0f8ff8c34b5b..45303cc275042b4eb83ab37f2fd67ce077fa0561 100644
--- a/src/caosdb/__init__.py
+++ b/src/caosdb/__init__.py
@@ -41,11 +41,12 @@ from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, FILE, INTEGER,
 from caosdb.common.state import State, Transition
 # Import of the basic  API classes:
 from caosdb.common.models import (ACL, ALL, FIX, NONE, OBLIGATORY, RECOMMENDED,
-                                  SUGGESTED, Container, DropOffBox, Entity,
+                                  SUGGESTED, Container, Entity,
                                   File, Info, Message, Permissions, Property,
                                   Query, QueryTemplate, Record, RecordType,
                                   delete, execute_query, get_global_acl,
-                                  get_known_permissions, raise_errors)
+                                  get_known_permissions, raise_errors,
+                                  Directory, Link)
 from caosdb.configuration import _read_config_files, configure, get_config
 from caosdb.connection.connection import configure_connection, get_connection
 from caosdb.exceptions import *
diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py
index 05f661c9b6ef2c89027e2a56fe22e6a9d1e59dff..40e49f5a6c6646d6218874a60e29666c4054d3af 100644
--- a/src/caosdb/common/models.py
+++ b/src/caosdb/common/models.py
@@ -51,7 +51,7 @@ from lxml import etree
 from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, INTEGER, TEXT,
                                     is_list_datatype, is_reference)
 from caosdb.common.state import State
-from caosdb.common.utils import uuid, xml2str, experimental
+from caosdb.common.utils import uuid, xml2str
 from caosdb.common.timezone import TimeZone
 from caosdb.common.versioning import Version
 from caosdb.configuration import get_config
@@ -115,6 +115,7 @@ class Entity(object):
         self.parents = _Parents()
         self.path = None
         self.file = None
+        self.link_target = None
         self.unit = None
         self.acl = None
         self.permissions = None
@@ -301,6 +302,30 @@ class Entity(object):
     def path(self, new_path):
         self.__path = new_path
 
+    @property
+    def link_target(self):
+        if self.__link_target is not None or self._wrapped_entity is None:
+            return self.__link_target
+
+        return self._wrapped_entity.link_target
+
+    @link_target.setter
+    def link_target(self, new_link_target):
+        self.__link_target = new_link_target
+
+    @property
+    def thumbnail(self):
+        warn(DeprecationWarning(
+            "The thumbnail feature has been removed from the CaosDB server "
+            "API"))
+        return None
+
+    @thumbnail.setter
+    def thumbnail(self, new_thumbnail):
+        warn(DeprecationWarning(
+            "The thumbnail feature has been removed from the CaosDB server "
+            "API"))
+
     @property
     def file(self):
         if self.__file is not None or self._wrapped_entity is None:
@@ -314,14 +339,16 @@ class Entity(object):
 
     @property
     def pickup(self):
-        if self.__pickup is not None or self._wrapped_entity is None:
-            return self.__pickup
-
-        return self._wrapped_entity.pickup
+        warn(DeprecationWarning(
+            "The drop-off/pickup feature has been removed from the CaosDB "
+            "server API"))
+        return None
 
     @pickup.setter
     def pickup(self, new_pickup):
-        self.__pickup = new_pickup
+        warn(DeprecationWarning(
+            "The drop-off/pickup feature has been removed from the CaosDB "
+            "server API"))
 
     def grant(self, realm=None, username=None, role=None,
               permission=None, priority=False, revoke_denial=True):
@@ -1048,6 +1075,9 @@ class Entity(object):
         if self.file is not None and local_serialization:
             xml.set("file", self.file)
 
+        if self.link_target is not None:
+            xml.set("linktarget", str(self.link_target))
+
         if self.checksum is not None:
             xml.set("checksum", self.checksum)
 
@@ -1102,6 +1132,7 @@ class Entity(object):
         entity.description = elem.get("description")
         entity.path = elem.get("path")
         entity._checksum = elem.get("checksum")
+        entity.link_target = elem.get("linktarget")
         entity._size = elem.get("size")
         entity.datatype = elem.get("datatype")  # @ReservedAssignment
         entity.unit = elem.get("unit")
@@ -1365,7 +1396,7 @@ def _parse_value(datatype, value):
 
     # This is for a special case, where the xml parser could not differentiate
     # between single values and lists with one element. As
-    if hasattr(value, "__len__") and len(value) == 1:
+    if hasattr(value, "__len__") and len(value) == 1 and not isinstance(value, str):
         return _parse_value(datatype, value[0])
 
     # deal with references
@@ -1419,23 +1450,12 @@ class QueryTemplate():
         self.description = description
         self.query = query
         self._cuid = None
-        self.value = None
-        self.datatype = None
         self.messages = _Messages()
-        self.properties = None
-        self.parents = None
-        self.path = None
-        self.file = None
-        self._checksum = None
-        self._size = None
-        self._upload = None
-        self.unit = None
         self.acl = None
         self.permissions = None
         self.is_valid = lambda: False
         self.is_deleted = lambda: False
         self.version = None
-        self.state = None
 
     def retrieve(self, raise_exception_on_error=True, unique=True, sync=True,
                  flags=None):
@@ -1691,9 +1711,7 @@ class Property(Entity):
 
 class Message(object):
 
-    # @ReservedAssignment
-
-    def __init__(self, type, code=None, description=None, body=None):  # @ReservedAssignment
+    def __init__(self, type, code=None, description=None, body=None):
         self.type = type
         self.code = code
         self.description = description
@@ -1804,8 +1822,39 @@ class Record(Entity):
         return Entity.to_xml(self, xml, add_properties=ALL)
 
 
-@experimental("The Directory class is experimental. It should not be used \
-              until the server's file storage API has been refactored.")
+class Link(Entity):
+    """This class represents CaosDB's link entities."""
+
+    def __init__(self, name=None, id=None, description=None, path=None,
+                 target=None):
+        Record.__init__(self, id=id, name=name, description=description)
+        self.role = "Link"
+        self.datatype = None
+        self.link_target = target
+
+        # location in the fileserver
+        self.path = path
+
+    def to_xml(self, xml=None, add_properties=ALL, local_serialization=False):
+        """Convert this Link to an xml element.
+
+        @return: xml element
+        """
+
+        if xml is None:
+            xml = etree.Element("Link")
+
+        return Entity.to_xml(self, xml=xml, add_properties=add_properties,
+                             local_serialization=local_serialization)
+
+    def add_property(self, property=None, id=None, name=None, description=None, datatype=None,
+                     value=None, unit=None, importance=FIX, inheritance=FIX):
+
+        return super().add_property(
+            property=property, id=id, name=name, description=description, datatype=datatype,
+            value=value, unit=unit, importance=importance, inheritance=inheritance)
+
+
 class Directory(Record):
     """This class represents CaosDB's directory entities."""
 
@@ -1822,7 +1871,7 @@ class Directory(Record):
         self.import_file = import_file or recursive_import
 
     def to_xml(self, xml=None, add_properties=ALL, local_serialization=False):
-        """Convert this file to an xml element.
+        """Convert this Directory to an xml element.
 
         @return: xml element
         """
@@ -1845,14 +1894,9 @@ class File(Record):
 
     """This class represents CaosDB's file entities.
 
-    For inserting a new file to the server, `path` gives the new location, and
-    (exactly?) one of `file` and `pickup` should (must?) be given to specify the
-    source of the file.
-
-    Symlinking from the "extroot" file system is not supported by this API yet,
-    it can be done manually using the `InsertFilesInDir` flag.  For sample code,
-    look at `test_files.py` in the Python integration tests of the
-    `load_files.py` script in the advanced user tools.
+    For inserting a new file to the server or updating an existent one.
+    `path` gives the new location. `file` specifies the (local) file which is
+    to be uploaded. It is mandatory for insertions, but optionally for updates.
 
     Parameters
     ----------
@@ -1870,14 +1914,16 @@ class File(Record):
         A local path or python file object.  The file designated by this
         argument will be uploaded to the server via HTTP.
     pickup : str
-        A file/folder in the DropOffBox (the server will move that file into
-        its "caosroot" file system). (DEPRECATED, use import feature)
+        Deprecated.
+    thumbnail : str
+        Deprecated.
     import_file : bool
         Import the file (don't upload it, its already there). Default: `False`
     """
 
     def __init__(self, name=None, id=None, description=None,
-                 path=None, file=None, pickup=None, import_file=False):
+                 path=None, file=None, pickup=None,
+                 thumbnail=None, import_file=False):
         Record.__init__(self, id=id, name=name, description=description)
         self.role = "File"
         self.datatype = None
@@ -1888,9 +1934,18 @@ class File(Record):
         # local file path or pointer to local file
         self.file = file
 
-        self.pickup = pickup
         self.import_file = import_file
 
+        if thumbnail is not None:
+            warn(DeprecationWarning(
+                "The thumbnail feature has been removed from the CaosDB "
+                "server API"))
+
+        if pickup is not None:
+            warn(DeprecationWarning(
+                "The drop-off/pickup feature has been removed from the CaosDB "
+                "server API"))
+
     def to_xml(self, xml=None, add_properties=ALL, local_serialization=False):
         """Convert this file to an xml element.
 
@@ -1942,21 +1997,21 @@ class File(Record):
         return checksum.hexdigest().lower()
 
     @staticmethod
-    def sha512(file):
-        return File._get_checksum_single_file(file)
+    def _get_checksum(file):
+        if hasattr(file, "name"):
+            file_name = file.name
+        else:
+            file_name = file
 
-    @staticmethod
-    def _get_checksum_single_file(single_file):
-        _file = open(single_file, 'rb')
-        data = _file.read(1000)
         checksum = sha512()
-
-        while data:
-            checksum.update(data)
+        with open(file_name, 'rb') as _file:
             data = _file.read(1000)
-        _file.close()
 
-        return checksum.hexdigest()
+            while data:
+                checksum.update(data)
+                data = _file.read(1000)
+
+        return checksum.hexdigest().lower()
 
     def add_property(self, property=None, id=None, name=None, description=None, datatype=None,
                      value=None, unit=None, importance=FIX, inheritance=FIX):
@@ -2499,30 +2554,13 @@ def _basic_sync(e_local, e_remote):
                          "this client did't know about it yet.".format(
                              e_remote.role, e_local.role))
 
-    e_local.id = e_remote.id
-    e_local.name = e_remote.name
-    e_local.description = e_remote.description
-    e_local.path = e_remote.path
-    e_local._checksum = e_remote._checksum
-    e_local._size = e_remote._size
-    e_local.datatype = e_remote.datatype
-    e_local.unit = e_remote.unit
-    e_local.value = e_remote.value
-    e_local.properties = e_remote.properties
-    e_local.parents = e_remote.parents
-    e_local.messages = e_remote.messages
-    e_local.acl = e_remote.acl
-    e_local.permissions = e_remote.permissions
-    e_local.is_valid = e_remote.is_valid
-    e_local.is_deleted = e_remote.is_deleted
-    e_local.version = e_remote.version
-    e_local.state = e_remote.state
-
-    if hasattr(e_remote, "query"):
-        e_local.query = e_remote.query
-
-    if hasattr(e_remote, "affiliation"):
-        e_local.affiliation = e_remote.affiliation
+    for attr in ["path", "link_target", "_checksum", "_size", "datatype",
+                 "unit", "value", "properties", "parents", "messages", "acl",
+                 "permissions", "is_valid", "is_deleted", "version", "state",
+                 "query", "affiliation", "id", "name", "description"]:
+
+        if hasattr(e_remote, attr):
+            setattr(e_local, attr, getattr(e_remote, attr))
 
     return e_local
 
@@ -3006,6 +3044,7 @@ class Container(list):
 
         for local_entity in self:
             if (sync_dict[local_entity] is None
+                    and hasattr(local_entity, "path")
                     and local_entity.path is not None):
                 sync_remote_entities = []
 
@@ -3425,8 +3464,6 @@ class Container(list):
 
             if hasattr(entity, '_upload') and entity._upload is not None:
                 entity_xml.set("upload", entity._upload)
-            elif hasattr(entity, 'pickup') and entity.pickup is not None:
-                entity_xml.set("pickup", entity.pickup)
             elif hasattr(entity, 'import_file') and entity.import_file is True:
                 entity_xml.set("import", "true")
 
@@ -3575,8 +3612,6 @@ class Container(list):
 
             if hasattr(entity, '_upload') and entity._upload is not None:
                 entity_xml.set("upload", entity._upload)
-            elif hasattr(entity, 'pickup') and entity.pickup is not None:
-                entity_xml.set("pickup", entity.pickup)
             elif hasattr(entity, 'recursive_import') and entity.recursive_import is True:
                 entity_xml.set("recursive_import", "true")
             elif hasattr(entity, 'import_file') and entity.import_file is True:
@@ -4292,39 +4327,6 @@ def execute_query(q, unique=False, raise_exception_on_error=True, cache=True, fl
                          cache=cache)
 
 
-class DropOffBox(list):
-
-    path = None
-
-    def sync(self):
-        c = get_connection()
-        _log_request("GET: Info")
-        http_response = c.retrieve(["Info"])
-        body = http_response.read()
-        _log_response(body)
-
-        xml = etree.fromstring(body)
-
-        for child in xml:
-            if child.tag.lower() == "stats":
-                infoelem = child
-
-                break
-
-        for child in infoelem:
-            if child.tag.lower() == "dropoffbox":
-                dropoffboxelem = child
-
-                break
-        del self[:]
-        self.path = dropoffboxelem.get('path')
-
-        for f in dropoffboxelem:
-            self.append(f.get('path'))
-
-        return self
-
-
 class UserInfo():
 
     def __init__(self, xml):
@@ -4451,6 +4453,7 @@ def _parse_single_xml_element(elem):
         'property': Property,
         'file': File,
         'directory': Directory,
+        'link': Link,
         'parent': Parent,
         'entity': Entity}
 
diff --git a/tox.ini b/tox.ini
index 50c22d5716769ef2ec818f6c8fb94491ea372434..3b3371a9424cf6692a7c3c05f9c911ffdd34b957 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,3 +12,8 @@ commands=py.test --cov=caosdb -vv {posargs}
 
 [flake8]
 max-line-length=100
+
+[pytest]
+testpaths = unittests
+xfail_strict = True
+addopts = -x -vv --cov=caosdb
diff --git a/unittests/test_datatype.py b/unittests/test_datatype.py
index 9b3c6267fb018e2cd3085dea568d7396c4549ac8..5e42117581f6e1d21cac9e9a30ed95a0b658d52a 100644
--- a/unittests/test_datatype.py
+++ b/unittests/test_datatype.py
@@ -87,3 +87,9 @@ def test_parsing_of_references():
 
     entity = db.Record(name="bla")
     assert id(_parse_value(dtype, entity)) == id(entity)
+
+
+def test_parsing_of_single_list_item_str_len_one():
+    db.Record().add_property("blub", datatype=db.LIST(db.TEXT), value=["a"])
+    db.Record().add_property("blub", datatype=db.LIST(db.INTEGER), value=[1])
+    db.Record().add_property("blub", datatype=db.LIST("bla"), value=["a"])
diff --git a/unittests/test_file.py b/unittests/test_file.py
index 3c80af7f362a7cdabe0a9ebc89cd2986d04fe242..9abe3dcc41663b38083a284fd0e1a84be00b3c6a 100644
--- a/unittests/test_file.py
+++ b/unittests/test_file.py
@@ -24,14 +24,10 @@
 """Tests for the File class."""
 from caosdb import File, Record, configure_connection
 from caosdb.connection.mockup import MockUpServerConnection
-# pylint: disable=missing-docstring
-from nose.tools import assert_equal as eq
-from nose.tools import assert_is_not_none as there
-from nose.tools import assert_true as tru
 
 
 def setup_module():
-    there(File)
+    assert File is not None
     configure_connection(url="unittests", username="testuser",
                          password_method="plain",
                          password="testpassword", timeout=200,
@@ -39,12 +35,12 @@ def setup_module():
 
 
 def hat(obj, attr):
-    tru(hasattr(obj, attr))
+    assert hasattr(obj, attr)
 
 
 def test_is_record():
     file_ = File()
-    tru(isinstance(file_, Record))
+    assert isinstance(file_, Record)
 
 
 def test_instance_variable():
@@ -57,4 +53,39 @@ def test_instance_variable():
 
 def test_role():
     file_ = File()
-    eq(file_.role, "File")
+    assert file_.role == "File"
+
+
+def test_ticket_237():
+    """This is an ancient test which has been moved here from the integration
+    test suite. It checks the basic functionality of the _wrap(function)"""
+
+    f1 = File(
+        name="name1",
+        path="path1",
+        file="file1")
+    assert f1.name == "name1"
+    assert f1.path == "path1"
+    assert f1.file == "file1"
+
+    f2 = File(name="name2")
+    assert f2.name == "name2"
+    assert f2.path is None
+    assert f2.file is None
+
+    f2._wrap(f1)
+
+    assert f2.name == "name2"
+    assert f2.path == "path1"
+    assert f2.file == "file1"
+
+    f2.path = "path2"
+    f2.file = "file2"
+
+    assert f2.name == "name2"
+    assert f2.path == "path2"
+    assert f2.file == "file2"
+
+    assert f1.name == "name1"
+    assert f1.path == "path1"
+    assert f1.file == "file1"