Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 12213)
@@ -22,4 +22,5 @@
 import grok
 from hashlib import md5
+from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
 from zope.component import queryUtility, getUtility
 from zope.component.interfaces import IFactory
@@ -76,4 +77,17 @@
     filenames = ()
 
+    @property
+    def state(self):
+        state = IWorkflowState(self).getState()
+        return state
+
+    @property
+    def translated_state(self):
+        try:
+            TRANSLATED_STATES = getUtility(
+                IDocumentsUtils).TRANSLATED_DOCUMENT_STATES
+            return TRANSLATED_STATES[self.state]
+        except KeyError:
+            return
     @property
     def customer(self):
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py	(revision 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py	(revision 12213)
@@ -50,5 +50,5 @@
 from waeup.ikoba.authentication import LocalRoleSetEvent
 from waeup.ikoba.tests.test_async import FunctionalAsyncTestCase
-from waeup.ikoba.documents.workflow import VERIFIED
+from waeup.ikoba.interfaces import VERIFIED
 from waeup.ikoba.browser.tests.test_pdf import samples_dir
 
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/workflow.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/workflow.py	(revision 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/workflow.py	(revision 12213)
@@ -33,7 +33,6 @@
 from waeup.ikoba.customers.interfaces import (
     ICustomer, ICustomersUtils,
-    IContract)
+    IContract, ICustomerDocument)
 from waeup.ikoba.utils.helpers import get_current_principal
-from waeup.ikoba.documents.workflow import VERIFICATION_TRANSITIONS
 
 # Customer workflow
@@ -214,6 +213,6 @@
     )
 
-
 contract_workflow = IkobaWorkflow(CONTRACT_TRANSITIONS)
+
 
 class ContractWorkflowState(WorkflowState, grok.Adapter):
@@ -225,4 +224,5 @@
     state_key = 'wf.contract.state'
     state_id = 'wf.contract.id'
+
 
 class ContractWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
@@ -258,2 +258,118 @@
         pass
     return
