Created
December 30, 2011 14:47
-
-
Save anonymous/1540173 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################################################################ | |
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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