Skip to content

Instantly share code, notes, and snippets.

Created December 30, 2011 14:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/1540173 to your computer and use it in GitHub Desktop.
Save anonymous/1540173 to your computer and use it in GitHub Desktop.
################################################################
# ise.policy
# (C) 2011, Veit Schiele
################################################################
from Products.ATContentTypes.content.newsitem import ATNewsItemSchema
# inject teaser scale newsitems and PressRoom (derived)
ATNewsItemSchema['image'].sizes['teaser'] = (160, 400)
# Inject 'Employee' type into PressRelease as allowed referenable type
from Products.PressRoom.content.PressRelease import PressRelease
PressRelease.schema['releaseContacts'].allowed_types = ('PressContact', 'Employee')
################################################################
# ISE-42: monkey patch Image.tag() in order to
# support non-ascii 'title' fields
################################################################
from unidecode import unidecode
from cgi import escape
def _decode(s):
if not isinstance(s, unicode):
s = unicode(s, 'utf-8')
# s = unidecode(s)
s = s.replace(u'Ä', u'Ae')
s = s.replace(u'Ö', u'Oe')
s = s.replace(u'Ü', u'Ue')
s = s.replace(u'ö', u'ae')
s = s.replace(u'ü', u'oe')
s = s.replace(u'Ü', u'ue')
s = s.replace(u'ß', u'ss')
return s
def tag(self, height=None, width=None, alt=None,
scale=0, xscale=0, yscale=0, css_class=None, title=None, **args):
if height is None: height=self.height
if width is None: width=self.width
# Auto-scaling support
xdelta = xscale or scale
ydelta = yscale or scale
if xdelta and width:
width = str(int(round(int(width) * xdelta)))
if ydelta and height:
height = str(int(round(int(height) * ydelta)))
result='<img src="%s"' % (self.absolute_url())
if alt is None:
alt=getattr(self, 'alt', '')
alt = _decode(alt)
result = '%s alt="%s"' % (result, escape(alt, 1))
if title is None:
title=getattr(self, 'title', '')
# check for unicode (ajung, ISE-42)
title = _decode(title)
result = '%s title="%s"' % (result, escape(title, 1))
if height:
result = '%s height="%s"' % (result, height)
if width:
result = '%s width="%s"' % (result, width)
# Omitting 'border' attribute (Collector #1557)
# if not 'border' in [ x.lower() for x in args.keys()]:
# result = '%s border="0"' % result
if css_class is not None:
result = '%s class="%s"' % (result, css_class)
for key in args.keys():
value = args.get(key)
if value:
result = '%s %s="%s"' % (result, key, value)
return '%s />' % result
#########################################################################
# ISE-38: TinyMCE JSON Folder Listing should ignore INavigationRoot
#########################################################################
from zope.interface import implements
from zope.component import getUtility
try:
import json
except:
import simplejson as json
from plone.i18n.normalizer.interfaces import IIDNormalizer
from plone.app.layout.navigation.root import getNavigationRoot
from plone.app.layout.navigation.interfaces import INavigationRoot
from Products.TinyMCE.adapters.interfaces.JSONFolderListing import IJSONFolderListing
from Products.CMFCore.interfaces._content import IFolderish
from Products.CMFCore.utils import getToolByName
from Acquisition import aq_inner
def getListing(self, filter_portal_types, rooted, document_base_url, upload_type=None):
"""Returns the actual listing"""
catalog_results = []
results = {}
object = aq_inner(self.context)
portal_catalog = getToolByName(object, 'portal_catalog')
normalizer = getUtility(IIDNormalizer)
# check if object is a folderish object, if not, get it's parent.
if not IFolderish.providedBy(object):
object = object.getParentNode()
# ISE-38 (ajung) - ignore INavigationRoot
# if INavigationRoot.providedBy(object) or (rooted == "True" and document_base_url[:-1] == object.absolute_url()):
if rooted == "True" and document_base_url[:-1] == object.absolute_url():
results['parent_url'] = ''
else:
results['parent_url'] = object.getParentNode().absolute_url()
if rooted == "True":
results['path'] = self.getBreadcrumbs(results['parent_url'])
else:
# get all items from siteroot to context (title and url)
results['path'] = self.getBreadcrumbs()
# get all portal types and get information from brains
path = '/'.join(object.getPhysicalPath())
for brain in portal_catalog(portal_type=filter_portal_types, sort_on='getObjPositionInParent', path={'query': path, 'depth':1}):
catalog_results.append({
'id': brain.getId,
'uid': brain.UID,
'url': brain.getURL(),
'portal_type': brain.portal_type,
'normalized_type': normalizer.normalize(brain.portal_type),
'title' : brain.Title == "" and brain.id or brain.Title,
'icon' : brain.getIcon,
'is_folderish' : brain.is_folderish
})
# add catalog_ressults
results['items'] = catalog_results
# decide whether to show the upload new button
results['upload_allowed'] = False
if upload_type:
portal_types = getToolByName(object, 'portal_types')
fti = getattr(portal_types, upload_type, None)
if fti is not None:
results['upload_allowed'] = fti.isConstructionAllowed(object)
# return results in JSON format
return json.dumps(results)
##################################################################################
# Patch for archetypes.referencesbrowserwidget.browser.view.ReferenceBrowserPopup
# (support breadcrumbs into the portal root)
##################################################################################
from zope.component import getMultiAdapter
def refpopup_breadcrumbs(self, startup_directory=None):
assert self._updated
context = aq_inner(self.context)
portal_state = getMultiAdapter((context, self.request),
name=u'plone_portal_state')
bc_view = context.restrictedTraverse('@@breadcrumbs_view')
crumbs = bc_view.breadcrumbs()
if not self.widget.restrict_browsing_to_startup_directory:
# Support to breadcrumbs reaching over the navigation root
# (ajung)
newcrumbs = [{'Title': 'Home',
'absolute_url': self.genRefBrowserUrl(
portal_state.portal_url())}]
if portal_state.portal_url() != portal_state.navigation_root_url():
nav_root_path = portal_state.navigation_root_path()
nav_root = self.context.restrictedTraverse(nav_root_path)
newcrumbs.append({'Title': nav_root.Title(),
'absolute_url': self.genRefBrowserUrl(
portal_state.navigation_root_url())})
else:
# display only crumbs into startup directory
startup_dir_url = startup_directory or \
utils.getStartupDirectory(context,
self.widget.getStartupDirectory(context, self.field))
newcrumbs = []
crumbs = [c for c in crumbs \
if c['absolute_url'].startswith(startup_dir_url)]
for c in crumbs:
c['absolute_url'] = self.genRefBrowserUrl(c['absolute_url'])
newcrumbs.append(c)
return newcrumbs
##################################################################################
# ISE-9 (new) - Support to generate a Sitemap for an arbitrary context (folder)
# and not only for the Plone site root only. This default behavior of the
# SitemapQueryBuilder can be influences by setting
# request/sitemap_for_current_context = True
##################################################################################
from Products.CMFPlone.browser.navtree import NavtreeQueryBuilder
def SitemapQueryBuilder__init__(self, context):
NavtreeQueryBuilder.__init__(self, context)
portal_url = getToolByName(context, 'portal_url')
portal_properties = getToolByName(context, 'portal_properties')
navtree_properties = getattr(portal_properties, 'navtree_properties')
sitemapDepth = navtree_properties.getProperty('sitemapDepth', 2)
if context.REQUEST.get('sitemap_for_current_context', False):
self.query['path'] = {'query' : '/'.join(context.getPhysicalPath()),
'depth' : sitemapDepth}
else:
self.query['path'] = {'query' : portal_url.getPortalPath(),
'depth' : sitemapDepth}
##################################################################################
# ISE-19 (new): unrestricted image search within TinyMCE
##################################################################################
import json
def getSearchResults(self, filter_portal_types, searchtext):
"""Returns the actual search result"""
catalog_results = []
results = {}
results['parent_url'] = ''
results['path'] = []
if searchtext:
# for brain in self.context.portal_catalog.searchResults({'SearchableText':'%s*' % searchtext, 'portal_type':filter_portal_types, 'sort_on':'sortable_title', 'path': self.context.absolute_url_path()}):
for brain in self.context.portal_catalog.searchResults({'SearchableText':'%s*' % searchtext, 'portal_type':filter_portal_types, 'sort_on':'sortable_title'}):
catalog_results.append({
'id': brain.getId,
'uid': brain.UID,
'url': brain.getURL(),
'portal_type': brain.portal_type,
'title' : brain.Title == "" and brain.id or brain.Title,
'icon' : brain.getIcon,
'is_folderish' : brain.is_folderish
})
# add catalog_results
results['items'] = catalog_results
# never allow upload from search results page
results['upload_allowed'] = False
# return results in JSON format
return json.dumps(results)
##################################################################################
# ISE-37(new): add context getId() to bodyClass
##################################################################################
from zope.component import queryUtility
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
def bodyClass(self, template, view):
"""Returns the CSS class to be used on the body tag.
"""
context = self.context
url = getToolByName(context, "portal_url")
# template class (required)
name = ''
if isinstance(template, ViewPageTemplateFile):
# Browser view
name = view.__name__
else:
name = template.getId()
body_class = 'template-%s' % name
# portal type class (optional)
normalizer = queryUtility(IIDNormalizer)
portal_type = normalizer.normalize(context.portal_type)
if portal_type:
body_class += " portaltype-%s" % portal_type
# section class (optional)
contentPath = url.getRelativeContentPath(context)
if contentPath:
body_class += " section-%s" % contentPath[0]
# object id
# ajung (ISE-37)
body_class += " object-id-%s" % context.getId()
# class for hiding icons (optional)
if self.icons_visible():
body_class += ' icons-on'
return body_class
#################################################################
# ISE-70: ZPublisher delivering improper content-type header
# for image scale 'image'
# See https://bugs.launchpad.net/zope2/+bug/903251
#################################################################
def isHTML(self, s):
s = s.lstrip()
# Note that the string can be big, so s.lower().startswith() is more
# expensive than s[:n].lower().
if (s[:6].lower() == '<html>' or s[:14].lower() == '<!doctype html'):
return 1
# Stupid test
# if s.find('</') > 0:
# return 1
return 0
#################################################################
# ISE-71: don't limit Collage search to INavigationRoot
#################################################################
# This is a mega-patch navigation_root() is defined as a property within the
# original code but methods decorated as property CAN NOT monkey patched. For
# this reason we need to monkey-patch all other methods as well using the
# navigation_root() method as property in order to call the monkey-patch method
# as a method and not as a property.
from urllib import unquote
from zope.component import queryUtility
from Products.CMFCore.interfaces import ISiteRoot
from Products.CMFPlone import utils as plone_utils
from Products.CMFPlone.interfaces import IHideFromBreadcrumbs
try:
from Products.CMFPlone import Batch
except ImportError:
from Products.CMFPlone.PloneBatch import Batch
def navigation_root(self):
context = self.context.aq_inner
while context is not None:
if ISiteRoot.providedBy(context):
break
context = context.aq_parent
return context
def _data(self):
portal_types = self.request.get('portal_type', None)
if not portal_types:
portal_types = [pt['id'] for pt in self.listEnabledTypes()]
query_path = self.request.get('path') or \
"/".join(self.navigation_root().getPhysicalPath())
query_path = unquote(query_path)
b_start = self.request.get('b_start', 0)
query_text = unquote(self.request.get('SearchableText', ''))
if query_text:
depth = 10
else:
depth = 1
if not self.readitems:
results = []
else:
try:
results = self.catalog(
Language='all',
SearchableText=query_text,
portal_type=portal_types,
path={"query": query_path, 'depth': depth},
sort_on='sortable_title',
sort_order='ascending')
# alternative: sort_order='reverse', sort_on='modified'
except ParseError:
results = []
# setup description cropping
cropText = getMultiAdapter(
(self.context, self.request), name=u'plone').cropText
props = getMultiAdapter((self.context, self.request),
name=u'plone_tools').properties()
site_properties = props.site_properties
desc_length = getattr(site_properties, 'search_results_description_length', 25)
desc_ellipsis = getattr(site_properties, 'ellipsis', '...')
items = []
batch = Batch(results, self.limit, int(b_start), orphan=1)
idnormalizer = queryUtility(IIDNormalizer)
for result in batch:
cssType = idnormalizer.normalize(result.portal_type)
items.append({
'UID': result.UID,
'icon' : result.getIcon,
'title': result.Title,
'description': cropText(result.Description, desc_length, desc_ellipsis),
'type': result.Type,
'folderish': result.is_folderish,
'target_url': result.getURL(),
'path': result.getPath(),
'link_css_class': 'state-%s' % result.review_state,
'cssType': 'contenttype-%s' % cssType,
})
return {
'items': items,
'batch': batch,
'query': bool(query_text),
'query_text': query_text,
'path': query_path,
}
def breadcrumbs(self):
path = self.request.get('path')
portal = self.navigation_root()
if path is None:
context = portal
else:
context = portal.restrictedTraverse(unquote(path))
crumbs = []
context = aq_inner(context)
while context is not None:
if not IHideFromBreadcrumbs.providedBy(context):
crumbs.append({
'path': "/".join(context.getPhysicalPath()),
'url': context.absolute_url(),
'title': context.title_or_id()
})
if ISiteRoot.providedBy(context):
break
context = plone_utils.parent(context)
crumbs.reverse()
return crumbs
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:monkey="http://namespaces.plone.org/monkey"
i18n_domain="collective.monkeypatcher">
<include package="collective.monkeypatcher" />
<monkey:patch
description="ISE-42: OFS.Image.tag()"
class="OFS.Image.Image"
original="tag"
replacement=".patches.tag"
/>
<monkey:patch
description="ISE-42: OFS.Image.tag()"
class="Products.CMFCore.FSImage.FSImage"
original="tag"
replacement=".patches.tag"
/>
<monkey:patch
description="ISE-38: TinyMCE JSON Folder listing should ignore INavigationRoot"
class="Products.TinyMCE.adapters.JSONFolderListing.JSONFolderListing"
original="getListing"
replacement=".patches.getListing"
/>
<monkey:patch
description="ISE-108: Navigation support RefBrowserWidget across INavigationRoot"
class="archetypes.referencebrowserwidget.browser.view.ReferenceBrowserPopup"
original="breadcrumbs"
replacement=".patches.refpopup_breadcrumbs"
/>
<monkey:patch
description="ISE-9 (new): Support for Sub-Sitemap"
class="Products.CMFPlone.browser.navtree.SitemapQueryBuilder"
original="__init__"
replacement=".patches.SitemapQueryBuilder__init__"
/>
<monkey:patch
description="ISE-19 (new): unrestrict TinyMCE image search"
class="Products.TinyMCE.adapters.JSONSearch.JSONSearch"
original="getSearchResults"
replacement=".patches.getSearchResults"
/>
<monkey:patch
description="ISE-37 (new): supplementary classes within body package"
class="plone.app.layout.globals.layout.LayoutPolicy"
original="bodyClass"
replacement=".patches.bodyClass"
/>
<monkey:patch
description="ISE-70 (new): improper content-type for image scale"
class="ZPublisher.HTTPResponse.HTTPResponse"
original="isHTML"
replacement=".patches.isHTML"
/>
<monkey:patch
description="ISE-71 (new): naviagion_root fix for Collage"
class="Products.Collage.browser.existingitems.ExistingItemsView"
original="navigation_root"
replacement=".patches.navigation_root"
/>
<monkey:patch
description="ISE-71 (new): naviagion_root fix for Collage"
class="Products.Collage.browser.existingitems.ExistingItemsView"
original="_data"
replacement=".patches._data"
/>
<monkey:patch
description="ISE-71 (new): naviagion_root fix for Collage"
class="Products.Collage.browser.existingitems.ExistingItemsView"
original="breadcrumbs"
replacement=".patches.breadcrumbs"
/>
</configure>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment