[Checkins] SVN: z3ext.resource/trunk/s

Nikolay Kim fafhrd at datacom.kz
Fri Mar 28 07:32:26 EDT 2008


Log message for revision 84992:
  

Changed:
  U   z3ext.resource/trunk/setup.py
  A   z3ext.resource/trunk/src/z3ext/
  A   z3ext.resource/trunk/src/z3ext/__init__.py
  A   z3ext.resource/trunk/src/z3ext/resource/
  A   z3ext.resource/trunk/src/z3ext/resource/README.txt
  A   z3ext.resource/trunk/src/z3ext/resource/__init__.py
  A   z3ext.resource/trunk/src/z3ext/resource/configure.zcml
  A   z3ext.resource/trunk/src/z3ext/resource/fileresource.py
  A   z3ext.resource/trunk/src/z3ext/resource/interfaces.py
  A   z3ext.resource/trunk/src/z3ext/resource/meta.zcml
  A   z3ext.resource/trunk/src/z3ext/resource/resource.py
  A   z3ext.resource/trunk/src/z3ext/resource/stylesheet/
  A   z3ext.resource/trunk/src/z3ext/resource/stylesheet/__init__.py
  A   z3ext.resource/trunk/src/z3ext/resource/stylesheet/configure.zcml
  A   z3ext.resource/trunk/src/z3ext/resource/stylesheet/packer.py
  A   z3ext.resource/trunk/src/z3ext/resource/stylesheet/resource.py
  A   z3ext.resource/trunk/src/z3ext/resource/stylesheet/stylesheet.py
  A   z3ext.resource/trunk/src/z3ext/resource/tests.py
  A   z3ext.resource/trunk/src/z3ext/resource/zcml.py
  A   z3ext.resource/trunk/src/z3ext/resource/zrtresource/
  A   z3ext.resource/trunk/src/z3ext/resource/zrtresource/__init__.py
  A   z3ext.resource/trunk/src/z3ext/resource/zrtresource/configure.zcml
  A   z3ext.resource/trunk/src/z3ext/resource/zrtresource/processor.py
  A   z3ext.resource/trunk/src/z3ext/resource/zrtresource/zrtresource.py

-=-
Modified: z3ext.resource/trunk/setup.py
===================================================================
--- z3ext.resource/trunk/setup.py	2008-03-28 11:16:29 UTC (rev 84991)
+++ z3ext.resource/trunk/setup.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -21,7 +21,7 @@
 def read(*rnames):
     return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
 
-version = '1.1.0dev'
+version = '1.1.0'
 
 
 setup(name='z3ext.resource',
@@ -56,7 +56,9 @@
                           'zope.schema',
                           'zope.component',
                           'zope.interface',
-                          'zope.i18nmessageid',
+			  'zope.configuration',
+			  'zope.publisher',
+			  'zope.security',
                           'zope.app.publisher',
 			  'z3ext.cacheheaders',
                           ],

Added: z3ext.resource/trunk/src/z3ext/__init__.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/__init__.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/__init__.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,6 @@
+# namespace package boilerplate
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError, e:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)


