diff --git a/product/ERP5/Document/Document.py b/product/ERP5/Document/Document.py
index de9eb7281017dbf82e3eeeeda9d15fddfa9a2c2c..7aa5be9dff90f67b8db4a716a3d5f5bf1cad4cf3 100644
--- a/product/ERP5/Document/Document.py
+++ b/product/ERP5/Document/Document.py
@@ -59,6 +59,7 @@ from Products.ERP5.mixin.cached_convertable import CachedConvertableMixin
 from Products.ERP5.mixin.text_convertable import TextConvertableMixin
 from Products.ERP5.mixin.downloadable import DownloadableMixin
 from Products.ERP5.mixin.document import DocumentMixin
+from Products.ERP5.mixin.extensible_traversable import DocumentExtensibleTraversableMixIn
 
 _MARKER = []
 VALID_ORDER_KEY_LIST = ('user_login', 'content', 'file_name', 'input')
@@ -128,129 +129,6 @@ class DocumentProxyError(Exception):pass
 class NotConvertedError(Exception):pass
 allow_class(NotConvertedError)
 
-class PermanentURLMixIn(ExtensibleTraversableMixIn):
-  """
-    Provides access to documents through their permanent URL.
-    This class must be inherited by all document classes so
-    that documents displayed outside a Web Site / Web Section
-    can also use the permanent URL principle.
-  """
-
-  # Declarative security
-  security = ClassSecurityInfo()
-
-  def _forceIdentification(self, request):
-    # force identification (usable for extensible content)
-    cache = getReadOnlyTransactionCache(self)
-    if cache is not None:
-      key = ('__bobo_traverse__', self, 'user')
-      try:
-        user = cache[key]
-      except KeyError:
-        user = _MARKER
-    else:
-      user = _MARKER
-    old_user = getSecurityManager().getUser()
-    if user is _MARKER:
-      user = None # By default, do nothing
-      if old_user is None or old_user.getUserName() == 'Anonymous User':
-        user_folder = getattr(self.getPortalObject(), 'acl_users', None)
-        if user_folder is not None:
-          try:
-            if request.get('PUBLISHED', _MARKER) is _MARKER:
-              # request['PUBLISHED'] is required by validate
-              request['PUBLISHED'] = self
-              has_published = False
-            else:
-              has_published = True
-            try:
-              user = user_folder.validate(request)
-            except AttributeError:
-              # This kind of error happens with unrestrictedTraverse,
-              # because the request object is a fake, and it is just
-              # a dict object.
-              user = None
-            if not has_published:
-              try:
-                del request.other['PUBLISHED']
-              except AttributeError:
-                # The same here as above. unrestrictedTraverse provides
-                # just a plain dict, so request.other does not exist.
-                del request['PUBLISHED']
-          except:
-            LOG("ERP5 WARNING",0,
-                "Failed to retrieve user in __bobo_traverse__ of WebSection %s" % self.getPath(),
-                error=sys.exc_info())
-            user = None
-      if user is not None and user.getUserName() == 'Anonymous User':
-        user = None # If the user which is connected is anonymous,
-                    # do not try to change SecurityManager
-      if cache is not None:
-        cache[key] = user
-
-    old_manager = None
-    if user is not None:
-      # We need to perform identification
-      old_manager = getSecurityManager()
-      newSecurityManager(get_request(), user)
-
-    return old_manager, user
-
-  ### Extensible content
-  def _getExtensibleContent(self, request, name):
-    # Permanent URL traversal
-    old_manager, user = self._forceIdentification(request)
-    # Next get the document per name
-    portal = self.getPortalObject()
-    document = self.getDocumentValue(name=name, portal=portal)
-    # restore original security context if there's a logged in user
-    if user is not None:
-      setSecurityManager(old_manager)
-    if document is not None:
-      document = aq_base(document.asContext(id=name, # Hide some properties to permit locating the original
-                                            original_container=document.getParentValue(),
-                                            original_id=document.getId(),
-                                            editable_absolute_url=document.absolute_url()))
-      return document.__of__(self)
-
-    # no document found for current user, still such document may exists
-    # in some cases user (like Anonymous) can not view document according to portal catalog
-    # but we may ask him to login if such a document exists
-    isAuthorizationForced = getattr(self, 'isAuthorizationForced', None)
-    if isAuthorizationForced is not None and isAuthorizationForced():
-      if unrestricted_apply(self.getDocumentValue, (name, portal)) is not None:
-        # force user to login as specified in Web Section
-        raise Unauthorized
-
-  security.declareProtected(Permissions.View, 'getDocumentValue')
-  def getDocumentValue(self, name=None, portal=None, **kw):
-    """
-      Return the default document with the given
-      name. The name parameter may represent anything
-      such as a document reference, an identifier,
-      etc.
-
-      If name is not provided, the method defaults
-      to returning the default document by calling
-      getDefaultDocumentValue.
-
-      kw parameters can be useful to filter content
-      (ex. force a given validation state)
-
-      This method must be implemented through a
-      portal type dependent script:
-        WebSection_getDocumentValue
-    """
-    if name is None:
-      return self.getDefaultDocumentValue()
-
-    method = self._getTypeBasedMethod('getDocumentValue',
-              fallback_script_id='WebSection_getDocumentValue')
-
-    document = method(name, portal=portal, **kw)
-    if document is not None:
-      return document.__of__(self)
-
 class DocumentProxyMixin:
   """
   Provides access to documents referenced by the follow_up field
@@ -329,7 +207,7 @@ class UpdateMixIn:
     return method()
 
 
-class Document(PermanentURLMixIn, XMLObject, UrlMixIn, CachedConvertableMixin,
+class Document(DocumentExtensibleTraversableMixIn, XMLObject, UrlMixIn, CachedConvertableMixin,
                SnapshotMixin, UpdateMixIn, TextConvertableMixin,
                DownloadableMixin, DocumentMixin):
   """Document is an abstract class with all methods related to document
