Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py	(revision 12203)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py	(revision 12204)
@@ -19,4 +19,5 @@
 These are the document tickets.
 """
+import os
 import grok
 from time import time
@@ -28,5 +29,10 @@
 from zope.interface import implementedBy
 from zope.i18n import translate
-from waeup.ikoba.interfaces import IIkobaUtils, IObjectHistory
+
+from waeup.ikoba.image import IkobaImageFile
+from waeup.ikoba.imagestorage import DefaultFileStoreHandler
+from waeup.ikoba.interfaces import (
+    IIkobaUtils, IObjectHistory,
+    IFileStoreNameChooser, IFileStoreHandler, IExtFileStore)
 from waeup.ikoba.interfaces import MessageFactory as _
 from waeup.ikoba.utils.helpers import attrs_to_fields, get_current_principal
@@ -163,4 +169,102 @@
         return implementedBy(HTMLDocument)
 
+#: The file id marker for files
+DOCUMENT_FILE_STORE_NAME = 'file-document'
+
+
+class DocumentFileNameChooser(grok.Adapter):
+    """A file id chooser for :class:`Document` objects.
+
+    `context` is an :class:`Document` instance.
+
+    The delivered file_id contains the file id marker for
+    :class:`Document` objects in the central :class:`DocumentsContainer`.
+
+    This chooser is registered as an adapter providing
+    :class:`waeup.ikoba.interfaces.IFileStoreNameChooser`.
+
+    File store name choosers like this one are only convenience
+    components to ease the task of creating file ids for customer document
+    objects. You are nevertheless encouraged to use them instead of
+    manually setting up filenames for customer documents.
+
+    .. seealso:: :mod:`waeup.ikoba.imagestorage`
+
+    """
+
+    grok.context(IDocument)
+    grok.implements(IFileStoreNameChooser)
+
+    def checkName(self, name=None, attr=None):
+        """Check whether the given name is a valid file id for the context.
+
+        Returns ``True`` only if `name` equals the result of
+        :meth:`chooseName`.
+
+        """
+        return name == self.chooseName()
+
+    def chooseName(self, attr, name=None):
+        """Get a valid file id for customer document context.
+
+        *Example:*
+
+        For document with id 'd123'
+        with attr ``'nice_image.jpeg'`` stored in
+        the documents container this chooser would create:
+
+          ``'__file-document__documents/nice_image_d123.jpeg'``
+
+        meaning that the nice image of this document would be
+        stored in the site-wide file storage in path:
+
+          ``documents/nice_image_d123.jpeg``
+
+        """
+        basename, ext = os.path.splitext(attr)
+        doc_id = self.context.document_id
+        marked_filename = '__%s__documents/%s_%s%s' % (
+            DOCUMENT_FILE_STORE_NAME,
+            basename, doc_id, ext)
+        return marked_filename
+
+
+class DocumentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
+    """ Document specific file handling.
+
+    This handler knows in which path in a filestore to store document
+    files and how to turn this kind of data into some (browsable)
+    file object.
+
+    It is called from the global file storage, when it wants to
+    get/store a file with a file id starting with
+    ``__file-document__`` (the marker string for customer files).
+
+    Like each other file store handler it does not handle the files
+    really (this is done by the global file store) but only computes
+    paths and things like this.
+    """
+    grok.implements(IFileStoreHandler)
+    grok.name(DOCUMENT_FILE_STORE_NAME)
+
+    def pathFromFileID(self, store, root, file_id):
+        """All document files are put in directory ``documents``.
+        """
+        marker, filename, basename, ext = store.extractMarker(file_id)
+        sub_root = os.path.join(root, 'documents')
+        return super(DocumentFileStoreHandler, self).pathFromFileID(
+            store, sub_root, basename)
+
+    def createFile(self, store, root, filename, file_id, file):
+        """Create a browsable file-like object.
+        """
+        # call super method to ensure that any old files with
+        # different filename extension are deleted.
+        file, path, file_obj = super(
+            DocumentFileStoreHandler, self).createFile(
+            store, root,  filename, file_id, file)
+        return file, path, IkobaImageFile(
+            file_obj.filename, file_obj.data)
+
 
 @grok.subscribe(IDocument, grok.IObjectAddedEvent)
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/tests/test_document.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/tests/test_document.py	(revision 12203)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/tests/test_document.py	(revision 12204)
@@ -19,14 +19,20 @@
 Tests for documents.
 """