Property changes on: z3ext.resource/trunk/src/z3ext/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/README.txt
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/README.txt	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/README.txt	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,245 @@
+============================
+``z3ext:resource`` Directive
+============================
+
+This package provides a new directive similar to browser:resource directive.
+
+  >>> from zope import component, interface
+  >>> from z3ext.resource import interfaces, fileresource
+
+  >>> import z3ext.resource
+  >>> from zope.configuration import xmlconfig
+  >>> context = xmlconfig.file('meta.zcml', z3ext.resource)
+
+Now we can register a resource:
+
+  >>> import tempfile, os, os.path
+  >>> fn = tempfile.mktemp('.js')
+  >>> open(fn, 'w').write('''some resource data''')
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns:z3ext="http://namespaces.zope.org/z3ext">
+  ...   <z3ext:resource
+  ...       name="resource.js"
+  ...       file="%s" />
+  ... </configure>
+  ... '''%fn, context=context)
+
+Now let's see whether the adapter has been registered:
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> response = request.response
+
+  >>> resource = component.getAdapter(
+  ...    request, interface.Interface, name='resource.js')
+
+  >>> modified = resource.modified()
+
+  >>> resource.render(request)
+  'some resource data'
+
+By default resource is FileResource
+
+  >>> isinstance(resource.context, fileresource.File)
+  True
+
+We can set resource type explicitly
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns:z3ext="http://namespaces.zope.org/z3ext">
+  ...   <z3ext:resource
+  ...       name="resource-image"
+  ...       type="imageresource"
+  ...       file="%s" />
+  ... </configure>
+  ... ''' %fn, context=context)
+
+  >>> resource = component.getAdapter(
+  ...    request, interface.Interface, name='resource-image')
+
+  >>> isinstance(resource.context, fileresource.Image)
+  True
+
+
+Custom resource type
+--------------------
+
+We have to register IResourceFactoryType utility
+
+  >>> from z3ext.resource.tests import CustomFileResourceFactory
+  >>> custom = component.factory.Factory(
+  ...   CustomFileResourceFactory, interfaces=(interfaces.IResourceFactory,))
+  >>> component.provideUtility(
+  ...     custom, interfaces.IResourceFactoryType, name='custom')
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns:z3ext="http://namespaces.zope.org/z3ext">
+  ...   <z3ext:resource
+  ...       name="resource-custom"
+  ...       type="custom"
+  ...       file="%s" />
+  ... </configure>
+  ... ''' %fn, context=context)
+
+  >>> resource = component.getAdapter(
+  ...    request, interface.Interface, name='resource-custom')
+
+  >>> resource
+  <z3ext.resource.tests.CustomResource object at ...>
+
+  >>> os.unlink(fn)
+
+
+=====================================
+``z3ext:resourceDirectory`` Directive
+=====================================
+
+We need some temporary directories and files
+
+  >>> import tempfile
+  >>> dn = tempfile.mkdtemp()
+  >>> os.mkdir(os.path.join(dn, 'subfolder'))
+  >>> open(os.path.join(dn, 'resource1.css'), 'w').write('''\
+  ... h1 {
+  ...   color: red;
+  ...   font: fontName;
+  ...   background: url('../img1/mybackground.gif');
+  ... }''')
+  >>> open(os.path.join(dn, 'resource2.js'), 'w').write('test')
+  >>> open(os.path.join(dn, 'resource3.css'), 'w').write('test')
+  >>> open(os.path.join(dn, 'resource4.jpg'), 'w').write('test')
+  >>> open(os.path.join(dn, 'resource5.png'), 'w').write('test')
+
+Directive require directory path
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns:z3ext="http://namespaces.zope.org/z3ext">
+  ...   <z3ext:resourcedirectory
+  ...       name="myresources"
+  ...       directory="%s" />
+  ... </configure>
+  ... ''' %(dn+'123123234534234'), context=context)
+  Traceback (most recent call last):
+  ...
+  ZopeXMLConfigurationError: ...
+
+Now we can register a directory of resources, also we can set
+resource types:
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns:z3ext="http://namespaces.zope.org/z3ext">
+  ...   <z3ext:resourcedirectory
+  ...       name="myresources"
+  ...       directory="%s"
+  ...	    mapping=".css:custom .js:fileresource
+  ...                resource3.css:fileresource .png:null" />
+  ... </configure>
+  ... ''' %dn, context=context)
+
+  >>> dirresource = component.getAdapter(
+  ...    request, interface.Interface, name='myresources')
+
+Now we can get resource
+
+  >>> dirresource.browserDefault(request)
+  (<function empty at ...>, ())
+
+  >>> resource = dirresource.publishTraverse(request, 'resource1.css')
+  >>> print resource.GET()
+  h1 {
+      color: red;
+      font: fontName;
+      background: url('../img1/mybackground.gif');
+  }
+
+  >>> print dirresource['resource1.css'].GET()
+  h1 {
+      color: red;
+      font: fontName;
+      background: url('../img1/mybackground.gif');
+  }
+
+  >>> dirresource.publishTraverse(request, 'unknown.css')
+  Traceback (most recent call last):
+  ...
+  NotFound: ...
+
+  >>> dirresource['unknown.css']
+  Traceback (most recent call last):
+  ...
+  KeyError: 'unknown.css'
+
+
+Types mapping
+-------------
+
+  In 'mapping' we defined that all files with '.css' extension should be 
+custom type, '.js' should be file resource and filename 'test.css' 
+should be file resource, '.png' should be not available
+
+  >>> dirresource.publishTraverse(request, 'resource1.css')
+  <z3ext.resource.tests.CustomResource object at ...>
+
+  >>> dirresource.publishTraverse(request, 'resource2.js')
+  <z3ext.resource.fileresource.FileResource object at ...>
+
+  >>> dirresource.publishTraverse(request, 'resource3.css')
+  <z3ext.resource.fileresource.FileResource object at ...>
+
+  >>> dirresource.publishTraverse(request, 'resource4.jpg')
+  <z3ext.resource.fileresource.FileResource object at ...>
+
+  >>> dirresource.publishTraverse(request, 'resource5.png')
+  Traceback (most recent call last):
+  ...
+  NotFound: Object: ...
+
+Or we can use 'resourceType' subdirective:
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns:z3ext="http://namespaces.zope.org/z3ext">
+  ...   <z3ext:resourcedirectory
+  ...       name="myresources2"
+  ...       directory="%s">
+  ...     <resourceType file=".css" type="custom" />
+  ...     <resourceType file=".js" type="fileresource" />
+  ...     <resourceType file="resource3.css" type="fileresource" />
+  ...     <resourceType file=".png" type="null" />
+  ...   </z3ext:resourcedirectory>
+  ... </configure>
+  ... ''' %dn, context=context)
+
+  >>> dirresource = component.getAdapter(
+  ...    request, interface.Interface, name='myresources2')
+
+  >>> dirresource.publishTraverse(request, 'resource1.css')
+  <z3ext.resource.tests.CustomResource object at ...>
+
+  >>> dirresource.publishTraverse(request, 'resource2.js')
+  <z3ext.resource.fileresource.FileResource object at ...>
+
+  >>> dirresource.publishTraverse(request, 'resource3.css')
+  <z3ext.resource.fileresource.FileResource object at ...>
+
+  >>> dirresource.publishTraverse(request, 'resource4.jpg')
+  <z3ext.resource.fileresource.FileResource object at ...>
+
+  >>> dirresource.publishTraverse(request, 'resource5.png')
+  Traceback (most recent call last):
+  ...
+  NotFound: Object: ...
+
+
+We can get sub directories
+
+  >>> subdir = dirresource.publishTraverse(request, 'subfolder')
+
+
+  >>> os.unlink(os.path.join(dn, 'resource1.css'))
+  >>> os.unlink(os.path.join(dn, 'resource2.js'))
+  >>> os.unlink(os.path.join(dn, 'resource3.css'))
+  >>> os.unlink(os.path.join(dn, 'resource4.jpg'))
+  >>> os.unlink(os.path.join(dn, 'resource5.png'))
+  >>> os.rmdir(os.path.join(dn, 'subfolder'))
+  >>> os.rmdir(dn)

Added: z3ext.resource/trunk/src/z3ext/resource/__init__.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/__init__.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/__init__.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.


Property changes on: z3ext.resource/trunk/src/z3ext/resource/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/configure.zcml
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/configure.zcml	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/configure.zcml	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,61 @@
+<configure 
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:zcml="http://namespaces.zope.org/zcml">
+
+  <include package="z3ext.cacheheaders" />
+
+  <utility
+     component=".fileresource.filefactory"
+     provides=".resource.IResourceFactoryType" />
+
+  <utility
+     name="fileresource"
+     component=".fileresource.filefactory"
+     provides=".resource.IResourceFactoryType" />
+
+  <utility
+     name="imageresource"
+     component=".fileresource.imagefactory"
+     provides=".resource.IResourceFactoryType" />
+
+  <utility
+     name="gif"
+     component=".fileresource.imagefactory"
+     provides=".resource.IResourceFactoryType" />
+
+  <utility
+     name="png"
+     component=".fileresource.imagefactory"
+     provides=".resource.IResourceFactoryType" />
+
+  <utility
+     name="jpg"
+     component=".fileresource.imagefactory"
+     provides=".resource.IResourceFactoryType" />
+
+  <utility
+     name="pt"
+     component=".resource.pagetemplatefactory"
+     provides=".resource.IResourceFactoryType" />
+
+  <utility
+     name="zpt"
+     component=".resource.pagetemplatefactory"
+     provides=".resource.IResourceFactoryType" />
+
+  <utility
+     name="html"
+     component=".resource.pagetemplatefactory"
+     provides=".resource.IResourceFactoryType" />
+
+  <adapter
+     for="zope.app.publisher.browser.fileresource.FileResource"
+     factory=".fileresource.FileResourceAdapter"
+     permission="zope.Public" />
+
+  <zcml:configure zcml:condition="installed z3c.zrtresource">
+    <include package="z3ext.resource.stylesheet" />
+    <include package="z3ext.resource.zrtresource" />
+  </zcml:configure>
+
+</configure>

