diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..e69de29
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..84da1b5
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,44 @@
+from setuptools import setup, find_packages
+import sys, os
+
+version = '1.0'
+shortdesc = 'Handle web application parts as tiles.'
+longdesc = open(os.path.join(os.path.dirname(__file__), 'src', 'bda', 'bfg',
+ 'tile', '_api.txt')).read()
+
+setup(name='bda.bfg.tile',
+ version=version,
+ description=shortdesc,
+ long_description=longdesc,
+ classifiers=[
+ 'Development Status :: 3 - Alpha',
+ 'Environment :: Web Environment',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
+ ],
+ keywords='',
+ author='BlueDynamics Alliance',
+ author_email='dev@bluedynamics.com',
+ url=u'https://svn.bluedynamics.net/svn/internal/bda.tile',
+ license='GNU General Public Licence',
+ packages=find_packages('src'),
+ package_dir = {'': 'src'},
+ namespace_packages=['bda', 'bda.bfg'],
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=[
+ 'setuptools',
+ 'repoze.bfg',
+ ],
+ dependency_links = [
+ "http://dist.repoze.org/bfg/1.0/",
+ ],
+ extras_require = dict(
+ test=[
+ 'interlude',
+ ]
+ ),
+ tests_require=['interlude'],
+ test_suite="bda.bfg.tile.tests.test_suite",
+ )
\ No newline at end of file
diff --git a/src/bda/__init__.py b/src/bda/__init__.py
new file mode 100644
index 0000000..b0d6433
--- /dev/null
+++ b/src/bda/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
\ No newline at end of file
diff --git a/src/bda/bfg/__init__.py b/src/bda/bfg/__init__.py
new file mode 100644
index 0000000..b0d6433
--- /dev/null
+++ b/src/bda/bfg/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
\ No newline at end of file
diff --git a/src/bda/bfg/tile/__init__.py b/src/bda/bfg/tile/__init__.py
new file mode 100644
index 0000000..f521de3
--- /dev/null
+++ b/src/bda/bfg/tile/__init__.py
@@ -0,0 +1,7 @@
+from _api import render_template
+from _api import render_template_to_response
+from _api import ITile
+from _api import TileRenderer
+from _api import Tile
+from _api import registerTile
+from _api import tile
diff --git a/src/bda/bfg/tile/_api.py b/src/bda/bfg/tile/_api.py
new file mode 100644
index 0000000..80df1c0
--- /dev/null
+++ b/src/bda/bfg/tile/_api.py
@@ -0,0 +1,176 @@
+import os
+from zope.interface import Interface, Attribute, implements
+from zope.component import queryUtility
+from zope.component import getMultiAdapter
+from zope.component import ComponentLookupError
+from repoze.bfg.threadlocal import get_current_registry
+from webob import Response
+from webob.exc import HTTPFound
+from repoze.bfg.interfaces import IRequest
+from repoze.bfg.interfaces import IResponseFactory
+from repoze.bfg.interfaces import IAuthenticationPolicy
+from repoze.bfg.interfaces import IViewPermission
+from repoze.bfg.path import caller_package
+from repoze.bfg.renderers import template_renderer_factory
+from repoze.bfg.chameleon_zpt import ZPTTemplateRenderer
+#from repoze.bfg.security import ViewPermissionFactory
+from repoze.bfg.security import has_permission
+
+class ITile(Interface):
+ """returns on call some HTML snippet."""
+
+ def __call__(model, request):
+ """Renders the tile.
+
+ It's intended to work this way: First it calls its own prepare method,
+ then it checks its own show attribute. If this returns True it renders
+ the template in the context of the ITile implementing class instance.
+ """
+
+ def prepare():
+ """Prepares the tile.
+
+ I.e. fetch data to display ...
+ """
+
+ show = Attribute("""Render this tile?""")
+
+def _update_kw(**kw):
+ if not ('request' in kw and 'model' in kw):
+ raise ValueError, "Eexpected kwargs missing: model, request."
+ kw.update({'tile': TileRenderer(kw['model'], kw['request'])})
+ return kw
+
+def _redirect(kw):
+ if kw['request'].environ.get('redirect'):
+ return True
+ return False
+
+def render_template(path, **kw):
+ kw = _update_kw(**kw)
+ if _redirect(kw):
+ return u''
+ renderer = template_renderer_factory(path, ZPTTemplateRenderer)
+ return renderer(kw, {})
+
+def render_template_to_response(path, **kw):
+ kw = _update_kw(**kw)
+ kw['request'].environ['redirect'] = None
+ renderer = template_renderer_factory(path, ZPTTemplateRenderer)
+ result = renderer(kw, {})
+ if _redirect(kw):
+ return HTTPFound(location=kw['request'].environ['redirect'])
+ response_factory = queryUtility(IResponseFactory, default=Response)
+ return response_factory(result)
+
+class Tile(object):
+ implements(ITile)
+
+ def __init__(self, path, attribute):
+ self.path = path
+ self.attribute = attribute
+
+ def __call__(self, model, request):
+ self.model = model
+ self.request = request
+ self.prepare() # XXX maybe remove.
+ if not self.show:
+ return u''
+ if self.path:
+ try:
+ # XXX: do not catch exception.
+ return render_template(self.path, request=request,
+ model=model, context=self)
+ except Exception, e:
+ return u"Error:
%s" % e + renderer = getattr(self, self.attribute) + result = renderer() + return result + + @property + def show(self): + return True + + def prepare(self): + pass + + def render(self): + return u'' + + def redirect(self, url): + self.request.environ['redirect'] = url + + @property + def nodeurl(self): + relpath = [p for p in self.model.path if p is not None] + return '/'.join([self.request.application_url] + relpath) + +class TileRenderer(object): + + def __init__(self, model, request): + self.model, self.request = model, request + + def __call__(self, name): + registry = get_current_registry() + # XXX fix me. new API in repoze.bfg + #secured = not not registry.queryUtility(IAuthenticationPolicy) + #if secured: + # permitted = registry.getMultiAdapter((self.model, self.request), + # IViewPermission, + # name=name) + # if not permitted: + # return u'permission denied' + try: + tile = getMultiAdapter((self.model, self.request), ITile, name=name) + except ComponentLookupError, e: + return u"Tile with name '%s' not found:
%s" % \ + (name, e) + return tile + +# Registration +def registerTile(name, path=None, attribute='render', + interface=Interface, _class=Tile, permission='view'): + if isinstance(interface, basestring): + pass # XXX: lookup + if path: + if not (':' in path or os.path.isabs(path)): + caller = caller_package(level=1) + path = '%s:%s' % (caller.__name__, path) + factory = _class(path, attribute) + registry = get_current_registry() + registry.registerAdapter(factory, [interface, IRequest], + ITile, name, event=False) + # XXX fix me. new API in repoze.bfg + #if permission: + # factory = ViewPermissionFactory(permission) + # registry.registerAdapter(factory, [interface, IRequest], + # IViewPermission, name) + +class tile(object): + """Tile decorator. + """ + + def __init__(self, name, path=None, attribute='render', + interface=Interface, permission='view', level=2): + """name to register as, path to template, interface adapting to. + level is a special to make doctests pass the magic path-detection. + you should never need latter. + """ + self.name = name + self.path = path + if path: + if not (':' in path or os.path.isabs(path)): + caller = caller_package(level) + self.path = '%s:%s' % (caller.__name__, path) + self.attribute = attribute + self.interface = interface + self.permission = permission + + def __call__(self, ob): + registerTile(self.name, + self.path, + self.attribute, + interface=self.interface, + _class=ob, + permission=self.permission) + return ob \ No newline at end of file diff --git a/src/bda/bfg/tile/_api.txt b/src/bda/bfg/tile/_api.txt new file mode 100644 index 0000000..e44436a --- /dev/null +++ b/src/bda/bfg/tile/_api.txt @@ -0,0 +1,63 @@ +A tile is a piece of web application, i.e. a form, a navigation, etc. + +Splitting your application in such small and logic application parts makes it +easy to re-use this application, simplifies application ajaxification and +the use of same application parts in different manners. + +Imports.: + + >>> from bda.bfg.tile import Tile + >>> from bda.bfg.tile import TileRenderer + >>> from bda.bfg.tile import registerTile + >>> from bda.bfg.tile import tile + +We need some dummies as model and request.: + + >>> class Model(object): pass + >>> model = Model() + >>> from repoze.bfg.request import DEFAULT_REQUEST_FACTORIES + >>> request = DEFAULT_REQUEST_FACTORIES[None]['factory'](environ={}) + +The pure Tile itself. Normally you do not create this directly, this is done +due registration, see below.: + + >>> mytile = Tile('testdata/tile1.pt', None) + >>> mytile(model, request) + u'Tile One' + +Register a tile using the prior template testtemplate. When no object is given, +the default tile is instanciated as above.: + + >>> registerTile('tileone', 'testdata/tile1.pt') + +Render the already registered tile.: + + >>> TileRenderer(model, request)('tileone') + u'Tile One' + +Now the decorator - level=1 is needed for the doctest only to reduce the module +level.: + + >>> @tile('tiletwo', 'testdata/tile2.pt', level=1) + ... class Tile2(Tile): + ... data = u'custom' + >>> TileRenderer(model, request)('tiletwo') + u'Tile Two: Tile One' + +You can define an attribute which is responsible to render the tile instead of +defining a template. By default ``render`` is taken. With the keyword argument +``attribute`` you can point to a different attribute.: + + >>> @tile('attrtile') + ... class Tile2(Tile): + ... def render(self): + ... return u'