Back to index

plone3  3.1.7
browser.py
Go to the documentation of this file.
00001 from os.path import sep, isabs, split, basename
00002 from Acquisition import aq_inner
00003 
00004 from Products.Five.component.interfaces import IObjectManagerSite
00005 from Products.Five.component import findSite
00006 from Products.Five.browser import BrowserView
00007 
00008 from zope.interface import providedBy, Interface
00009 from zope.component import getMultiAdapter, getSiteManager
00010 from zope.component import getUtility, queryUtility
00011 from zope.dottedname.resolve import resolve
00012 from zope.interface.interfaces import IInterface
00013 from zope.schema.interfaces import IVocabularyFactory
00014 from zope.publisher.interfaces.browser import IBrowserRequest
00015 from zope.traversing.browser import absoluteURL
00016 from zope.app.apidoc.presentation import getViews
00017 
00018 from five.customerize.zpt import TTWViewTemplate
00019 from five.customerize.interfaces import IViewTemplateContainer
00020 
00021 
00022 def mangleAbsoluteFilename(filename):
00023     """
00024     Mangle an absolute filename when the file happens to be in a
00025     package.  The mangled name will then be of the form::
00026 
00027       <dotted package name>/<base filename>.
00028 
00029     For example, let's take Five's configure.zcml as an example.  We
00030     assemble it in an OS-independent way so this test works on all
00031     platforms:
00032     
00033       >>> def filesystemPath(*elements):
00034       ...     return sep.join(elements)
00035 
00036     We see that the filename is now mangled:
00037 
00038       >>> mangleAbsoluteFilename(filesystemPath(
00039       ...     '', 'path', 'to', 'Products', 'Five', 'configure.zcml'))
00040       'Products.Five/configure.zcml'
00041 
00042     The name of a file that's not in a package is returned unchanged:
00043 
00044       >>> not_in_a_package = filesystemPath('', 'path', 'to', 'configure.zcml')
00045       >>> mangleAbsoluteFilename(not_in_a_package) == not_in_a_package
00046       True
00047     """
00048     if not isabs(filename):
00049         raise ValueError("Can only determine package for absolute filenames")
00050     dir, base = split(filename)
00051     pieces = dir.split(sep)
00052     if pieces[0] == '':
00053         pieces = pieces[1:]
00054     while pieces:
00055         try:
00056             resolve('.'.join(pieces))
00057             break
00058         except (ImportError, ValueError):
00059             pieces = pieces[1:]
00060     if not pieces:
00061         return filename
00062     return '.'.join(pieces) + '/' + base
00063 
00064 class CustomizationView(BrowserView):
00065 
00066     def templateViewRegistrations(self):
00067         for reg in getViews(providedBy(self.context), IBrowserRequest):
00068             factory = reg.factory
00069             while hasattr(factory, 'factory'):
00070                 factory = factory.factory
00071             #XXX this should really be dealt with using a marker interface
00072             # on the view factory
00073             if hasattr(factory, '__name__') and \
00074                    factory.__name__.startswith('SimpleViewClass'):
00075                 yield reg
00076 
00077     def templateViewRegInfo(self):
00078         def regkey(reg):
00079             return reg.name
00080         for reg in sorted(self.templateViewRegistrations(), key=regkey):
00081             yield {
00082                 'viewname': reg.name,
00083                 'for': reg.required[0].__identifier__,
00084                 'type': reg.required[1].__identifier__,
00085                 'zptfile': mangleAbsoluteFilename(reg.factory.index.filename),
00086                 'zcmlfile': mangleAbsoluteFilename(reg.info.file)
00087                 }
00088 
00089     def viewClassFromViewName(self, viewname):
00090         view = getMultiAdapter((self.context, self.request), name=viewname)
00091         # The view class is generally auto-generated, we usually want
00092         # the first base class, though if the view only has one base
00093         # (generally object or BrowserView) we return the full class
00094         # and hope that it can be pickled
00095         klass = view.__class__
00096         base = klass.__bases__[0]
00097         if base is BrowserView or base is object:
00098             return klass
00099         return base
00100 
00101     def templateFromViewName(self, viewname):
00102         view = getMultiAdapter((self.context, self.request), name=viewname)
00103         return view.index
00104 
00105     def templateCodeFromViewName(self, viewname):
00106         template = self.templateFromViewName(viewname)
00107         #XXX: we can't do template.read() here because of a bug in
00108         # Zope 3's ZPT implementation.
00109         return open(template.filename, 'rb').read()
00110 
00111     def permissionFromViewName(self, viewname):
00112         view = getMultiAdapter((self.context, self.request), name=viewname)
00113         permissions = view.__class__.__ac_permissions__
00114         for permission, methods in permissions:
00115             if methods[0] in ('', '__call__'):
00116                 return permission
00117 
00118     def doCustomizeTemplate(self, viewname):
00119         # find the nearest site
00120         site = findSite(self.context, IObjectManagerSite)
00121         if site is None:
00122             raise TypeError("No site found")  # TODO find right exception
00123 
00124         # we're using the original filename of the template, not the
00125         # view name to avoid potential conflicts and/or confusion in
00126         # URLs
00127         template = self.templateFromViewName(viewname)
00128         zpt_id = basename(template.filename)
00129 
00130         template_file = self.templateCodeFromViewName(viewname)
00131         viewclass = self.viewClassFromViewName(viewname)
00132         permission = self.permissionFromViewName(viewname)
00133         viewzpt = TTWViewTemplate(zpt_id, template_file, view=viewclass,
00134                                   permission=permission)
00135         container = queryUtility(IViewTemplateContainer)
00136         if container is not None:
00137             viewzpt = container.addTemplate(zpt_id, viewzpt)
00138         else:
00139             site._setObject(zpt_id, viewzpt) #XXXthere could be a naming conflict
00140             viewzpt = getattr(site, zpt_id)
00141 
00142         # find out the view registration object so we can get at the
00143         # provided and required interfaces
00144         for reg in getViews(providedBy(self.context), IBrowserRequest):
00145             if reg.name == viewname:
00146                 break
00147 
00148         components = site.getSiteManager()
00149         components.registerAdapter(viewzpt, required=reg.required,
00150                                    provided=reg.provided, name=viewname
00151                                    ) #XXX info?
00152 
00153         return viewzpt
00154 
00155     def customizeTemplate(self, viewname):
00156         viewzpt = self.doCustomizeTemplate(viewname)
00157         # to get a "direct" URL we use aq_inner for a straight
00158         # acquisition chain
00159         url = absoluteURL(aq_inner(viewzpt), self.request) + "/manage_workspace"
00160         self.request.response.redirect(url)
00161 
00162 class RegistrationsView(BrowserView):
00163 
00164     def viewRegistrations(self):
00165         regs = []
00166         components = getSiteManager(self.context)
00167         for reg in components.registeredAdapters():
00168             if (len(reg.required) == 2 and
00169                 reg.required[1].isOrExtends(IBrowserRequest) and
00170                 reg.factory == self.context):
00171                 regs.append(reg)
00172         def regkey(reg):
00173             return (reg.name, reg.required)
00174         return sorted(regs, key=regkey)
00175 
00176     def getAllInterfaceNames(self):
00177         factory = getUtility(IVocabularyFactory, 'Interfaces')
00178         vocab = factory(self.context)
00179         return (term.token for term in vocab)
00180 
00181     def getRequestInterfaceNames(self):
00182         factory = getUtility(IVocabularyFactory, 'Interfaces')
00183         vocab = factory(self.context)
00184         return (term.token for term in vocab
00185                 if term.value.isOrExtends(IBrowserRequest))
00186 
00187     # TODO needs tests
00188     def unregister(self):
00189         index = self.request.form.get('index')
00190         try:
00191             index = int(index)
00192         except (TypeError, ValueError):
00193             index = None
00194         if index is None:
00195             #XXX: find right exception type
00196             raise ValueError("Missing or invalid 'index' parameter.")
00197         regs = self.viewRegistrations()
00198         reg = regs[index]
00199         components = getSiteManager(self.context)
00200         components.unregisterAdapter(reg.factory, reg.required, reg.provided,
00201                                      reg.name)
00202         self.request.response.redirect('registrations.html')
00203 
00204     # TODO needs tests
00205     def register(self, for_name, type_name, name='', comment=''):
00206         for_ = getUtility(IInterface, for_name)
00207         type = getUtility(IInterface, type_name)
00208         components = getSiteManager(self.context)
00209         components.registerAdapter(self.context, (for_, type),
00210                                    Interface, name, comment)
00211         self.request.response.redirect('registrations.html')