Added: z3ext.resource/trunk/src/z3ext/resource/fileresource.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/fileresource.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/fileresource.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,101 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+from zope import interface
+from zope.component.factory import Factory
+from zope.app.publisher.fileresource import Image
+from zope.app.publisher.fileresource import File as BaseFile
+from zope.app.publisher.browser.fileresource import FileResource as BaseFileResource
+
+from interfaces import IResource, IResourceFactoryType
+
+
+class File(BaseFile): 
+
+    def __init__(self, path, name):
+        super(File, self).__init__(path, name)
+
+        if self.content_type == 'application/x-javascript':
+            self.content_type = 'text/javascript'
+
+
+class FileResource(BaseFileResource):
+    interface.implements(IResource)
+
+    def modified(self, default=long(0)):
+        file = self.chooseContext()
+        if getattr(file, 'lmt', None):
+            return long(file.lmt)
+        else:
+            return default
+    
+    def render(self, request):
+        file = self.chooseContext()
+        f = open(file.path,'rb')
+        data = f.read()
+        f.close()
+        return data
+
+
+class FileResourceAdapter(object):
+    interface.implements(IResource)
+
+    def __init__(self, context):
+        self.context = context
+
+    def modified(self, default=long(0)):
+        file = self.context.chooseContext()
+        if getattr(file, 'lmt', None):
+            return long(file.lmt)
+        else:
+            return default
+    
+    def render(self, request):
+        file = self.context.chooseContext()
+        f = open(file.path,'rb')
+        data = f.read()
+        f.close()
+        return data
+
+
+class FileResourceFactory(object):
+
+    def __init__(self, path, checker, name):
+        self._file = File(path, name)
+        self._checker = checker
+        self._name = name
+
+    def __call__(self, request):
+        resource = FileResource(self._file, request)
+        resource.__Security_checker__ = self._checker
+        resource.__name__ = self._name
+        return resource
+
+
+filefactory = Factory(FileResourceFactory)
+interface.directlyProvides(filefactory, IResourceFactoryType)
+
+
+class ImageResourceFactory(FileResourceFactory):
+
+    def __init__(self, path, checker, name):
+        self._file = Image(path, name)
+        self._checker = checker
+        self._name = name
+
+imagefactory = Factory(ImageResourceFactory)
+interface.directlyProvides(imagefactory, IResourceFactoryType)


Property changes on: z3ext.resource/trunk/src/z3ext/resource/fileresource.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/interfaces.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/interfaces.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/interfaces.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,37 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+from zope import interface
+from z3ext.cacheheaders.interfaces import IModificationInfo
+
+
+class IResource(IModificationInfo):
+
+    def render(request):
+        """ render resource """
+
+
+class IResourceFactory(interface.Interface):
+
+    def __call__(self, request, **kwargs):
+        """ create resource """
+
+
+class IResourceFactoryType(interface.Interface):
+
+    def __call__(self, path, checker, name):
+        """ create resource factory """


Property changes on: z3ext.resource/trunk/src/z3ext/resource/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/meta.zcml
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/meta.zcml	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/meta.zcml	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,23 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:meta="http://namespaces.zope.org/meta">
+
+  <meta:directives namespace="http://namespaces.zope.org/z3ext">
+    <meta:directive
+        name="resource"
+        schema=".zcml.IResourceDirective"
+        handler=".zcml.resourceDirective" />
+
+    <meta:complexDirective
+       name="resourcedirectory"
+       schema=".zcml.IResourceDirectoryDirective"
+       handler=".zcml.ResourceDirectoryDirective">
+
+      <meta:subdirective
+         name="resourceType"
+         schema=".zcml.IResourceDirectoryResourceType" />
+    </meta:complexDirective>
+
+  </meta:directives>
+
+</configure>

Added: z3ext.resource/trunk/src/z3ext/resource/resource.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/resource.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/resource.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,26 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+from zope import interface
+from zope.component.factory import Factory
+from zope.app.publisher.browser import pagetemplateresource
+
+from interfaces import IResourceFactoryType
+
+
+pagetemplatefactory = Factory(pagetemplateresource.PageTemplateResourceFactory)
+interface.directlyProvides(IResourceFactoryType)


Property changes on: z3ext.resource/trunk/src/z3ext/resource/resource.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/stylesheet/__init__.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/stylesheet/__init__.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/stylesheet/__init__.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.


Property changes on: z3ext.resource/trunk/src/z3ext/resource/stylesheet/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/stylesheet/configure.zcml
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/stylesheet/configure.zcml	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/stylesheet/configure.zcml	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,12 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:zcml="http://namespaces.zope.org/zcml">
+
+  <include package="z3c.zrtresource" file="meta.zcml" />
+
+  <utility
+     name="stylesheet"
+     provides="z3ext.resource.resource.IResourceFactoryType"
+     component=".resource.stylesheetfactory" />
+
+</configure>