+import shutil
+import tempfile
+from StringIO import StringIO
 from zope.interface.verify import verifyClass, verifyObject
-from zope.component import createObject
+from zope.component import createObject, queryUtility
 from hurry.workflow.interfaces import (
     IWorkflowInfo, IWorkflowState, InvalidTransitionError)
+from waeup.ikoba.interfaces import (
+    IObjectHistory, IFileStoreNameChooser, IFileStoreHandler)
+from waeup.ikoba.imagestorage import DefaultStorage
 from waeup.ikoba.documents.interfaces import (
     IDocumentsContainer, IDocument, IPDFDocument, IHTMLDocument)
-from waeup.ikoba.interfaces import IObjectHistory
 from waeup.ikoba.documents.container import DocumentsContainer
 from waeup.ikoba.documents.document import (
-    Document, PDFDocument, HTMLDocument)
+    Document, PDFDocument, HTMLDocument,
+    DocumentFileNameChooser, DocumentFileStoreHandler)
 from waeup.ikoba.testing import (FunctionalLayer, FunctionalTestCase)
 
@@ -108,2 +114,68 @@
         self.assertTrue('Document created by system' in messages)
         return
+
+
+class DocumentFileTests(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(DocumentFileTests, self).setUp()
+        self.workdir = tempfile.mkdtemp()
+        return
+
+    def tearDown(self):
+        super(DocumentFileTests, self).tearDown()
+        shutil.rmtree(self.workdir)
+        return
+
+    def test_file_store_handler_util_accessible(self):
+        # we can get an IFileStoreHandler utility for documents
+        handler = queryUtility(IFileStoreHandler, name='file-document')
+        self.assertTrue(
+            isinstance(handler, DocumentFileStoreHandler))
+        return
+
+    def test_file_store_handler(self):
+        store = DefaultStorage()
+        handler = queryUtility(IFileStoreHandler, name='file-document')
+        result = handler.pathFromFileID(
+            store, '/fake-root', '__any_marker__sample.jpg')
+        self.assertEqual(
+            result, '/fake-root/documents/sample')
+        return
+
+    def test_file_store_handler_create(self):
+        # we can create files in image store with the document file
+        # store handler
+        store = DefaultStorage(self.workdir)
+        handler = queryUtility(IFileStoreHandler, name='file-document')
+        file_obj, path, waeup_file = handler.createFile(
+            store, store.root, 'sample.jpg', '__file_document__sample',
+            StringIO('I am a JPG file'))
+        self.assertEqual(path[-20:], 'documents/sample.jpg')
+        return
+
+    def test_iface(self):
+        # make sure we implement promised interfaces
+        obj = DocumentFileNameChooser(None) # needs a context normally
+        verifyClass(IFileStoreNameChooser, DocumentFileNameChooser)
+        verifyObject(IFileStoreNameChooser, obj)
+        return
+
+    def test_name_chooser_available(self):
+        # we can get a name chooser for document objects as adapter
+        doc = Document()
+        chooser = IFileStoreNameChooser(doc)
+        self.assertTrue(chooser is not None)
+        return
+
+    def test_name_chooser_document(self):
+        # we can get an image filename for documents not in a container
+        doc = Document()
+        chooser = IFileStoreNameChooser(doc)
+        result = chooser.chooseName('sample.jpg')
+        # the file would be stored in a ``_default`` directory.
+        self.assertEqual(
+            result, '__file-document__documents/sample_d123.jpg')
+        return