diff --git a/product/ERP5/Document/WebSection.py b/product/ERP5/Document/WebSection.py
index c1083cba5e8075dc46a843c24fd9042ac3601b7a..9501cbd9a7b1f1c2f2c5942a45c8bc5e25c7eaec 100644
--- a/product/ERP5/Document/WebSection.py
+++ b/product/ERP5/Document/WebSection.py
@@ -30,8 +30,8 @@
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet
 from Products.ERP5.Document.Domain import Domain
-from Products.ERP5.Document.Document import PermanentURLMixIn
-from Acquisition import aq_base, aq_inner
+from Products.ERP5.mixin.extensible_traversable import DocumentExtensibleTraversableMixIn as PermanentURLMixIn
+from Acquisition import aq_base, aq_inner 
 from Products.ERP5Type.UnrestrictedMethod import unrestricted_apply
 from AccessControl import Unauthorized
 from OFS.Traversable import NotFound
diff --git a/product/ERP5/interfaces/extensible_traversable.py b/product/ERP5/interfaces/extensible_traversable.py
new file mode 100644
index 0000000000000000000000000000000000000000..2afc8ac970ff2248e597920106a50330f4037ebf
--- /dev/null
+++ b/product/ERP5/interfaces/extensible_traversable.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
+#                    Ivan Tyagov <ivan@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+
+from zope.interface import Interface
+
+class ILegacyExtensibleTraversable(Interface):
+  """
+  Extensible Traversable legacy interface specification
+  """
+  
+  def _getExtensibleContent(request, name):
+    """
+    Return extensible subcontent of context document during traversal.
+    """
+
+class IExtensibleTraversable(ILegacyExtensibleTraversable):
+  """
+  Extensible Traversable interface specification
+
+  IExtensibleTraversable provides methods so a document may become a container for extensible content 
+  during traversal.
+  """
+
+  def getExtensibleContent(request, name):
+    """
+    Return extensible subcontent of context document during traversal.
+    """
\ No newline at end of file
diff --git a/product/ERP5/mixin/extensible_traversable.py b/product/ERP5/mixin/extensible_traversable.py
new file mode 100644
index 0000000000000000000000000000000000000000..02879dcec214fb4c47a952721f662f5257ca16ed
--- /dev/null
+++ b/product/ERP5/mixin/extensible_traversable.py
@@ -0,0 +1,208 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
+#                    Ivan Tyagov <ivan@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+
+from zLOG import LOG
+from Acquisition import aq_base
+from Products.ERP5Type.Globals import get_request
+from AccessControl import Unauthorized
+from Products.ERP5Type.ExtensibleTraversable import ExtensibleTraversableMixIn
+from Products.ERP5Type.Cache import getReadOnlyTransactionCache
+from AccessControl import ClassSecurityInfo, getSecurityManager
+from AccessControl.SecurityManagement import newSecurityManager, setSecurityManager
+from Products.ERP5Type import Permissions
+from Products.CMFCore.utils import getToolByName, _setCacheHeaders, _ViewEmulator
+from OFS.Image import File as OFSFile
+from warnings import warn
+
+
+# XXX: these duplicate ones in ERP5.Document
+_MARKER = []
+EMBEDDED_FORMAT = '_embedded'
+class ConversionError(Exception):pass
+class DocumentProxyError(Exception):pass
+class NotConvertedError(Exception):pass
+
+class BaseExtensibleTraversableMixIn(ExtensibleTraversableMixIn):
+  """
+  This class provides a generic base mixin implementation of IExtensibleTraversable.
+
+  Provides access to documents through their permanent URL.
+  This class shoulf be used as a base mixin class using which can be used create
+  "extensible" mixin classes.
+  """
+
+  def _getExtensibleContent(self, request, name):
+    """
+    Legacy API
+    """
+    warn("_getExtensibleContent() function is deprecated. Use getExtensibleContent() instead.", \
+          DeprecationWarning, stacklevel=2)
+    return self.getExtensibleContent(request, name)
+
+  # Declarative security
+  security = ClassSecurityInfo()
+
+  def _forceIdentification(self, request):
+    # force identification (usable for extensible content)
+    cache = getReadOnlyTransactionCache(self)
+    if cache is not None:
+      key = ('__bobo_traverse__', self, 'user')
+      try:
+        user = cache[key]
+      except KeyError:
+        user = _MARKER
+    else:
+      user = _MARKER
+    old_user = getSecurityManager().getUser()
+    if user is _MARKER:
+      user = None # By default, do nothing
+      if old_user is None or old_user.getUserName() == 'Anonymous User':
+        user_folder = getattr(self.getPortalObject(), 'acl_users', None)
+        if user_folder is not None:
+          try:
+            if request.get('PUBLISHED', _MARKER) is _MARKER:
+              # request['PUBLISHED'] is required by validate
+              request['PUBLISHED'] = self
+              has_published = False
+            else:
+              has_published = True
+            try:
+              user = user_folder.validate(request)
+            except AttributeError:
+              # This kind of error happens with unrestrictedTraverse,
+              # because the request object is a fake, and it is just
+              # a dict object.
+              user = None
+            if not has_published:
+              try:
+                del request.other['PUBLISHED']
+              except AttributeError:
+                # The same here as above. unrestrictedTraverse provides
+                # just a plain dict, so request.other does not exist.
+                del request['PUBLISHED']
+          except:
+            LOG("ERP5 WARNING",0,
+                "Failed to retrieve user in __bobo_traverse__ of WebSection %s" % self.getPath(),
+                error=sys.exc_info())
+            user = None
+      if user is not None and user.getUserName() == 'Anonymous User':
+        user = None # If the user which is connected is anonymous,
+                    # do not try to change SecurityManager
+      if cache is not None:
+        cache[key] = user
+
+    old_manager = None
+    if user is not None:
+      # We need to perform identification
+      old_manager = getSecurityManager()
+      newSecurityManager(get_request(), user)
+
+    return old_manager, user
+
+  security.declareProtected(Permissions.View, 'getDocumentValue')
+  def getDocumentValue(self, name=None, portal=None, **kw):
+    """
+      Return the default document with the given
+      name. The name parameter may represent anything
+      such as a document reference, an identifier,
+      etc.
+
+      If name is not provided, the method defaults
+      to returning the default document by calling
+      getDefaultDocumentValue.
+
+      kw parameters can be useful to filter content
+      (ex. force a given validation state)
+
+      This method must be implemented through a
+      portal type dependent script:
+        WebSection_getDocumentValue
+    """
+    if name is None:
+      return self.getDefaultDocumentValue()
+
+    method = self._getTypeBasedMethod('getDocumentValue',
+              fallback_script_id='WebSection_getDocumentValue')
+
+    document = method(name, portal=portal, **kw)
+    if document is not None:
+      return document.__of__(self)
+
+class DocumentExtensibleTraversableMixIn(BaseExtensibleTraversableMixIn):
+  """
+  This class provides a implementation of IExtensibleTraversable for Document classed based documents.
+  """
+
+  def getExtensibleContent(self, request, name):
+    old_manager, user = self._forceIdentification(request)
+    # Next get the document per name
+    portal = self.getPortalObject()
+    document = self.getDocumentValue(name=name, portal=portal)
+    # restore original security context if there's a logged in user
+    if user is not None:
+      setSecurityManager(old_manager)
+    if document is not None:
+      document = aq_base(document.asContext(id=name, # Hide some properties to permit locating the original
+                                            original_container=document.getParentValue(),
+                                            original_id=document.getId(),
+                                            editable_absolute_url=document.absolute_url()))
+      return document.__of__(self)
+
+    # no document found for current user, still such document may exists
+    # in some cases user (like Anonymous) can not view document according to portal catalog
+    # but we may ask him to login if such a document exists
+    isAuthorizationForced = getattr(self, 'isAuthorizationForced', None)
+    if isAuthorizationForced is not None and isAuthorizationForced():
+      if unrestricted_apply(self.getDocumentValue, (name, portal)) is not None:
+        # force user to login as specified in Web Section
+        raise Unauthorized
+
+class OOoDocumentExtensibleTraversableMixIn(BaseExtensibleTraversableMixIn):
+  """
+  This class provides a implementation of IExtensibleTraversable for OOoDocument classed based documents.
+  """
+
+  def getExtensibleContent(self, request, name):
+    # Be sure that html conversion is done,
+    # as it is required to extract extensible content
+    old_manager, user = self._forceIdentification(request)
+    web_cache_kw = {'name': name,
+                    'format': EMBEDDED_FORMAT}
+    try:
+      self._convert(format='html')
+      _setCacheHeaders(_ViewEmulator().__of__(self), web_cache_kw)
+      mime, data = self.getConversion(format=EMBEDDED_FORMAT, file_name=name)
+      document = OFSFile(name, name, data, content_type=mime).__of__(self.aq_parent)
+    except (NotConvertedError, ConversionError, KeyError):
+      document = DocumentExtensibleTraversableMixIn._getExtensibleContent(self, request, name)
+    # restore original security context if there's a logged in user
+    if user is not None:
+      setSecurityManager(old_manager)
+    return document
+ 
\ No newline at end of file