Added: z3ext.resource/trunk/src/z3ext/resource/stylesheet/packer.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/stylesheet/packer.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/stylesheet/packer.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,388 @@
+import re
+
+
+class KeywordMapper:
+    def __init__(self, regexp, encoder):
+        if isinstance(regexp, (str, unicode)):
+            self.regexp = re.compile(regexp)
+        else:
+            self.regexp = regexp
+        self.encoder = encoder
+        self.mapping = {}
+
+    def analyseKeywords(self, input):
+        matches = self.regexp.findall(input)
+
+        protected = {}
+        keyword_count = {}
+        index = 0
+        for match in matches:
+            if match not in keyword_count:
+                keyword_count[match] = 0
+                protected[self.encoder(index)] = index
+                index = index + 1
+            keyword_count[match] = keyword_count[match] + 1
+
+        for match in matches:
+            if match in protected and keyword_count[match]:
+                keyword_count[match] = 0
+
+        protected = {}
+        for match in keyword_count:
+            if not keyword_count[match]:
+                protected[match] = None
+
+        ## sorted_matches = [(c,len(v),v) for v,c in keyword_count.iteritems()]
+        # the above line implements the original behaviour, the code below
+        # removes keywords which have not enough weight to be encoded, in total
+        # this saves some bytes, because the total length of the generated
+        # codes is a bit smaller. This needs corresponding code in the
+        # fast_decode javascript function of the decoder, see comment there
+        sorted_matches = []
+        for value, count in keyword_count.iteritems():
+            weight = count * len(value)
+            if len(value) >= weight:
+                keyword_count[value] = 0
+                sorted_matches.append((0, value))
+            else:
+                sorted_matches.append((weight, value))
+        sorted_matches.sort()
+        sorted_matches.reverse()
+        sorted_matches = [x[-1] for x in sorted_matches]
+
+        index = 0
+        mapping = {}
+        for match in sorted_matches:
+            if not keyword_count[match]:
+                if match not in protected:
+                    mapping[match] = (-1, match)
+                continue
+            while 1:
+                encoded = self.encoder(index)
+                index = index + 1
+                if encoded in protected:
+                    mapping[encoded] = (index-1, encoded)
+                    continue
+                else:
+                    break
+            mapping[match] = (index-1, encoded)
+
+        return mapping
+
+    def analyse(self, input):
+        self.mapping = self.analyseKeywords(input)
+
+    def getKeywords(self):
+        sorted = zip(self.mapping.itervalues(), self.mapping.iterkeys())
+        sorted.sort()
+        keywords = []
+        for (index, encoded), value in sorted:
+            if index >= 0:
+                if encoded != value:
+                    keywords.append(value)
+                else:
+                    keywords.append('')
+        return keywords
+
+    def sub(self, input):
+        def repl(m):
+            return self.mapping.get(m.group(0), ('', m.group(0)))[1]
+        return self.regexp.sub(repl, input)
+
+
+class JavascriptKeywordMapper(KeywordMapper):
+    def __init__(self, regexp=None, encoder=None):
+        if regexp is None:
+            self.regexp = re.compile(r'\w+')
+        elif isinstance(regexp, (str, unicode)):
+            self.regexp = re.compile(regexp)
+        else:
+            self.regexp = regexp
+        if encoder is None:
+            self.encoder = self._encode
+        else:
+            self.encoder = encoder
+        self.mapping = {}
+
+    def _encode(self, charCode,
+                mapping="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"):
+        result = []
+        quotient = charCode
+        while quotient or not len(result):
+            quotient, remainder = divmod(quotient, 62)
+            result.append(mapping[remainder])
+        result.reverse()
+        return "".join(result)
+
+    def getDecodeFunction(self, fast=True, name=None):
+        jspacker = JavascriptPacker('full')
+
+        # fast boot function
+        fast_decoder = r"""
+            // does the browser support String.replace where the
+            //  replacement value is a function?
+            if (!''.replace(/^/, String)) {
+                // decode all the values we need
+                // we have to add the dollar prefix, because $encoded can be
+                // any keyword in the decode function below. For example
+                // 'constructor' is an attribute of any object and it would
+                // return a false positive match in that case.
+                while ($count--) $decode["$"+$encode($count)] = $keywords[$count] || $encode($count);
+                // global replacement function
+                $keywords = [function($encoded){$result = $decode["$"+$encoded]; return $result!=undefined?$result:$encoded}];
+                // generic match
+                $encode = function(){return'\\w+'};
+                // reset the loop counter -  we are now doing a global replace
+                $count = 1;
+            };"""
+
+        if name is None:
+            # boot function
+            decoder = r"""
+                function($packed, $ascii, $count, $keywords, $encode, $decode) {
+                    $encode = function($charCode) {
+                        return ($charCode < $ascii ? "" : $encode(parseInt($charCode / $ascii))) +
+                            (($charCode = $charCode % $ascii) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));
+                    };
+                    // fastDecodePlaceholder
+                    while ($count--)
+                        if ($keywords[$count])
+                            $packed = $packed.replace(new RegExp("\\b" + $encode($count) + "\\b", "g"), $keywords[$count]);
+                    return $packed;
+                }"""
+
+            if fast:
+                decoder = decoder.replace('// fastDecodePlaceholder', fast_decoder)
+
+            decoder = jspacker.pack(decoder)
+
+        else:
+            decoder = r"""
+                var %s = function($ascii, $count, $keywords, $encode, $decode) {
+                    $encode = function($charCode) {
+                        return ($charCode < $ascii ? "" : $encode(parseInt($charCode / $ascii))) +
+                            (($charCode = $charCode %% $ascii) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));
+                    };
+                    // fastDecodePlaceholder
+                    var decoder = function($packed, $ascii1, $count1, $keywords1, $encode1, $decode1) {
+                        $count1 = $count;
+                        while ($count1--)
+                            if ($keywords[$count1])
+                                $packed = $packed.replace(new RegExp("\\b" + $encode($count1) + "\\b", "g"), $keywords[$count1]);
+                        return $packed;
+                    };
+                    return decoder;
+                }""" % name
+
+            if fast:
+                decoder = decoder.replace('// fastDecodePlaceholder', fast_decoder)
+
+            decoder = jspacker.pack(decoder)
+
+            keywords = self.getKeywords()
+            decoder = "%s(62, %i, '%s'.split('|'), 0, {});" % (decoder, len(keywords), "|".join(keywords))
+
+        return decoder
+
+    def getDecoder(self, input, keyword_var=None, decode_func=None):
+        if keyword_var is None:
+            keywords = self.getKeywords()
+            num_keywords = len(keywords)
+            keywords = "|".join(keywords)
+            keywords = "'%s'.split('|')" % keywords
+        else:
+            keywords = keyword_var
+            num_keywords = len(self.getKeywords())
+
+        if decode_func is None:
+            decode_func = self.getDecodeFunction()
+
+        escaped_single = input.replace("\\","\\\\").replace("'","\\'").replace('\n','\\n')
+        escaped_double = input.replace("\\","\\\\").replace('"','\\"').replace('\n','\\n')
+        if len(escaped_single) < len(escaped_double):
+            script = "'%s'" % escaped_single
+        else:
+            script = '"%s"' % escaped_double
+        return "eval(%s(%s,62,%i,%s,0,{}))" % (decode_func, script,
+                                               num_keywords,
+                                               keywords)
+
+
+class Packer:
+    def __init__(self):
+        self.patterns = []
+
+    def copy(self):
+        result = Packer()
+        result.patterns = self.patterns[:]
+        return result
+
+    def _repl(self, match):
+        # store protected part
+        self.replacelist.append(match.group(1))
+        # return escaped index
+        return "\x00%i" % len(self.replacelist)
+
+    def pack(self, input):
+        # list of protected parts
+        self.replacelist = []
+        # escape the escapechar
+        output = input.replace('\x00','\x00\x00')
+        for regexp, replacement, keyword_encoder in self.patterns:
+            if replacement is None:
+                if keyword_encoder is None:
+                    # protect the matched parts
+                    output = regexp.sub(self._repl, output)
+                else:
+                    mapper = KeywordMapper(regexp=regexp,
+                                           encoder=keyword_encoder)
+                    # get keywords
+                    mapper.analyse(output)
+                    # replace keywords
+                    output = mapper.sub(output)
+            else:
+                # substitute
+                output = regexp.sub(replacement, output)
+        # restore protected parts
+        replacelist = list(enumerate(self.replacelist))
+        replacelist.reverse() # from back to front, so 1 doesn't break 10 etc.
+        for index, replacement in replacelist:
+            # we use lambda in here, so the real string is used and no escaping
+            # is done on it
+            before = len(output)
+            regexp = re.compile('(?<!\x00)\x00%i' % (index+1))
+            output = regexp.sub(lambda m:replacement, output)
+        # unescape
+        output = output.replace('\x00\x00','\x00')
+        # done
+        return output
+
+    def protect(self, pattern, flags=None):
+        self.keywordSub(pattern, None, flags)
+
+    def sub(self, pattern, replacement, flags=None):
+        if flags is None:
+            self.patterns.append((re.compile(pattern), replacement, None))
+        else:
+            self.patterns.append((re.compile(pattern, flags), replacement, None))
+
+    def keywordSub(self, pattern, keyword_encoder, flags=None):
+        if flags is None:
+            self.patterns.append((re.compile(pattern), None, keyword_encoder))
+        else:
+            self.patterns.append((re.compile(pattern, flags), None, keyword_encoder))
+
+
+class JavascriptPacker(Packer):
+    def __init__(self, level='safe'):
+        Packer.__init__(self)
+        if level == 'full':
+            # encode local variables. those are preceeded by dollar signs
+            # the amount of dollar signs says how many characters are preserved
+            # any trailing digits are preserved as well
+            # $name -> n, $$name -> na, $top1 -> t1, $top2 -> t2
+            def _dollar_replacement(match):
+                length = len(match.group(2))
+                start = length - max(length - len(match.group(3)), 0)
+                result = match.group(1)[start:start+length] + match.group(4)
+                return result
+            self.sub(r"""((\$+)([a-zA-Z\$_]+))(\d*)\b""", _dollar_replacement)
+            
+            self.keywordSub(r"""\b_[A-Za-z\d]\w*""", lambda i: "_%i" % i)
+    
+            # protect strings
+            # this is more correct, but needs more testing
+            # it has to be more accurate because of the more aggresive packing later
+            self.protect(r"""(?<=return|..case|.....[=\[|(,?:+])\s*((?P<quote>['"])(?:\\(?P=quote)|\\\n|.)*?(?P=quote))""", re.DOTALL)
+        else:
+            # protect strings
+            # these sometimes catch to much, but in safe mode this doesn't hurt
+            self.protect(r"""('(?:\\'|\\\n|.)*?')""")
+            self.protect(r'''("(?:\\"|\\\n|.)*?")''')
+        # protect regular expressions
+        self.protect(r"""\s+(\/[^\/\n\r\*][^\/\n\r]*\/g?i?)""")
+        self.protect(r"""([^\w\$\/'"*)\?:]\/[^\/\n\r\*][^\/\n\r]*\/g?i?)""")
+        # multiline comments
+        self.sub(r'/\*(?!@).*?\*/', '', re.DOTALL)
+        # one line comments
+        self.sub(r'\s*//.*$', '', re.MULTILINE)
+        # strip whitespace at the beginning and end of each line
+        self.sub(r'^[ \t\r\f\v]*(.*?)[ \t\r\f\v]*$', r'\1', re.MULTILINE)
+        # whitespace after some special chars but not
+        # before function declaration
+        self.sub(r'([{;\[(,=&|\?:<>%!/])\s+(?!function)', r'\1')
+        # after an equal sign a function definition is ok
+        self.sub(r'=\s+(?=function)', r'=')
+        if level == 'full':
+            # whitespace after some more special chars
+            self.sub(r'([};\):,])\s+', r'\1')
+        # whitespace before some special chars
+        self.sub(r'\s+([={},&|\?:\.()<>%!/\]])', r'\1')
+        # whitespace before plus chars if no other plus char before it
+        self.sub(r'(?<!\+)\s+\+', '+')
+        # whitespace after plus chars if no other plus char after it
+        self.sub(r'\+\s+(?!\+)', '+')
+        # whitespace before minus chars if no other minus char before it
+        self.sub(r'(?<!-)\s+-', '-')
+        # whitespace after minus chars if no other minus char after it
+        self.sub(r'-\s+(?!-)', '-')
+        # remove redundant semi-colons
+        self.sub(r';+\s*([};])', r'\1')
+        # remove any excessive whitespace left except newlines
+        self.sub(r'[ \t\r\f\v]+', ' ')
+        # excessive newlines
+        self.sub(r'\n+', '\n')
+        # first newline
+        self.sub(r'^\n', '')
+
+
+class CSSPacker(Packer):
+    def __init__(self, level='safe'):
+        Packer.__init__(self)
+        # protect strings
+        # these sometimes catch to much, but in safe mode this doesn't hurt
+        self.protect(r"""('(?:\\'|\\\n|.)*?')""")
+        self.protect(r'''("(?:\\"|\\\n|.)*?")''')
+        # strip whitespace
+        self.sub(r'^[ \t\r\f\v]*(.*?)[ \t\r\f\v]*$', r'\1', re.MULTILINE)
+        # remove comment contents
+        self.sub(r'/\*.*?( ?[\\/*]*\*/)', r'/*\1', re.DOTALL)
+        # remove lines with comments only (consisting of stars only)
+        self.sub(r'^/\*+\*/$', '', re.MULTILINE)
+        # excessive newlines
+        self.sub(r'\n+', '\n')
+        # first newline
+        self.sub(r'^\n', '')
+
+        if level == 'full':
+            #remove more whitespace
+            self.sub(r'([{,;])\s+', r'\1')
+
+
+## jspacker = JavascriptPacker('safe')
+## jspacker_full = JavascriptPacker('full')
+
+## def run():
+    ## script = open('cssQuery.js').read()
+    ## script = jspacker_full.pack(script)
+    ## open('output.js','w').write(script)
+    ## mapper = JavascriptKeywordMapper()
+    ## mapper.analyse(script)
+    ## keywords = mapper.getKeywords()
+    ## script = mapper.sub(script)
+    ## f = open('output1.js','w')
+    ## #f.write("keywords='%s'.split('|');\n" % "|".join(keywords))
+    ## #f.write(mapper.getDecodeFunction(name='__dEcOdE'))
+    ## f.write(mapper.getDecoder(script))
+    ## for index, keyword in enumerate(keywords):
+        ## encoded = mapper._encode(index)
+        ## if keyword == '':
+            ## replacement = encoded
+        ## else:
+            ## replacement = keyword
+        ## regexp = re.compile(r'\b%s\b' % encoded)
+        ## script = regexp.sub(lambda m: replacement, script)
+    ## open('output2.js','w').write(script)
+
+## if __name__=='__main__':
+    ## run()


