Source code for plone.app.blocks.resource
import urlparse
import Globals
from zope.interface import implements
from zope.component import adapter
from zope.site.hooks import getSite
from zope.publisher.browser import BrowserView
from zope.schema.interfaces import IVocabularyFactory
from zope.schema.vocabulary import SimpleVocabulary
from zope.schema.vocabulary import SimpleTerm
from plone.resource.traversal import ResourceTraverser
from plone.resource.manifest import getAllResources
from plone.registry.interfaces import IRecordModifiedEvent
from plone.memoize.volatile import cache, DontCache, store_on_context
from plone.app.blocks.interfaces import SITE_LAYOUT_RESOURCE_NAME
from plone.app.blocks.interfaces import SITE_LAYOUT_FILE_NAME
from plone.app.blocks.interfaces import SITE_LAYOUT_MANIFEST_FORMAT
from plone.app.blocks.interfaces import DEFAULT_SITE_LAYOUT_REGISTRY_KEY
from plone.app.blocks.utils import resolveResource
from plone.app.blocks.utils import getDefaultSiteLayout
from plone.app.blocks.utils import getLayoutAwareSiteLayout
from Acquisition import aq_parent
from Products.CMFCore.utils import getToolByName
from OFS.interfaces import ITraversable
from zExceptions import NotFound
[docs]class SiteLayoutTraverser(ResourceTraverser):
"""The site layout traverser.
Allows traveral to /++sitelayout++<name> using ``plone.resource`` to fetch
things stored either on the filesystem or in the ZODB.
"""
name = SITE_LAYOUT_RESOURCE_NAME
[docs]class AvailableLayoutsVocabulary(object):
"""Vocabulary to return available layouts of a given type
"""
implements(IVocabularyFactory)
def __init__(self, format, defaultFilename):
self.format = format
self.defaultFilename = defaultFilename
def __call__(self, context):
items = []
resources = getAllResources(self.format)
for name, manifest in resources.items():
title = name.capitalize().replace('-', ' ').replace('.', ' ')
filename = self.defaultFilename
if manifest is not None:
title = manifest['title'] or title
filename = manifest['file'] or filename
path = "/++%s++%s/%s" % (self.format.resourceType, name, filename)
items.append(SimpleTerm(path, name, title))
return SimpleVocabulary(items)
AvailableSiteLayoutsVocabularyFactory = AvailableLayoutsVocabulary(
SITE_LAYOUT_MANIFEST_FORMAT,
SITE_LAYOUT_FILE_NAME,
)
[docs]def cacheKey(method, self):
"""Invalidate if the fti is modified, the global registry is modified,
or the content is modified
"""
if Globals.DevelopmentMode:
raise DontCache()
catalog = getToolByName(self.context, 'portal_catalog')
return (
getattr(self.context, '_p_mtime', None),
catalog.getCounter(),
)
@adapter(IRecordModifiedEvent)
[docs]def globalSiteLayoutModified(event):
"""Invalidate caches if the global site layout is changed. This will
likely also affect things cached using plone.app.caching, which is what
we want - the page has probably changed
"""
if event.record.__name__ == DEFAULT_SITE_LAYOUT_REGISTRY_KEY:
if event.oldValue != event.newValue:
catalog = getToolByName(getSite(), 'portal_catalog', None)
if catalog is not None and hasattr(catalog, '_increment_counter'):
catalog._increment_counter()
[docs]class DefaultSiteLayout(BrowserView):
"""Look up and render the site layout to use for the context.
Use this for a page that does not have the ILayout behavior, or a
standalone page template.
The idea is that you can do:
<html data-layout="./@@default-site-layout">
and always get the correct site layout for the page, taking section-
specific settings into account.
"""
@cache(cacheKey, store_on_context)
def __call__(self):
layout = self._getLayout()
if layout is None:
raise NotFound("No default site layout set")
pathContext = self.context
while not ITraversable.providedBy(pathContext):
pathContext = aq_parent(pathContext)
if pathContext is None:
break
path = layout
if pathContext is not None:
path = urlparse.urljoin(pathContext.absolute_url_path(), layout)
return resolveResource(path)
def _getLayout(self):
return getDefaultSiteLayout(self.context)
[docs]class PageSiteLayout(DefaultSiteLayout):
"""Look up and render the site layout to use for the context.
Use this for a page that does have the ILayout behavior. It will take the
``pageSiteLayout`` property into account.
The idea is that you can do:
<html data-layout="./@@page-site-layout">
and always get the correct site layout for the page, taking section-
and page-specific settings into account.
"""
def _getLayout(self):
return getLayoutAwareSiteLayout(self.context)