+
+# Customer document workflow
+
+VERIFICATION_TRANSITIONS = (
+    Transition(
+        transition_id = 'create',
+        title = _('Create document'),
+        source = None,
+        condition = NullCondition,
+        msg = _('Document created'),
+        destination = CREATED),
+
+    Transition(
+        transition_id = 'submit',
+        title = _('Submit for verification'),
+        msg = _('Submitted for verification'),
+        source = CREATED,
+        destination = SUBMITTED),
+
+    Transition(
+        transition_id = 'verify',
+        title = _('Verify'),
+        msg = _('Verified'),
+        source = SUBMITTED,
+        destination = VERIFIED),
+
+    Transition(
+        transition_id = 'reject',
+        title = _('Reject'),
+        msg = _('REJECTED'),
+        source = SUBMITTED,
+        destination = REJECTED),
+
+    Transition(
+        transition_id = 'reset1',
+        title = _('Reset to initial state'),
+        msg = _('Reset to initial state'),
+        source = REJECTED,
+        destination = CREATED),
+
+    Transition(
+        transition_id = 'reset2',
+        title = _('Reset to initial state'),
+        msg = _('Reset to initial state'),
+        source = VERIFIED,
+        destination = CREATED),
+
+    Transition(
+        transition_id = 'reset3',
+        title = _('Reset to initial state'),
+        msg = _('Reset to initial state'),
+        source = SUBMITTED,
+        destination = CREATED),
+
+    Transition(
+        transition_id = 'expire',
+        title = _('Set to expired'),
+        msg = _('Set to expired'),
+        source = VERIFIED,
+        destination = EXPIRED),
+
+    Transition(
+        transition_id = 'reset4',
+        title = _('Reset to initial state'),
+        msg = _('Reset to initial state'),
+        source = EXPIRED,
+        destination = CREATED),
+    )
+
+verification_workflow = IkobaWorkflow(VERIFICATION_TRANSITIONS)
+
+
+class VerificationWorkflowState(WorkflowState, grok.Adapter):
+    """An adapter to adapt CustomerDocument objects to workflow states.
+    """
+    grok.context(ICustomerDocument)
+    grok.provides(IWorkflowState)
+
+    state_key = 'wf.verification.state'
+    state_id = 'wf.verification.id'
+
+
+class VerificationWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
+    """Adapter to adapt CustomerDocument objects to workflow info objects.
+    """
+    grok.context(ICustomerDocument)
+    grok.provides(IIkobaWorkflowInfo)
+
+    def __init__(self, context):
+        self.context = context
+        self.wf = verification_workflow
+
+@grok.subscribe(ICustomerDocument, IWorkflowTransitionEvent)
+def handle_customer_document_transition_event(obj, event):
+    """Append message to document history and log file.
+
+    Undo the verification of document and raise an exception if document
+    does not meet the requirements for verification.
+    """
+    if event.transition.destination == VERIFIED:
+        verifiable, error = obj.is_verifiable
+        if not verifiable:
+            # Undo transition and raise an exception.
+            IWorkflowState(obj).setState(event.transition.source)
+            raise InvalidTransitionError(error)
+    if event.transition.transition_id == 'verify':
+        obj.setMD5()
+    msg = event.transition.user_data['msg']
+    history = IObjectHistory(obj)
+    history.addMessage(msg)
+    try:
+        customers_container = grok.getSite()['customers']
+        customers_container.logger.info('%s - %s' % (obj.customer_id,msg))
+    except (TypeError, AttributeError):
+        pass
+    return
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/browser.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/browser.py	(revision 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/browser.py	(revision 12213)
@@ -125,5 +125,5 @@
     @action(_('Create document'), style='primary')
     def addDocument(self, **data):
-        document = createObject(u'waeup.Document')
+        document = createObject(u'waeup.PublicDocument')
         self.applyData(document, **data)
         try:
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/container.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/container.py	(revision 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/container.py	(revision 12213)
@@ -24,6 +24,7 @@
 from waeup.ikoba.documents.interfaces import IDocumentsContainer, IDocument
 from waeup.ikoba.utils.helpers import attrs_to_fields
+from waeup.ikoba.utils.logger import Logger
 
-class DocumentsContainer(grok.Container):
+class DocumentsContainer(grok.Container, Logger):
     """This is a container for all kind of documents.
     """
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py	(revision 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py	(revision 12213)
@@ -38,5 +38,5 @@
 from waeup.ikoba.utils.helpers import attrs_to_fields, get_current_principal
 from waeup.ikoba.documents.interfaces import (
-    IDocument, IDocument, IDocumentsUtils,
+    IDocument, IPublicDocument, IDocumentsUtils,
     IPDFDocument, IHTMLDocument)
 from waeup.ikoba.documents.utils import generate_document_id
@@ -54,4 +54,6 @@
 
     user_id = None
+    state = None
+    translated_state = None
 
     def __init__(self):
@@ -70,4 +72,36 @@
 
     @property
+    def class_name(self):
+        return self.__class__.__name__
+
+    @property
+    def formatted_transition_date(self):
+        try:
+            return self.history.messages[-1].split(' - ')[0]
+        except IndexError:
+            return
+
+    @property
+    def connected_files(self):
+        return
+
+    @property
+    def is_verifiable(self):
+        return True, None
+
+    def setMD5(self):
+        """Determine md5 checksum of all files and store checksums as
+        document attributes.
+        """
+        return
+        
+class PublicDocumentBase(Document):
+    """This is a customer document baseclass.
+    """
+    grok.implements(IPublicDocument)
+    grok.provides(IPublicDocument)
+    grok.baseclass()
+
+    @property
     def state(self):
         state = IWorkflowState(self).getState()
@@ -83,33 +117,6 @@
             return
 
-    @property
-    def class_name(self):
-        return self.__class__.__name__
-
-    @property
-    def formatted_transition_date(self):
-        try:
-            return self.history.messages[-1].split(' - ')[0]
-        except IndexError:
-            return
-
-    @property
-    def connected_files(self):
-        return
-
-    @property
-    def is_verifiable(self):
-        return True, None
-
-    def setMD5(self):
-        """Determine md5 checksum of all files and store checksums as
-        document attributes.
-        """
-        return
-        
-Document = attrs_to_fields(Document)
-
-
-class PDFDocument(Document):
+
+class PDFDocument(PublicDocumentBase):
     """This is a  document for a single pdf upload file.
     """
@@ -120,5 +127,5 @@
 
 
-class HTMLDocument(Document):
+class HTMLDocument(PublicDocumentBase):
     """This is a  document to render html-coded text.
     """
@@ -133,13 +140,13 @@
     """
     grok.implements(IFactory)
-    grok.name(u'waeup.Document')
-    title = u"Create a new document.",
-    description = u"This factory instantiates new documents."
+    grok.name(u'waeup.PublicDocument')
+    title = u"Create a new public document.",
+    description = u"This factory instantiates new public documents."
 
     def __call__(self, *args, **kw):
-        return Document(*args, **kw)
+        return PublicDocument(*args, **kw)
 
     def getInterfaces(self):
-        return implementedBy(Document)
+        return implementedBy(PublicDocument)
 
 
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/interfaces.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/interfaces.py	(revision 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/interfaces.py	(revision 12213)
@@ -59,5 +59,11 @@
         """
 
-class IPDFDocument(IDocument):
+class IPublicDocument(IDocument):
+    """A base representation of public documents.
+
+    """
+
+
+class IPDFDocument(IPublicDocument):
     """A base representation of PDF documents.
 
@@ -65,5 +71,5 @@
 
 
-class IHTMLDocument(IDocument):
+class IHTMLDocument(IPublicDocument):
     """A base representation of HTML documents.
 
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 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/tests/test_document.py	(revision 12213)
@@ -30,8 +30,8 @@
 from waeup.ikoba.imagestorage import DefaultStorage
 from waeup.ikoba.documents.interfaces import (
-    IDocumentsContainer, IDocument, IPDFDocument, IHTMLDocument)
+    IDocumentsContainer, IPublicDocument, IPDFDocument, IHTMLDocument)
 from waeup.ikoba.documents.container import DocumentsContainer
 from waeup.ikoba.documents.document import (
-    Document, PDFDocument, HTMLDocument,
+    PDFDocument, HTMLDocument,
     DocumentFileNameChooser, DocumentFileStoreHandler)
 from waeup.ikoba.testing import (FunctionalLayer, FunctionalTestCase)
@@ -53,13 +53,4 @@
         self.assertTrue(
             verifyClass(
-                IDocument, Document)
-            )
-        self.assertTrue(
-            verifyObject(
-                IDocument, Document())
-            )
-
-        self.assertTrue(
-            verifyClass(
                 IPDFDocument, PDFDocument)
             )
@@ -68,5 +59,4 @@
                 IPDFDocument, PDFDocument())
             )