Property changes on: z3ext.resource/trunk/src/z3ext/resource/stylesheet/packer.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/stylesheet/resource.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/stylesheet/resource.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/stylesheet/resource.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,97 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" 
+
+$Id$
+"""
+from zope import interface
+from zope.component.factory import Factory
+from zope.app.component.hooks import getSite
+
+from z3c.zrtresource.replace import Replace
+from z3c.zrtresource.processor import ZRTProcessor
+
+from z3ext.resource.fileresource import File, FileResource
+from z3ext.resource.interfaces import IResourceFactory, IResourceFactoryType
+
+from packer import CSSPacker
+
+packers = {'full': CSSPacker('full'),
+           'save': CSSPacker('save')}
+
+
+class ZRTFileResource(FileResource):
+    """ zrt resource """
+    
+    _commands_file = ''
+
+    def __init__(self, context, request, media='', compression='', **kw):
+        self.context = context
+        self.request = request
+        self.media = media
+        self.compression = compression
+
+    def index_html(self, *args):
+        """ make ResourceRegistry happy """
+        value = self.GET()
+        if isinstance(value, unicode):
+            value = value.encode('utf-8')
+        return value
+
+    def render(self, request):
+        file = self.chooseContext()
+        f = open(file.path,'rb')
+        data = f.read()
+        f.close()
+        p = ZRTProcessor(data, commands={'replace': Replace})
+        return p.process(getSite(), self.request)
+
+    def GET(self):
+        """ default content """
+        data = super(ZRTFileResource, self).GET()
+        if self.request.response.getStatus() == 304:
+            return ''
+
+	# Process the file
+        p = ZRTProcessor(data, commands={'replace': Replace})
+        result = p.process(getSite(), self.request)
+
+        packer = packers.get(self.compression)
+        if packer is not None:
+            result = packer.pack(result)
+
+        if self.media:
+            result = '@media %s { %s}' % (self.media, result)
+
+        return result
+
+
+class StylesheetResourceFactory(object):
+    interface.implements(IResourceFactory)
+
+    def __init__(self, path, checker, name):
+        self.__file = File(path, name)
+        self.__checker = checker
+        self.__name = name
+
+    def __call__(self, request, **kwargs):
+        resource = ZRTFileResource(self.__file, request, **kwargs)
+        resource.__Security_checker__ = self.__checker
+
+        resource.__name__ = self.__name
+        return resource
+
+
+stylesheetfactory = Factory(StylesheetResourceFactory)
+interface.directlyProvides(stylesheetfactory, IResourceFactoryType)


Property changes on: z3ext.resource/trunk/src/z3ext/resource/stylesheet/resource.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/stylesheet/stylesheet.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/stylesheet/stylesheet.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/stylesheet/stylesheet.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,69 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+from zope.component import getMultiAdapter
+from zope.app.component.hooks import getSite
+from zope.traversing.browser.interfaces import IAbsoluteURL
+
+from packer import CSSPacker
+from resource import Resource
+from package import Package, PackageFactory
+
+packers = {'full': CSSPacker('full'),
+           'save': CSSPacker('save')}
+
+
+class Stylesheet(Resource):
+
+    def __init__(self, path, name='', media='all', rel='stylesheet', compression='full'):
+        super(Stylesheet, self).__init__(path)
+
+        self.rel = rel
+        self.media = media
+        self.compression = compression
+        self.resource_path = '++resource++/%s'%path
+
+    def render(self, request, compress=True):
+        content = super(Stylesheet, self).render(request, compress=True)
+
+        if compress:
+            packer = packers.get(self.compression, None)
+            if packer is not None:
+                content = packer.pack(content)
+
+        url = '%s/%s'%(
+	    str(getMultiAdapter((getSite(), request), IAbsoluteURL)), self.resource_path)
+        content = ' /* %s */\n%s'%(url, content)
+
+        if self.media:
+            content = '@media %s { %s}' % (self.media, content)
+
+        return content
+
+
+class StylesheetPackage(Package):
+    type = u'stylesheet'
+    content_type = u'text/css'
+
+    def link(self):
+        return '<link rel="stylesheet" type="text/css" media="screen" href="%s" />'%self()
+
+    def addResource(self, path, **kw):
+        self.resources.append(Stylesheet(path, **kw))
+
+
+packageFactory = PackageFactory(StylesheetPackage)


Property changes on: z3ext.resource/trunk/src/z3ext/resource/stylesheet/stylesheet.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/tests.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/tests.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/tests.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,67 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""test setup
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import doctest, unittest
+from zope import component
+from zope.app.testing import placelesssetup
+from zope.traversing import testing
+from zope.traversing.interfaces import ITraversable
+from zope.traversing.namespace import view
+from zope.app.publisher.browser.fileresource import FileResource
+
+from z3ext.resource import fileresource
+from z3ext.resource.interfaces import IResourceFactoryType
+
+
+class CustomResource(fileresource.FileResource):
+    pass
+    
+
+class CustomFileResourceFactory(fileresource.FileResourceFactory):
+
+    def __call__(self, request):
+        resource = CustomResource(self._file, request)
+        resource.__Security_checker__ = self._checker
+        resource.__name__ = self._name
+        return resource
+
+
+def setUp(test):
+    placelesssetup.setUp(test)
+    testing.setUp()
+    component.provideAdapter(view, (None, None), ITraversable, name="view")
+
+    component.provideUtility(
+        fileresource.filefactory, IResourceFactoryType)
+    component.provideUtility(
+        fileresource.filefactory, IResourceFactoryType, name='fileresource')
+    component.provideUtility(
+        fileresource.imagefactory, IResourceFactoryType, name='imageresource')
+
+    component.provideAdapter(
+        fileresource.FileResourceAdapter, (FileResource,))
+
+
+def test_suite():
+    return unittest.TestSuite((
+            doctest.DocFileSuite(
+                'README.txt',
+                setUp=setUp, tearDown=placelesssetup.tearDown,
+                optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+         ))


Property changes on: z3ext.resource/trunk/src/z3ext/resource/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/zcml.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/zcml.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/zcml.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,264 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" z3ext:resource and z3ext:directoryResource directives implementation
+
+$Id$
+"""
+import os, posixpath
+
+from zope import schema, interface
+from zope.component.zcml import handler
+from zope.component import getUtility, queryUtility
+
+from zope.configuration import fields
+from zope.configuration.exceptions import ConfigurationError
+
+from zope.publisher.browser import BrowserView
+from zope.publisher.interfaces import NotFound
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.interfaces.browser import IBrowserPublisher
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+
+from zope.security.checker import CheckerPublic, NamesChecker
+
+from zope.app.publisher.browser import metadirectives
+from zope.app.publisher.browser.resource import Resource
+from zope.app.publisher.browser.resources import empty
+from zope.app.publisher.browser.resourcemeta import allowed_names
+from zope.app.publisher.browser.directoryresource import Directory
+
+from z3ext.resource import fileresource
+from z3ext.resource.interfaces import IResourceFactory, IResourceFactoryType
+
+_marker = object()
+
+
+class IResourceDirective(metadirectives.IBasicResourceInformation):
+    """ z3ext:resource directive """
+
+    name = schema.TextLine(
+        title = u"The name of the resource",
+        description = u"""
+        This is the name used in resource urls. Resource urls are of
+        the form site/@@/resourcename, where site is the url of
+        "site", a folder with a site manager.
+
+        We make resource urls site-relative (as opposed to
+        content-relative) so as not to defeat caches.""",
+        required = True)
+
+    type = schema.TextLine(
+        title = u'Resource type',
+        required = False)
+
+    file = fields.Path(
+        title = u'Resource file',
+        description = u'The file containing the resource data.',
+        required = True)
+
+# Arbitrary keys and values are allowed to be passed to the CustomWidget.
+IResourceDirective.setTaggedValue('keyword_arguments', True)
+
+
+class IResourceDirectoryDirective(metadirectives.IResourceDirectoryDirective):
+
+    mapping = fields.Tokens(
+        title = u'Resource mapping',
+        description = u'By default resourceDirective tries determine resource '\
+            u"type by it's extension, but we can define mapping explicitly."\
+            u'Format ".{extention}:{resource type}" or "{filename}:{resource type}',
+        required=False,
+        value_type=schema.TextLine())
+
+
+class IResourceDirectoryResourceType(interface.Interface):
+
+    file = schema.TextLine(
+        title = u'Filename',
+        required = True)
+
+    type = schema.TextLine(
+        title = u'Resource type',
+        required = True)
+
+
+# Arbitrary keys and values are allowed to be passed to the CustomWidget.
+IResourceDirectoryResourceType.setTaggedValue('keyword_arguments', True)
+
+
+def resourceDirective(_context, name, file, layer=IDefaultBrowserLayer,
+                      permission='zope.Public', type='', **kwargs):
+    if permission == 'zope.Public':
+        permission = CheckerPublic
+
+    checker = NamesChecker(allowed_names, permission)
+
+    _context.action(
+        discriminator = ('z3ext.resource', name, IBrowserRequest, layer),
+        callable = resourceHandler,
+        args = (name, layer, checker, file, type, _context.info),
+        )
+
+
+def resourceHandler(name, layer, checker, file, type, info):
+    if type:
+        factory = getUtility(IResourceFactoryType, name=type)
+    else:
+        type = os.path.splitext(os.path.normcase(name))[1][1:]
+
+        factory = queryUtility(IResourceFactoryType, name=type)
+        if factory is None:
+            factory = getUtility(IResourceFactoryType)
+
+    factory = factory(file, checker, name)
+
+    handler('registerAdapter',
+            factory, (layer,), interface.Interface, name, info)
+
+
+class ResourceDirectoryDirective(object):
+
+
+    def __init__(self, _context, name, directory, layer=IDefaultBrowserLayer,
+                 permission='zope.Public', mapping=()):
+        if permission == 'zope.Public':
+            permission = CheckerPublic
+
+        checker = NamesChecker(
+            allowed_names + ('__getitem__', 'get'), permission)
+
+        if not os.path.isdir(directory):
+            raise ConfigurationError(
+                "Directory %s does not exist" % directory)
+
+        # process resource types
+        types = {}
+        for l in mapping:
+            fname, type = l.split(':')
+            info = {'file': None,
+                    'ext': None,
+                    'type': type}
+            if fname[0] == '.':
+                info['ext'] = fname[1:]
+            else:
+                info['file'] = fname
+            types[fname] = info
+
+        factory = DirectoryResourceFactory(directory, checker, name, types)
+        _context.action(
+            discriminator = ('z3ext.resource', name, IBrowserRequest, layer),
+            callable = handler,
+            args = ('registerAdapter',
+                    factory, (layer,), interface.Interface, name, _context.info))
+
+        self.factory = factory
+
+    def resourceType(self, _context, file, type, **kwargs):
+        info = {'file': None,
+                'ext': None,
+                'type': type}
+        info.update(kwargs)
+
+        if file[0] == '.':
+            info['ext'] = file[1:]
+        else:
+            info['file'] = file
+        
+        self.factory.types[file] = info
+
+
+class DirectoryResource(BrowserView, Resource):
+
+    interface.implements(IBrowserPublisher)
+
+    default_factory = fileresource.filefactory
+
+    types = {}
+
+    def publishTraverse(self, request, name):
+        '''See interface IBrowserPublisher'''
+        return self.get(name)
+
+    def browserDefault(self, request):
+        '''See interface IBrowserPublisher'''
+        return empty, ()
+
+    def __getitem__(self, name):
+        res = self.get(name, None)
+        if res is None:
+            raise KeyError(name)
+        return res
+
+    def get(self, name, default=_marker):
+        request = self.request
+        path = self.context.path
+        filename = os.path.join(path, name)
+        isfile = os.path.isfile(filename)
+        isdir = os.path.isdir(filename)
+
+        rname = posixpath.join(self.__name__, name)
+
+        if not (isfile or isdir):
+            if default is _marker:
+                raise NotFound(self, name)
+            return default
+
+        if isfile:
+            types = self.types
+
+            info = types.get(name)
+            if info is None:
+                ext = os.path.splitext(os.path.normcase(name))[1]
+                info = types.get(ext)
+
+            if info is not None:
+                if info['type'] == 'null':
+                    raise NotFound(self, name)
+
+                factoryType = queryUtility(IResourceFactoryType, name=info['type'])
+                if factoryType is not None:
+                    factory = factoryType(filename, self.context.checker, rname)
+
+                    if IResourceFactory.providedBy(factory):
+                        resource = factory(request, **info)
+                    else:
+                        resource = factory(request)
+
+                    resource.__parent__ = self
+                    return resource
+
+            resource = self.default_factory(
+                filename, self.context.checker, rname)(request)
+        else:
+            resource = DirectoryResourceFactory(
+                filename, self.context.checker, rname, self.types)(request)
+
+        resource.__parent__ = self
+        return resource
+
+
+class DirectoryResourceFactory(object):
+
+    def __init__(self, path, checker, name, types={}):
+        self._dir = Directory(path, checker, name)
+        self._checker = checker
+        self._name = name
+        self.types = types
+
+    def __call__(self, request):
+        resource = DirectoryResource(self._dir, request)
+        resource.__Security_checker__ = self._checker
+        resource.__name__ = self._name
+        resource.types = self.types
+        return resource


