diff --git a/src/repdav/textgrid_dav_provider.py b/src/repdav/textgrid_dav_provider.py
index 893f2b3960aee035672e53118c2019da74bc25dd..5f4ad4a14b5069451e5c94b16816fc52fd8fa6c9 100644
--- a/src/repdav/textgrid_dav_provider.py
+++ b/src/repdav/textgrid_dav_provider.py
@@ -1,41 +1,69 @@
+"""The DAV resource provider and its resources.
+"""
+import io
 import logging
 
 from wsgidav.dav_provider import DAVCollection, DAVNonCollection, DAVProvider
-from wsgidav.util import join_uri
+from wsgidav.util import join_uri, pop_path
 
-from .tgapi import TextgridAuth
+
+from .tgapi import TextgridAuth, TextgridCRUD, TextgridSearch
 
 _logger = logging.getLogger(__name__)
 
 
-class TextgridRootCollection(DAVCollection):
-    """top level collection that incorporates so called "projects"
+# TODO think about caching with the session ID as key
+
+class TextgridRoot(DAVCollection):
+    """Top level collection that incorporates Textgrid projects.
 
-    this is implemented as READ-ONLY as project may not be created with webDAV
+    This is implemented as READ-ONLY as projects may not be created with webDAV.
     """
 
-    def __init__(self, environ):
-        DAVCollection.__init__(self, "/", environ)
+    def __init__(self, path, environ):
+        DAVCollection.__init__(self, path, environ)
         self._sid = environ["wsgidav.auth.user_name"]
         _logger.debug("MY SID: %s", self._sid)
-        self._tg_auth = TextgridAuth()
-        self._projects = tuple(self._tg_auth.assigned_projects(self._sid))
-        _logger.debug("MY PROJECTS: %s", self._projects)
+
+    def get_display_info(self):
+        return {"type": "Textgrid root collection"}
 
     def get_member_names(self):
-        _logger.debug("Called get_member_names(self).")
-        return self._projects
+        _logger.debug("Called TextgridRoot.get_member_names(self).")
+        projects = tuple(TextgridAuth().assigned_projects(self._sid))
+        _logger.debug("MY PROJECTS: %s", projects)
+        return projects
 
     def get_member(self, name):
-        _logger.debug("Called get_member(self, name).")
-        if name in self._projects:
-            return TextgridCollection(join_uri(self.path, name), self.environ)
-        return None
-
-
-class TextgridCollection(DAVCollection):
+        _logger.debug("Called TextgridRoot.get_member(self, %s).", name)
+        return TextgridProject(join_uri(self.path, name), self.environ)
+
+    # temporary override for debugging
+    def resolve(self, script_name, path_info):
+        """Return a _DAVResource object for the path (None, if not found).
+        `path_info`: is a URL relative to this object.
+        """
+        _logger.debug("Called TextgridRoot.resolve(self, %s, %s).",
+                      script_name, path_info)
+        if path_info in ("", "/"):
+            return self
+        assert path_info.startswith("/")
+        name, rest = pop_path(path_info)
+        res = self.get_member(name)
+        _logger.debug("TextgridRoot_NAME: %s, REST: %s, RES: %s",
+                      name, rest, res)
+        if res is None or rest in ("", "/"):
+            return res
+        _logger.debug("RES: %s", res)
+        return res.resolve(join_uri(script_name, name), rest)
+
+
+class TextgridProject(DAVCollection):
     def __init__(self, path, environ):
+        _logger.debug(
+            "Called TextgridProject.__init__(self, %s, environ).", path)
         DAVCollection.__init__(self, path, environ)
+        self._sid = environ["wsgidav.auth.user_name"]
 
     def create_empty_resource(self, name):
         pass
@@ -43,28 +71,137 @@ class TextgridCollection(DAVCollection):
     def create_collection(self, name):
         pass
 
+    def get_display_info(self):
+        return {"type": "Textgrid project"}
+
     def get_member_names(self):
+        _logger.debug("Called TextgridProject.get_member_names(self).")
+        # names = []
+        # the item keys are textgrid uris but we want the resource titles only
+        # for _, member_dict in self._resources.items():
+        #     names.append(member_dict.get("title"))
+        # return names
+        #
+        # path resolution has to be rewritten before we can work with resource titles
+        resources = TextgridSearch().get_project_contents(
+            self._sid, self.path.split("/")[-1])
+        _logger.debug("RESOURCES: %s", resources)
+        return resources.keys()
+
+    def get_member(self, name):
+        _logger.debug("Called TextgridProject.get_member(self, %s).", name)
+        info = TextgridSearch().info(self._sid, name)
+        _logger.info("INFO: %s", info)
+        if "aggregation" in info[name]["format"]:
+            return TextgridAggregation(join_uri(self.path, name), self.environ, info)
+        return TextgridResource(join_uri(self.path, name), self.environ, info)
+
+    def delete(self):
+        pass
+
+    def copy_move_single(self, dest_path, is_move):
+        pass
+
+    # temporary override for debugging
+    def resolve(self, script_name, path_info):
+        """Return a _DAVResource object for the path (None, if not found).
+        `path_info`: is a URL relative to this object.
+        """
+        _logger.debug(
+            "Called TextgridProject.resolve(self, %s, %s).", script_name, path_info)
+        if path_info in ("", "/"):
+            return self
+        assert path_info.startswith("/")
+        name, rest = pop_path(path_info)
+        res = self.get_member(name)
+        _logger.debug(
+            "TextgridProject_NAME: %s, REST: %s, RES: %s", name, rest, res)
+        if res is None or rest in ("", "/"):
+            return res
+        return res.resolve(join_uri(script_name, name), rest)
+
+# TODO: merge TextgridProject with TextgridAggregation and/or derive from a common base class.
+
+
+class TextgridAggregation(DAVCollection):
+    def __init__(self, path, environ, info):
+        _logger.debug(
+            "Called TextgridAggregation.__init__(self, %s, environ).", path)
+        DAVCollection.__init__(self, path, environ)
+        self._sid = environ["wsgidav.auth.user_name"]
+        self._info = info
+
+    def create_empty_resource(self, name):
+        pass
+
+    def create_collection(self, name):
         pass
 
+    def get_display_info(self):
+        return {"type": "Textgrid aggregation"}
+
+    def get_member_names(self):
+        _logger.debug("Called TextgridAggregation.get_member_names(self).")
+        resources = TextgridSearch().get_aggregation_contents(
+            self._sid, self.path.split("/")[-1])
+        #_logger.debug("RESOURCES: %s", resources)
+        return resources.keys()
+
+    def get_member(self, name):
+        _logger.debug("Called TextgridAggregation.get_member(self, %s).", name)
+        info = TextgridSearch().info(self._sid, name)
+        _logger.info("INFO: %s", info)
+        if "aggregation" in info[name]["format"]:
+            return TextgridAggregation(join_uri(self.path, name), self.environ, info)
+        return TextgridResource(join_uri(self.path, name), self.environ, info)
+
     def delete(self):
         pass
 
     def copy_move_single(self, dest_path, is_move):
         pass
 
+    # temporary override for debugging
+    def resolve(self, script_name, path_info):
+        """Return a _DAVResource object for the path (None, if not found).
+        `path_info`: is a URL relative to this object.
+        """
+        _logger.debug(
+            "Called TextgridAggregation.resolve(self, %s, %s).", script_name, path_info)
+        if path_info in ("", "/"):
+            return self
+        assert path_info.startswith("/")
+        name, rest = pop_path(path_info)
+        res = self.get_member(name)
+        _logger.debug(
+            "TextgridAggregation_NAME: %s, REST: %s, RES: %s", name, rest, res)
+        if res is None or rest in ("", "/"):
+            return res
+        return res.resolve(join_uri(script_name, name), rest)
+
 
 class TextgridResource(DAVNonCollection):
-    def __init__(self, path, environ):
+    """Non-Aggregation resources."""
+
+    def __init__(self, path, environ, info):
+        _logger.debug(
+            "Called TextgridResource.__init__(self, %s, environ).", path)
         DAVNonCollection.__init__(self, path, environ)
+        self._sid = environ["wsgidav.auth.user_name"]
+        self._info = info
 
     def get_content_length(self):
-        pass
+        _logger.debug("Called TextgridResource.get_content_length(self).")
+        # return TextgridCRUD().get_metadata(self._sid, self.path.split("/")[-1])["content-length"]
+        return self._info[self.name]["extent"]
 
     def get_content_type(self):
-        pass
+        _logger.debug("Called TextgridResource.get_content_type(self).")
+        # return TextgridCRUD().get_metadata(self._sid, self.path.split("/")[-1])["content-type"]
+        return self._info[self.name]["format"]
 
     def get_content(self):
-        pass
+        return io.BytesIO(TextgridCRUD().get_data(self._sid, self.path.split("/")[-1]))
 
     def begin_write(self, content_type=None):
         pass
@@ -74,17 +211,16 @@ class TextgridResource(DAVNonCollection):
 # TextgridResourceProvider
 # ============================================================================
 class TextgridResourceProvider(DAVProvider):
-    """
-    DAV provider that serves Textgrid resources
+    """DAV provider that serves Textgrid resources.
     """
 
     def __init__(self):
         super(TextgridResourceProvider, self).__init__()
 
     def get_resource_inst(self, path, environ):
-        _logger.debug("PATH: %s", path)
-        _logger.debug("ENVIRON: %s", environ)
+        _logger.debug(
+            "Called TextgridResourceProvider.get_resource_inst(self, %s, %s).", path, environ)
         self._count_get_resource_inst += 1
-        root = TextgridRootCollection(environ)
+        root = TextgridRoot("/", environ)
         # an instance of _DAVResource (i.e. either DAVCollection or DAVNonCollection)
-        return root.resolve("", path)
+        return root.resolve("/", path)