[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