Property changes on: z3ext.resource/trunk/src/z3ext/resource/zcml.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/zrtresource/__init__.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/zrtresource/__init__.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/zrtresource/__init__.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.


Property changes on: z3ext.resource/trunk/src/z3ext/resource/zrtresource/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/zrtresource/configure.zcml
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/zrtresource/configure.zcml	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/zrtresource/configure.zcml	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,12 @@
+<configure 
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:browser="http://namespaces.zope.org/browser">
+
+  <include package="z3c.zrtresource" file="meta.zcml" />
+
+  <utility
+     name="zrt"
+     provides="z3ext.resource.resource.IResourceFactoryType"
+     component=".zrtresource.zrtfactory" />
+
+</configure>

Added: z3ext.resource/trunk/src/z3ext/resource/zrtresource/processor.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/zrtresource/processor.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/zrtresource/processor.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,104 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import re
+from zope.component import queryUtility
+
+from z3c.zrtresource.processor import ZRTProcessor, COMMAND_REGEX, COMMAND, TEXTBLOCK
+from z3c.zrtresource.interfaces import IZRTCommand, IZRTCommandFactory, UnknownZRTCommand
+
+EXTERNAL_COMMAND = 2
+
+
+class ExtZRTProcessor(ZRTProcessor):
+    """A ZRT Processor"""
+
+    def __init__(self, source, commands=None, commands_file=''):
+        super(ExtZRTProcessor, self).__init__(source, commands)
+        self.commands_file = commands_file
+
+
+    def compile(self):
+        """See interfaces.IZRTProcessor"""
+	# get extra commands from external file
+        extra = ''
+        try:
+            if bool(self.commands_file):
+                extra = open(self.commands_file, 'r').read() + '\n'
+        except IOError:
+            pass
+
+        bytecode = []
+        pos = 0
+
+        regex = re.compile(COMMAND_REGEX %(self.commandStartRegex,self.commandEndRegex))
+
+        # Find all commands
+        for match in regex.finditer(self.source):
+            command, args = match.groups()
+
+            # Add the previous text block and update position
+            bytecode.append((TEXTBLOCK, self.source[pos:match.start()]))
+            pos = match.end() + 1
+
+            # Make sure the command exists
+            if command not in self.commands:
+                cmd = queryUtility(IZRTCommandFactory, command)
+
+                if cmd is None:
+                    raise UnknownZRTCommand(command)
+
+                # Add the command
+                bytecode.append((EXTERNAL_COMMAND, 
+                                 (cmd, args, match.start(), match.end())))
+            else:
+                # Add the command
+                bytecode.append((COMMAND,
+                                 (command, args, match.start(), match.end())))
+
+        # Add the final textblock
+        bytecode.append((TEXTBLOCK, self.source[pos:]))
+
+        self._bytecode = bytecode
+
+        bytecode = []
+        pos = 0
+
+        # Find all commands
+        for match in regex.finditer(extra):
+            command, args = match.groups()
+
+            # Add the previous text block and update position
+            pos = match.end() + 1
+
+            # Make sure the command exists
+            if command not in self.commands:
+                cmd = queryUtility(IZRTCommandFactory, command)
+
+                if cmd is None:
+                    raise UnknownZRTCommand(command)
+
+                # Add the command
+                bytecode.append((EXTERNAL_COMMAND, 
+                                 (cmd, args, match.start(), match.end())))
+            else:
+                # Add the command
+                bytecode.append((COMMAND,
+                                 (command, args, match.start(), match.end())))
+
+
+        self._bytecode = bytecode + self._bytecode