-
         self.assertTrue(
             verifyClass(
@@ -93,17 +83,4 @@
         IWorkflowInfo(document).fireTransition('create')
         self.assertEqual(IWorkflowState(document).getState(), 'created')
-        IWorkflowInfo(document).fireTransition('submit')
-        self.assertEqual(IWorkflowState(document).getState(), 'submitted')
-        IWorkflowInfo(document).fireTransition('verify')
-        self.assertEqual(IWorkflowState(document).getState(), 'verified')
-        IWorkflowInfo(document).fireTransition('reset2')
-        self.assertEqual(IWorkflowState(document).getState(), 'created')
-        self.assertRaises(InvalidTransitionError,
-            IWorkflowInfo(document).fireTransition, 'verify')
-        IWorkflowInfo(document).fireTransition('submit')
-        IWorkflowInfo(document).fireTransition('reset3')
-        self.assertEqual(IWorkflowState(document).getState(), 'created')
-        IWorkflowInfo(document).fireTransition('publish')
-        self.assertEqual(IWorkflowState(document).getState(), 'published')
         return
 
@@ -166,5 +143,5 @@
     def test_name_chooser_available(self):
         # we can get a name chooser for document objects as adapter
-        doc = Document()
+        doc = PDFDocument()
         chooser = IFileStoreNameChooser(doc)
         self.assertTrue(chooser is not None)
@@ -173,5 +150,5 @@
     def test_name_chooser_document(self):
         # we can get an image filename for documents not in a container
-        doc = Document()
+        doc = PDFDocument()
         chooser = IFileStoreNameChooser(doc)
         result = chooser.chooseName('sample.jpg')
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/workflow.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/workflow.py	(revision 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/workflow.py	(revision 12213)
@@ -19,5 +19,4 @@
 """
 import grok
-from datetime import datetime
 from zope.component import getUtility
 from hurry.workflow.workflow import Transition, WorkflowState, NullCondition
@@ -27,11 +26,11 @@
     IObjectHistory, IIkobaWorkflowInfo,
     SimpleIkobaVocabulary,
-    CREATED, SUBMITTED, VERIFIED, REJECTED, EXPIRED, PUBLISHED)
+    CREATED, PUBLISHED)
 from waeup.ikoba.interfaces import MessageFactory as _
 from waeup.ikoba.workflow import IkobaWorkflow, IkobaWorkflowInfo
-from waeup.ikoba.utils.helpers import get_current_principal
-from waeup.ikoba.documents.interfaces import IDocument
+from waeup.ikoba.documents.interfaces import IPublicDocument
 
-VERIFICATION_TRANSITIONS = (
+
+PUBLISHING_TRANSITIONS = (
     Transition(
         transition_id = 'create',
@@ -41,112 +40,39 @@
         msg = _('Document created'),
         destination = CREATED),
-
-    Transition(
-        transition_id = 'submit',
-        title = _('Submit for verification'),
-        msg = _('Submitted for verification'),
-        source = CREATED,
-        destination = SUBMITTED),
-
-    Transition(
-        transition_id = 'verify',
-        title = _('Verify'),
-        msg = _('Verified'),
-        source = SUBMITTED,
-        destination = VERIFIED),
-
-    Transition(
-        transition_id = 'reject',
-        title = _('Reject'),
-        msg = _('REJECTED'),
-        source = SUBMITTED,
-        destination = REJECTED),
-
-    Transition(
-        transition_id = 'reset1',
-        title = _('Reset to initial state'),
-        msg = _('Reset to initial state'),
-        source = REJECTED,
-        destination = CREATED),
-
-    Transition(
-        transition_id = 'reset2',
-        title = _('Reset to initial state'),
-        msg = _('Reset to initial state'),
-        source = VERIFIED,
-        destination = CREATED),
-
-    Transition(
-        transition_id = 'reset3',
-        title = _('Reset to initial state'),
-        msg = _('Reset to initial state'),
-        source = SUBMITTED,
-        destination = CREATED),
-
-    Transition(
-        transition_id = 'expire',
-        title = _('Set to expired'),
-        msg = _('Set to expired'),
-        source = VERIFIED,
-        destination = EXPIRED),
-
-    Transition(
-        transition_id = 'reset4',
-        title = _('Reset to initial state'),
-        msg = _('Reset to initial state'),
-        source = EXPIRED,
-        destination = CREATED),
-
-    Transition(
-        transition_id = 'publish',
-        title = _('Publish'),
-        msg = _('Published'),
-        source = CREATED,
-        destination = PUBLISHED),
     )
 
+publishing_workflow = IkobaWorkflow(PUBLISHING_TRANSITIONS)
 
-verification_workflow = IkobaWorkflow(VERIFICATION_TRANSITIONS)
-
-class VerificationWorkflowState(WorkflowState, grok.Adapter):
+class PublishingWorkflowState(WorkflowState, grok.Adapter):
     """An adapter to adapt Document objects to workflow states.
     """
-    grok.context(IDocument)
+    grok.context(IPublicDocument)
     grok.provides(IWorkflowState)
 
-    state_key = 'wf.verification.state'
-    state_id = 'wf.verification.id'
+    state_key = 'wf.publishing.state'
+    state_id = 'wf.publishing.id'
 
-class VerificationWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
-    """Adapter to adapt Document objects to workflow info objects.
+
+class PublishingWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
+    """Adapter to adapt CustomerDocument objects to workflow info objects.
     """
-    grok.context(IDocument)
+    grok.context(IPublicDocument)
     grok.provides(IIkobaWorkflowInfo)
 
     def __init__(self, context):
         self.context = context
-        self.wf = verification_workflow
+        self.wf = publishing_workflow
 
-@grok.subscribe(IDocument, IWorkflowTransitionEvent)
-def handle_document_transition_event(obj, event):
+
+@grok.subscribe(IPublicDocument, IWorkflowTransitionEvent)
+def handle_public_document_transition_event(obj, event):
     """Append message to document history and log file.
 
-    Undo the verification of document and raise an exception if document
-    does not meet the requirements for verification.
     """
-    if event.transition.destination == VERIFIED:
-        verifiable, error = obj.is_verifiable
-        if not verifiable:
-            # Undo transition and raise an exception.
-            IWorkflowState(obj).setState(event.transition.source)
-            raise InvalidTransitionError(error)
-    if event.transition.transition_id == 'verify':
-        obj.setMD5()
     msg = event.transition.user_data['msg']
     history = IObjectHistory(obj)
     history.addMessage(msg)
     try:
-        customers_container = grok.getSite()['customers']
-        customers_container.logger.info('%s - %s' % (obj.customer_id,msg))
+        grok.getSite().logger.info('%s' % msg)
     except (TypeError, AttributeError):
         pass
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/products/container.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/products/container.py	(revision 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/products/container.py	(revision 12213)
@@ -23,6 +23,7 @@
 from waeup.ikoba.products.interfaces import IProductsContainer, IProduct
 from waeup.ikoba.utils.helpers import attrs_to_fields
+from waeup.ikoba.utils.logger import Logger
 
-class ProductsContainer(grok.Container):
+class ProductsContainer(grok.Container, Logger):
     """This is a container for all kind of products.
     """
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/products/tests/test_browser.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/products/tests/test_browser.py	(revision 12212)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/products/tests/test_browser.py	(revision 12213)
@@ -41,5 +41,4 @@
 from waeup.ikoba.authentication import LocalRoleSetEvent
 from waeup.ikoba.tests.test_async import FunctionalAsyncTestCase
-from waeup.ikoba.documents.workflow import VERIFIED
 from waeup.ikoba.browser.tests.test_pdf import samples_dir
 
