[CMF-checkins] SVN: CMF/branches/tseaver-pkg_resources/C Finish repairing test / usage stuff for "package resource" skins.

Tres Seaver tseaver at palladion.com
Thu Oct 27 14:20:13 EDT 2005


Log message for revision 39678:
  Finish repairing test / usage stuff for "package resource" skins.
  
  N.B.: Skin paths now normalize to include 'Products/' prefix;
        old skin directories will be upgraded in place.
  
  

Changed:
  U   CMF/branches/tseaver-pkg_resources/CHANGES.txt
  U   CMF/branches/tseaver-pkg_resources/CMFCore/DirectoryView.py
  U   CMF/branches/tseaver-pkg_resources/CMFCore/FSFile.py
  U   CMF/branches/tseaver-pkg_resources/CMFCore/FSObject.py
  U   CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_DirectoryView.py
  U   CMF/branches/tseaver-pkg_resources/CMFCore/utils.py
  U   CMF/branches/tseaver-pkg_resources/CMFDefault/profiles/default/skins.xml

-=-
Modified: CMF/branches/tseaver-pkg_resources/CHANGES.txt
===================================================================
--- CMF/branches/tseaver-pkg_resources/CHANGES.txt	2005-10-27 18:17:21 UTC (rev 39677)
+++ CMF/branches/tseaver-pkg_resources/CHANGES.txt	2005-10-27 18:20:13 UTC (rev 39678)
@@ -136,6 +136,9 @@
 
   Others
 
+    - Skin paths now normalize to include 'Products/' prefix;  old skin
+      directories will be upgraded in place.
+
     - {CMFCalendar,CMFActionIcons,CMFDefault,CMFTopic}/__init__.py:
       Don't register skin directories when being imported by the testrunner
       without 'Products' in our package name.

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/DirectoryView.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/DirectoryView.py	2005-10-27 18:17:21 UTC (rev 39677)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/DirectoryView.py	2005-10-27 18:20:13 UTC (rev 39678)
@@ -41,7 +41,6 @@
 from permissions import AccessContentsInformation
 from permissions import ManagePortal
 from utils import _dtmldir
-from utils import minimalpath
 from utils import normalize
 
 
@@ -288,10 +287,11 @@
                             LOG( 'DirectoryView', ERROR,
                                  '\n'.join(exc_lines) )
 
-                            ob = BadFile( name,
-                                          entry_minimal_fp,
-                                          exc_str='\r\n'.join(exc_lines),
-                                          fullname=entry )
+                            ob = BadFile( name
+                                        , filepath=entry_minimal_fp
+                                        , fullname=entry
+                                        , exc_str='\r\n'.join(exc_lines)
+                                        )
                         finally:
                             tb = None   # Avoid leaking frame!
 
@@ -328,19 +328,19 @@
     """ DI for directories read using pkg_resources API.
     """
 
-    def __init__(self, pname, name, ignore=ignore):
-        self._pname = pname
+    def __init__(self, package, name, ignore=ignore):
+        self._package = package
         self._name = name
         self.ignore = base_ignore + tuple(ignore)
         subdirs = []
         for entry in self._listEntries():
             entry_subpath = self._getEntrySubpath(entry)
-            if resource_isdir(pname, entry_subpath):
+            if resource_isdir(package, entry_subpath):
                subdirs.append(entry)
         self.subdirs = tuple(subdirs)
 
     def _listEntries(self):
-        for entry in resource_listdir(self._pname, self._name):
+        for entry in resource_listdir(self._package, self._name):
             if entry not in self.ignore and not ignore_re.match(entry):
                 yield entry
 
@@ -349,8 +349,8 @@
 
     def readFile(self, filename, mode='ignored'):
         try:
-            return resource_string(self._pname, '%s/%s' % (self._name,
-                                                           filename))
+            return resource_string(self._package, '%s/%s'
+                                                    % (self._name, filename))
         except IOError:
             return None
 
@@ -358,11 +358,11 @@
         # Creates objects for each file.
         data = {}
         objects = []
-        pname = self._pname
+        package = self._package
         types = self._readTypesFile()
-        faux_path_prefix = pname.split('.')
+        faux_path_prefix = package.split('.')
 
