############################################################################## # # Copyright (c) 2004, 2005 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. # ############################################################################## """Json directives Directives of the 'http://namespaces.zope.org/json' namespace This is based on how it is done in Five, since we really want to extend its behaviour only to allow traversals to json pages """ import os from zope.interface import Interface from zope.component import getGlobalService, ComponentLookupError from zope.configuration.exceptions import ConfigurationError try: # Zope 2.8 backward compatibility from zope.component.servicenames import Presentation except ImportError: # Zope 2.9 _pre_2_9 = False from zope.publisher.interfaces.browser import IDefaultBrowserLayer _layer = IDefaultBrowserLayer else: # Zope 2.8 backward compatibility _pre_2_9 = True _layer = 'default' from zope.app.publisher.browser.viewmeta import pages as zope_app_pages from zope.app.component.metaconfigure import handler from Products.Five.browser import BrowserView from Products.Five.metaclass import makeClass from Products.Five.security import getSecurityInfo, protectClass, protectName,\ initializeClass try: from Products.Five.pagetemplatefile import ZopeTwoPageTemplateFile from Products.Five.browserconfigure import _handle_for, _handle_menu, ViewMixinForAttributes, \ ViewMixinForTemplates, makeClassForTemplate except ImportError: # No idea which is the caninonical path. This was needed for Five installed for Zope 2.7. from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile from Products.Five.browser.metaconfigure import _handle_for, _handle_menu, ViewMixinForAttributes, \ ViewMixinForTemplates, makeClassForTemplate from interfaces import IJsonRequest def page(_context, name, permission, for_, layer=_layer, template=None, class_=None, allowed_interface=None, allowed_attributes=None, attribute='__call__', menu=None, title=None, ): if _pre_2_9: try: s = getGlobalService(Presentation) except ComponentLookupError, err: pass _handle_menu(_context, menu, title, [for_], name, permission) if not (class_ or template): raise ConfigurationError("Must specify a class or template") if allowed_attributes is None: allowed_attributes = [] if allowed_interface is not None: for interface in allowed_interface: if _pre_2_9: attrs = [n for n, d in interface.namesAndDescriptions(1)] allowed_attributes.extend(attrs) else: allowed_attributes.extend(interface.names()) if attribute != '__call__': if template: raise ConfigurationError( "Attribute and template cannot be used together.") if not class_: raise ConfigurationError( "A class must be provided if attribute is used") if template: template = os.path.abspath(str(_context.path(template))) if not os.path.isfile(template): raise ConfigurationError("No such file", template) if class_: # new-style classes do not work with Five. As we want to import # packages from z3 directly, we ignore new-style classes for now. if type(class_) == type: return if attribute != '__call__': if not hasattr(class_, attribute): raise ConfigurationError( "The provided class doesn't have the specified attribute " ) cdict = getSecurityInfo(class_) if _pre_2_9: pass else: cdict['__name__'] = name if template: if _pre_2_9: new_class = makeClassForTemplate(template, bases=(class_, ), cdict=cdict) else: new_class = makeClassForTemplate(template, bases=(class_, ), cdict=cdict, name=name) elif attribute != "__call__": # we're supposed to make a page for an attribute (read: # method) and it's not __call__. We thus need to create a # new class using our mixin for attributes. cdict.update({'__page_attribute__': attribute}) new_class = makeClass(class_.__name__, (class_, ViewMixinForAttributes, ), cdict) # in case the attribute does not provide a docstring, # ZPublisher refuses to publish it. So, as a workaround, # we provide a stub docstring func = getattr(new_class, attribute) if not func.__doc__: # cannot test for MethodType/UnboundMethod here # because of ExtensionClass if hasattr(func, 'im_func'): # you can only set a docstring on functions, not # on method objects func = func.im_func func.__doc__ = "Stub docstring to make ZPublisher work" else: # we could use the class verbatim here, but we'll execute # some security declarations on it so we really shouldn't # modify the original. So, instead we make a new class # with just one base class -- the original new_class = makeClass(class_.__name__, (class_, ), cdict) else: if _pre_2_9: # template new_class = makeClassForTemplate(template, ) else: # template new_class = makeClassForTemplate(template, name=name) _handle_for(_context, for_) if _pre_2_9: _context.action( discriminator = ('view', for_, name, IJsonRequest, layer), callable = handler, args = (Presentation, 'provideAdapter', IJsonRequest, new_class, name, [for_], Interface, layer, _context.info), ) else: _context.action( discriminator = ('view', for_, name, IJsonRequest, layer), callable = handler, args = ('provideAdapter', (for_, layer), Interface, name, new_class, _context.info), ) _context.action( discriminator = ('five:protectClass', new_class), callable = protectClass, args = (new_class, permission) ) if allowed_attributes: for attr in allowed_attributes: _context.action( discriminator = ('five:protectName', new_class, attr), callable = protectName, args = (new_class, attr, permission) ) _context.action( discriminator = ('five:initialize:class', new_class), callable = initializeClass, args = (new_class,) ) class pages(zope_app_pages): def page(self, _context, name, attribute='__call__', template=None, menu=None, title=None): return page(_context, name=name, attribute=attribute, template=template, menu=menu, title=title, **(self.opts))