Property changes on: z3ext.resource/trunk/src/z3ext/resource/zrtresource/processor.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3ext.resource/trunk/src/z3ext/resource/zrtresource/zrtresource.py
===================================================================
--- z3ext.resource/trunk/src/z3ext/resource/zrtresource/zrtresource.py	                        (rev 0)
+++ z3ext.resource/trunk/src/z3ext/resource/zrtresource/zrtresource.py	2008-03-28 11:32:25 UTC (rev 84992)
@@ -0,0 +1,92 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+from zope import interface
+from zope.component.factory import Factory
+from zope.app.component.hooks import getSite
+
+from z3c.zrtresource.replace import Replace
+
+from z3ext.resource.interfaces import IResourceFactoryType
+from z3ext.resource.fileresource import File, FileResource
+
+from processor import ExtZRTProcessor
+
+
+class ZRTFileResource(FileResource):
+    """ zrt resource """
+    
+    _commands_file = ''
+
+    def index_html(self, *args):
+        """ make ResourceRegistry happy """
+        value = self.GET()
+        if isinstance(value, unicode):
+            value = value.encode('utf-8')
+        return value
+
+    def render(self, request):
+        file = self.chooseContext()
+        f = open(file.path,'rb')
+        data = f.read()
+        f.close()
+        p = ExtZRTProcessor(
+            data, commands={'replace': Replace}, 
+            commands_file = self._commands_file)
+        return p.process(getSite(), self.request)
+
+    def GET(self):
+        """ default content """
+        data = super(ZRTFileResource, self).GET()
+        if self.request.response.getStatus() == 304:
+            return ''
+
+	# Process the file
+        p = ExtZRTProcessor(data, commands={'replace': Replace},
+                            commands_file = self._commands_file)
+        return p.process(getSite(), self.request)
+
+
+class ZRTFileResourceFactory(object):
+
+    def __init__(self, path, checker, name, commands_file=''):
+        self.__file = File(path, name)
+        self.__checker = checker
+        self.__name = name
+
+        if not commands_file:
+            if path.endswith('.zrt'):
+                self.__commands_file = '%s.null'%path
+            else:
+                self.__commands_file = '%s.zrt'%path
+        else:
+            self.__commands_file = commands_file
+
+    def __call__(self, request):
+        resource = ZRTFileResource(self.__file, request)
+        resource.__Security_checker__ = self.__checker
+
+        name = self.__name
+        if name.endswith('.zrt'):
+            name = name[:-4]
+        resource.__name__ = name
+        resource._commands_file = self.__commands_file
+        return resource
+
+
+zrtfactory = Factory(ZRTFileResourceFactory)
+interface.directlyProvides(zrtfactory, IResourceFactoryType)


Property changes on: z3ext.resource/trunk/src/z3ext/resource/zrtresource/zrtresource.py
___________________________________________________________________
Name: svn:keywords
   + Id



More information about the Checkins mailing list