-        for entry in resource_listdir(self._pname, self._name):
+        for entry in resource_listdir(self._package, self._name):
 
             if not self._isAllowableFilename(entry):
                 continue
@@ -379,7 +379,7 @@
                     # Register unknown subdirs
                     registry.registerDirectoryByGlobals(entry_subpath, 
                                                         {'__name__' :
-                                                            self._pname },
+                                                            self._package },
                                                         ignore=self.ignore,
                                                        )
                     info = registry.getDirectoryInfo(faux_path)
@@ -395,11 +395,12 @@
                     t = registry.getTypeByMetaType(mt)
                     if t is None:
                         t = DirectoryView
-                    metadata = FSMetadata(package=pname,
+                    metadata = FSMetadata(package=package,
                                           entry_subpath=entry_subpath)
                     metadata.read()
                     ob = t( entry
-                          , entry_minimal_fp
+                          , package=package
+                          , entry_subpath=entry_subpath
                           , properties=metadata.getProperties()
                           )
                     ob_id = ob.getId()
@@ -430,12 +431,12 @@
                     t = registry.getTypeByExtension(ext)
 
                 if t is not None:
-                    metadata = FSMetadata(package=pname,
+                    metadata = FSMetadata(package=package,
                                           entry_subpath=entry_subpath)
                     metadata.read()
                     try:
                         ob = t(name,
-                               package=pname,
+                               package=package,
                                entry_subpath=entry_subpath,
                                fullname=entry,
                                properties=metadata.getProperties(),
@@ -451,7 +452,8 @@
                                  '\n'.join(exc_lines) )
 
                             ob = BadFile( name,
-                                          entry_minimal_fp,
+                                          package=package,
+                                          entry_subpath=entry_subpath,
                                           exc_str='\r\n'.join(exc_lines),
                                           fullname=entry )
                         finally:
@@ -517,18 +519,18 @@
 
     def registerDirectoryByGlobals(self, name, pglobals,
                                    subdirs=1, ignore=ignore):
-        pname = pglobals['__name__']
-        faux_path_elements = pname.split('.')
+        package = pglobals['__name__']
+        faux_path_elements = package.split('.')
         faux_path_elements.append(name)
         faux_path = '/'.join(faux_path_elements)
-        info = ResourceDirectoryInformation(pname, name, ignore=ignore)
+        info = ResourceDirectoryInformation(package, name, ignore=ignore)
         self._directories[faux_path] = info
         if subdirs:
-            for entry in resource_listdir(pname, name):
+            for entry in resource_listdir(package, name):
                 if entry in ignore:
                     continue
                 sub_name = '%s/%s' % (name, entry)
-                if resource_isdir(pname, sub_name):
+                if resource_isdir(package, sub_name):
                     self.registerDirectoryByGlobals( sub_name
                                                    , pglobals
                                                    , subdirs
@@ -542,7 +544,7 @@
         # The idea is that the registry will only contain
         # small paths that are likely to work across platforms
         # and SOFTWARE_HOME, INSTANCE_HOME and PRODUCTS_PATH setups
-        minimal_fp = minimalpath(filepath)
+        minimal_fp = _normalizeDirPath(filepath)
         info = DirectoryInformation(filepath, minimal_fp, ignore=ignore)
         self._directories[minimal_fp] = info
         if subdirs:
@@ -640,6 +642,9 @@
             if index != -1:
                 dirpath = dirpath[index+len('products/'):]
             info = _dirreg.getDirectoryInfo(dirpath)
+            if info is None and not dirpath.startswith('/'):  #BBB
+                dirpath = 'Products/%s' % dirpath
+                info = _dirreg.getDirectoryInfo(dirpath)
             if info is not None:
                 # update the directory view with a corrected path
                 self._dirpath = dirpath
@@ -661,6 +666,16 @@
 
 InitializeClass(DirectoryView)
 
+def _normalizeDirPath(dirpath):
+    dirpath = normalize(dirpath)
+    index = dirpath.rfind('Products')
+    if index == -1:
+        index = dirpath.rfind('products')
+    if index != -1:
+        dirpath = dirpath[index+len('products/'):]
+    if not dirpath.startswith('/') and ':' not in dirpath:
+        dirpath = 'Products/%s' % dirpath
+    return dirpath
 
 class DirectoryViewSurrogate (Folder):
     """ Folderish DirectoryView.
@@ -696,7 +711,7 @@
     def manage_properties( self, dirpath, REQUEST=None ):
         """ Update the directory path of the DirectoryView.
         """
-        self.__dict__['_real']._dirpath = dirpath
+        self.__dict__['_real']._dirpath = _normalizeDirPath(dirpath)
         if REQUEST is not None:
             REQUEST['RESPONSE'].redirect( '%s/manage_propertiesForm'
                                         % self.absolute_url() )
@@ -735,6 +750,9 @@
     """ Add either a DirectoryView or a derivative object.
     """
     info = _dirreg.getDirectoryInfo(minimal_fp)
+    if info is None:    # maybe old data
+        minimal_fp = _normalizeDirPath(minimal_fp)
+        info = _dirreg.getDirectoryInfo(minimal_fp)
     if info is None:
         raise ValueError('Not a registered directory: %s' % minimal_fp)
     if not id:
@@ -753,16 +771,16 @@
     """
     if isinstance(_prefix, basestring):
         filepath = path.join(_prefix, name)
-        adjusted = minimalpath(filepath)
+        adjusted = _normalizeDirPath(filepath)
     else:
-        pname = _prefix['__name__']
-        elements = pname.split('.') + [name]
+        package = _prefix['__name__']
+        elements = package.split('.') + [name]
         adjusted = '/'.join(elements)
 
     info = _dirreg.getDirectoryInfo(adjusted)
 
     if info is None:
-        raise ValueError('Not a registered directory: %s' % minimal_fp)
+        raise ValueError('Not a registered directory: %s' % adjusted)
 
     for entry in info.getSubdirs():
         entry_subname = '%s/%s' % (adjusted, entry)

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/FSFile.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/FSFile.py	2005-10-27 18:17:21 UTC (rev 39677)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/FSFile.py	2005-10-27 18:20:13 UTC (rev 39678)
@@ -82,7 +82,7 @@
 
     def _getFilename(self):
         path = self._filepath or self._entry_subpath
-        dir, fn = os.path.split(self._filepath)
+        dir, fn = os.path.split(path)
         return fn
 
     def _readFile(self, reparse):

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/FSObject.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/FSObject.py	2005-10-27 18:17:21 UTC (rev 39677)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/FSObject.py	2005-10-27 18:20:13 UTC (rev 39678)
@@ -15,12 +15,20 @@
 $Id$
 """
 
-from os import path
-from os import stat
-from pkg_resources import resource_string
-from pkg_resources import get_provider
+import os
+import sys
 import time
 
+try:
+    from pkg_resources import resource_string
+    from pkg_resources import get_provider
+    from pkg_resources import ZipProvider
+    _HAS_PKG_RESOURCES = True
+except ImportError:
+    _HAS_PKG_RESOURCES = False
+    class ZipProvider:  # silence!
+        pass
+
 import Globals
 from AccessControl import ClassSecurityInfo
 from AccessControl.Role import RoleManager
@@ -184,12 +192,13 @@
         if self._filepath:
             fp = expandpath(self._filepath)
             try:
-                statinfo = stat(fp)
+                statinfo = os.stat(fp)
                 size, mtime = statinfo[6], statinfo[8]
             except:
                 size = mtime = 0
         else:
-            size, mtime = _getZipStatInfo(self._package, self._entry_subpath)
+            size, mtime = _getResourceStatInfo(self._package,
+                                               self._entry_subpath)
 
         if not parsed or mtime != self._file_mod_time:
             # if we have to read the file again, remove the cache
@@ -253,6 +262,11 @@
 <dtml-var manage_tabs>
 <h2> Bad Filesystem Object: &dtml-getId; </h2>
 
+<h3> File Path </h3>
+<pre>
+<dtml-var getFilePath>
+</pre>
+
 <h3> File Contents </h3>
 <pre>
 <dtml-var getFileContents>
@@ -269,22 +283,43 @@
         {'label':'Error', 'action':'manage_showError'},
         )
 
-    def __init__( self, id, filepath, exc_str=''
-                , fullname=None, properties=None):
+    def __init__( self, id, package=None, entry_subpath=None, filepath=None
+                , fullname=None, properties=None, exc_str=''
+                ):
         id = fullname or id # Use the whole filename.
         self.exc_str = exc_str
         self.file_contents = ''
-        FSObject.__init__(self, id, filepath, fullname, properties)
+        FSObject.__init__(self, id, package, entry_subpath, filepath,
+                          fullname, properties)
 
     security = ClassSecurityInfo()
 
+    security.declarePrivate('showError')
     showError = Globals.HTML( BAD_FILE_VIEW )
+
+    security.declarePrivate('_readFile')
+    def _readFile(self, reparse):
+        """ Stub this out.
+        """
+        pass
+
     security.declareProtected(ManagePortal, 'manage_showError')
     def manage_showError( self, REQUEST ):
+        """ Render the error page.
         """
-        """
         return self.showError( self, REQUEST )
 
+    security.declarePublic( 'getFilePath' )
+    def getFilePath( self ):
+        """
+            Return the path to the file.
+        """
+        if self._filepath:
+            return self._filepath
+        else:
+            return 'package: %s, subpath: %s' % (self._package,
+                                                 self._entry_subpath)
+
     security.declarePublic( 'getFileContents' )
     def getFileContents( self ):
         """
@@ -302,21 +337,33 @@
 
 Globals.InitializeClass( BadFile )
 
-def _getZipStatInfo(package, subpath):
+def _getResourceStatInfo(package, subpath):
     provider = get_provider(package)
-    zip_stat = provider.zipinfo.get(subpath)
-    if zip_stat is None:
-        return None, None
-    path, compression, csize, size, date, time, crc = zip_stat
-    date_time = ((date >> 9) + 1980,    # year
-                 (date >> 5) & 0xF,     # month
-                 date & 0x1F,           # day
-                 (time & 0xFFFF) >> 11, # hour
-                 (time >> 5) & 0x3F,    # minute
-                 (time & 0x1F) * 2,     # second
-                 0,                     # ?
-                 0,                     # ?
-                 -1,                    # ?
-                )
-    return size, time.mktime(date_time) 
 
+    if _HAS_PKG_RESOURCES and isinstance(provider, ZipProvider):
+        zip_stat = provider.zipinfo.get(subpath)
+        if zip_stat is None:
+            return None, None
+        path, compression, csize, size, date, time, crc = zip_stat
+        date_time = ((date >> 9) + 1980,    # year
+                    (date >> 5) & 0xF,     # month
+                    date & 0x1F,           # day
+                    (time & 0xFFFF) >> 11, # hour
+                    (time >> 5) & 0x3F,    # minute
+                    (time & 0x1F) * 2,     # second
+                    0,                     # ?
+                    0,                     # ?
+                    -1,                    # ?
+                    )
+        return size, time.mktime(date_time)
+
+    module = sys.modules[package]
+    path = os.path.split(module.__file__)
+
+    try:
+        statinfo = os.stat(path)
+        size, mtime = statinfo[6], statinfo[8]
+    except:
+        size = mtime = 0
+
+    return size, mtime

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_DirectoryView.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_DirectoryView.py	2005-10-27 18:17:21 UTC (rev 39677)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_DirectoryView.py	2005-10-27 18:20:13 UTC (rev 39678)
@@ -60,6 +60,8 @@
         skin.manage_properties(r'Products\CMFCore\tests\fake_skins\fake_skin')
         self.failUnless( hasattr(self.ob.fake_skin, 'test1'),
                          self.ob.fake_skin.getDirPath() )
+        self.assertEqual(skin._dirpath, 
+                         'Products/CMFCore/tests/fake_skins/fake_skin')
 
     # windows SOFTWARE_HOME
     def test_getDirectoryInfo2(self):
@@ -68,6 +70,8 @@
                   r'C:\Zope\2.5.1\Products\CMFCore\tests\fake_skins\fake_skin')
         self.failUnless( hasattr(self.ob.fake_skin, 'test1'),
                          self.ob.fake_skin.getDirPath() )
+        self.assertEqual(skin._dirpath, 
+                         'Products/CMFCore/tests/fake_skins/fake_skin')
 
     # *nix INSTANCE_HOME
     def test_getDirectoryInfo3(self):
@@ -75,6 +79,8 @@
         skin.manage_properties('Products/CMFCore/tests/fake_skins/fake_skin')
         self.failUnless( hasattr(self.ob.fake_skin, 'test1'),
                          self.ob.fake_skin.getDirPath() )
+        self.assertEqual(skin._dirpath, 
+                         'Products/CMFCore/tests/fake_skins/fake_skin')
 
     # *nix SOFTWARE_HOME
     def test_getDirectoryInfo4(self):
@@ -83,6 +89,8 @@
            '/usr/local/zope/2.5.1/Products/CMFCore/tests/fake_skins/fake_skin')
         self.failUnless( hasattr(self.ob.fake_skin, 'test1'),
                          self.ob.fake_skin.getDirPath() )
+        self.assertEqual(skin._dirpath, 
+                         'Products/CMFCore/tests/fake_skins/fake_skin')
 
     # windows PRODUCTS_PATH
     def test_getDirectoryInfo5(self):
@@ -91,6 +99,8 @@
                                r'\Products\CMFCore\tests\fake_skins\fake_skin')
         self.failUnless( hasattr(self.ob.fake_skin, 'test1'),
                          self.ob.fake_skin.getDirPath() )
+        self.assertEqual(skin._dirpath, 
+                         'Products/CMFCore/tests/fake_skins/fake_skin')
 
     # linux PRODUCTS_PATH
     def test_getDirectoryInfo6(self):
@@ -99,6 +109,8 @@
                                 '/Products/CMFCore/tests/fake_skins/fake_skin')
         self.failUnless( hasattr(self.ob.fake_skin, 'test1'),
                          self.ob.fake_skin.getDirPath() )
+        self.assertEqual(skin._dirpath, 
+                         'Products/CMFCore/tests/fake_skins/fake_skin')
 
     # second 'Products' in path
     def test_getDirectoryInfo7(self):
@@ -107,7 +119,18 @@
            r'C:\CoolProducts\Zope\Products\CMFCore\tests\fake_skins\fake_skin')
         self.failUnless( hasattr(self.ob.fake_skin, 'test1'),
                          self.ob.fake_skin.getDirPath() )
+        self.assertEqual(skin._dirpath, 
+                         'Products/CMFCore/tests/fake_skins/fake_skin')
 
+    # *No* 'Products' in path (typical for sites upgraded from 1.5.x)
+    def test_getDirectoryInfo8(self):
+        skin = self.ob.fake_skin
+        skin.manage_properties('CMFCore/tests/fake_skins/fake_skin')
+        self.failUnless( hasattr(self.ob.fake_skin, 'test1'),
+                         self.ob.fake_skin.getDirPath() )
+        self.assertEqual(skin._dirpath, 
+                         'Products/CMFCore/tests/fake_skins/fake_skin')
+
     # Test we do nothing if given a really wacky path
     def test_UnhandleableExpandPath( self ):
         from tempfile import mktemp
@@ -137,10 +160,11 @@
     def test_registerDirectoryMinimalPath(self):
         from Products.CMFCore.DirectoryView import _dirreg
         dirs = _dirreg._directories
-        self.failUnless( dirs.has_key('CMFCore/tests/fake_skins/fake_skin'),
+        self.failUnless( dirs.has_key(
+                            'Products/CMFCore/tests/fake_skins/fake_skin'),
                          dirs.keys() )
         self.assertEqual( self.ob.fake_skin.getDirPath(),
-                          'CMFCore/tests/fake_skins/fake_skin' )
+                          'Products/CMFCore/tests/fake_skins/fake_skin' )
 
 
 class DirectoryViewTests( FSDVTest ):
@@ -290,7 +314,52 @@
         self.failUnless('Products/Rotten/skins' in added)
         self.failUnless('Products/Rotten/skins/rotten' in added)
 
+class Test_normalizeDirPath( TestCase ):
 
+    def _check(self, before, after):
+        from Products.CMFCore.DirectoryView import _normalizeDirPath
+        self.assertEqual(_normalizeDirPath(before), after)
+
+    def test__nDP_already_normalized(self):
+        BEFORE = 'Products/CMFCore/skins/foo'
+        AFTER = 'Products/CMFCore/skins/foo'
+        self._check(BEFORE, AFTER)
+
+    def test__nDP_product_but_backward_slashes(self):
+        BEFORE = r'Products\CMFCore\skins\foo'
+        AFTER = 'Products/CMFCore/skins/foo'
+        self._check(BEFORE, AFTER)
+
+    def test__nDP_no_Products_forward_slashes(self):
+        BEFORE = 'CMFCore/skins/foo'
+        AFTER = 'Products/CMFCore/skins/foo'
+        self._check(BEFORE, AFTER)
+
+    def test__nDP_no_Products_backward_slashes(self):
+        BEFORE = r'CMFCore\skins\foo'
+        AFTER = 'Products/CMFCore/skins/foo'
+        self._check(BEFORE, AFTER)
+
+    def test__nDP_leading_path_forward_slashes(self):
+        BEFORE = '/var/zope_instance/Products/CMFCore/skins/foo'
+        AFTER = 'Products/CMFCore/skins/foo'
+        self._check(BEFORE, AFTER)
+
+    def test__nDP_leading_path_backward_slashes(self):
+        BEFORE = r'C:\zope_instance\Products\CMFCore\skins\foo'
+        AFTER = 'Products/CMFCore/skins/foo'
+        self._check(BEFORE, AFTER)
+
+    def test__nDP_abs_path_no_products_forward_slashes(self):
+        BEFORE = '/var/zope_instance/CMFCore/skins/foo'
+        AFTER = '/var/zope_instance/CMFCore/skins/foo'
+        self._check(BEFORE, AFTER)
+
+    def test__nDP_abs_path_backward_slashes(self):
+        BEFORE = r'C:\zope_instance\CMFCore\skins\foo'
+        AFTER = r'C:/zope_instance/CMFCore/skins/foo'
+        self._check(BEFORE, AFTER)
+
 if DevelopmentMode:
 
   class DebugModeTests( FSDVTest ):
@@ -369,6 +438,7 @@
         makeSuite(DirectoryViewIgnoreTests),
         makeSuite(DirectoryViewFolderTests),
         makeSuite(DirectoryViewEggTests),
+        makeSuite(Test_normalizeDirPath),
         makeSuite(DebugModeTests),
         ))
 

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/utils.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/utils.py	2005-10-27 18:17:21 UTC (rev 39677)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/utils.py	2005-10-27 18:20:13 UTC (rev 39678)
@@ -757,6 +757,9 @@
     if os_path.isabs(p):
         return p
 
+    if p.startswith('Products'):
+        p = p[len('Products/'):]
+
     for ppath in ProductsPath:
         abs = os_path.join(ppath, p)
         if os_path.exists(abs):

Modified: CMF/branches/tseaver-pkg_resources/CMFDefault/profiles/default/skins.xml
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFDefault/profiles/default/skins.xml	2005-10-27 18:17:21 UTC (rev 39677)
+++ CMF/branches/tseaver-pkg_resources/CMFDefault/profiles/default/skins.xml	2005-10-27 18:20:13 UTC (rev 39678)
@@ -3,13 +3,13 @@
             request_varname="portal_skin" allow_any="0"
             cookie_persistence="0">
  <skin-directory id="Images"
-                 directory="CMFDefault/skins/Images"/>
+                 directory="Products/CMFDefault/skins/Images"/>
  <skin-directory id="zpt_content"
-                 directory="CMFDefault/skins/zpt_content"/>
+                 directory="Products/CMFDefault/skins/zpt_content"/>
  <skin-directory id="zpt_control"
-                 directory="CMFDefault/skins/zpt_control"/>
+                 directory="Products/CMFDefault/skins/zpt_control"/>
  <skin-directory id="zpt_generic"
-                 directory="CMFDefault/skins/zpt_generic"/>
+                 directory="Products/CMFDefault/skins/zpt_generic"/>
  <skin-path id="Basic">
   <layer name="custom"/>
   <layer name="zpt_content"/>



More information about the CMF-checkins mailing list