############################################################################## # # Copyright (c) 2003-2005 Kupu Contributors. All rights reserved. # # This software is distributed under the terms of the Kupu # License. See LICENSE.txt for license text. For a list of Kupu # Contributors see CREDITS.txt. # ############################################################################## """Plone Kupu library tool This module defines a mixin class for the kupu tool which contains support code for drawers. Much of this code was formerly in separate Python scripts, but has been moved here to make it easier to maintain. """ import re, string from thread import get_ident from AccessControl import Unauthorized, ClassSecurityInfo, getSecurityManager from Globals import InitializeClass from Products.Archetypes.public import * from Products.Archetypes.interfaces.referenceable import IReferenceable from Products.PythonScripts.standard import html_quote, newline_to_br from Products.kupu.plone.librarytool import KupuError from Products.CMFCore.utils import getToolByName try: from Products.CMFPlone.utils import getSiteEncoding except ImportError: def getSiteEncoding(context): tool = getToolByName(context, 'plone_utils') return tool.getSiteEncoding() import html2captioned try: from PIL import Image except ImportError: HAS_PIL = False else: HAS_PIL = True UIDURL = re.compile(".*\\bresolveuid/([^/?#]+)") NOCC = nocc = string.maketrans(''.join([chr(c) for c in range(32) if c not in (9,10,13)]), "?"*29) uNOCC = dict([(c,ord('?')) for c in range(32) if c not in (9,10,13)]) def filterControlChars(s): """convert characters which are illegal in xml to '?'""" if isinstance(s, unicode): return s.translate(uNOCC) else: return s.translate(NOCC) # mapping (thread-id, portal-physicalPath, portal_type) -> # imagefield-getAvailableSizes (as tuple sorted by dimension) (width, height, key) IMAGE_SIZES_CACHE = {} class ResourceType: '''Resource types are wrapped into a class so we can easily access attributes which may, or may not be present. ''' def __init__(self, tool, name): self.name = name self._tool = tool parts = name.split('.', 1) self.subObject = None if len(parts)==1: self._portal_types = tool.queryPortalTypesForResourceType(name, ()) self._field = self._widget = None else: # Must be portal_type.fieldname typename, fieldname = parts # Topic criteria have typename and fieldname embedded in # the criteria name. if fieldname.startswith('crit__'): self.subObject = '_'.join(fieldname.split('_')[:-1]) typename, fieldname = fieldname.split('_')[-2:] __traceback_info__ = (parts, typename, fieldname) archetype_tool = getToolByName(tool, 'archetype_tool', None) types = archetype_tool.listRegisteredTypes() typeinfo = [t for t in types if t['portal_type']==typename] if len(typeinfo)==0: raise KupuError("Unrecognised portal type for resource %s" % name) schema = typeinfo[0]['schema'] self.klass = typeinfo[0]['klass'] try: self._field = schema[fieldname] except KeyError: raise KupuError("Unrecognised fieldname for resource %s" % name) self._widget = self._field.widget def __repr__(self): return " uid = match.group(1) obj = reference_tool.lookupObject(uid) elif src and '://' in src: # src=http://someurl/somepath/someobject base = portal.absolute_url() if src.startswith(base): src = src[len(base):].lstrip('/') try: obj = portal.restrictedTraverse(src) except AttributeError: return [] if portal_types: while obj.portal_type not in portal_types: obj = obj.aq_parent if obj is portal: return [] else: # src= ... src = src.split(' ') # src is a list of uids. objects = [ reference_tool.lookupObject(uid) for uid in src ] objects = [ o for o in objects if o is not None ] return objects if obj is None: return None return [obj] security.declarePublic("getCurrentParent") def getCurrentParent(self): """Find the parent of the object specified in the src string. If multiple objects and they don't have the same parent, or if no suitable object returns None, otherwise returns the parent.""" objects = self.getCurrentObject() parent = None for obj in objects: if parent is not None and parent is not obj.aq_parent: return None parent = obj.aq_parent return parent security.declarePublic("getCurrentSelection") def getCurrentSelection(self, portal=None): '''Returns object information for a selected object''' objects = self.getCurrentObject(portal) return self.infoForBrains(objects, self.getResourceType(), portal) security.declarePublic("getMyItems") def getMyItems(self): request = self.REQUEST response = request.RESPONSE response.setHeader('Cache-Control', 'no-cache') member_tool = getToolByName(self, 'portal_membership') member = member_tool.getAuthenticatedMember() # the default resource type is mediaobject search_params = {} search_params['sort_on'] = 'modified' search_params['sort_order'] = 'reverse' search_params['limit'] = 20 search_params['Creator'] = member.getMemberId() return self.infoForQuery(search_params) security.declarePublic("getRecentItems") def getRecentItems(self): search_params = {} search_params['sort_on'] = 'modified' search_params['sort_order'] = 'reverse' search_params['limit'] = 20 search_params['review_state'] = 'visible', 'published' return self.infoForQuery(search_params) security.declarePublic("kupuSearch") def kupuSearch(self): request = self.REQUEST search_params = {} search_params.update(request.form) # Get the maximum number of results with 500 being the default and # absolute maximum. abs_max = 500 search_params['limit'] = min(request.get('max_results', abs_max), abs_max) return self.infoForQuery(search_params) security.declareProtected("View", "infoForQuery") def infoForQuery(self, query, resource_type=None, portal=None): resource_type = self.getResourceType() if portal is None: portal = getToolByName(self, 'portal_url').getPortalObject() baseQuery = resource_type.getQuery() query.update(baseQuery) limit = query.get('limit', None) pt = query['portal_type'] if not pt: del query['portal_type'] catalog = getToolByName(portal, 'portal_catalog') values = catalog.searchResults(query) if limit: values = values[:limit] return self.infoForBrains(values, resource_type, portal) security.declareProtected("View", "infoForBrains") def infoForBrains(self, values, resource_type, portal=None, linkids=None): request = self.REQUEST response = request.RESPONSE response.setHeader('Cache-Control', 'no-cache') linktypes=resource_type.portal_types if portal is None: portal = getToolByName(self, 'portal_url').getPortalObject() adaptor = InfoAdaptor(self, resource_type, portal) # For Plone 2.0.5 compatability, if getId is callable we assume # we have an object rather than a brains. if values and callable(values[0].getId): info = adaptor.info_object else: info = adaptor.info res = [] for obj in values: portal_type = getattr(obj, 'portal_type', '') if linkids is not None: linkable = obj.id in linkids elif len(linktypes)==0: linkable = True else: linkable = portal_type in linktypes data = info(obj, linkable) if data: res.append(data) return res security.declareProtected("View", "canCaption") def canCaption(self, field): return (getattr(field, 'default_output_type', None) in ('text/x-html-safe', 'text/x-html-captioned')) security.declareProtected("View", "getKupuFields") def getKupuFields(self, filter=1): """Returns a list of all kupu editable fields""" inuse = getToolByName(self, 'portal_catalog').uniqueValuesFor('portal_type') site_encoding = getSiteEncoding(self) for t,f,pt in self._getKupuFields(): if html2captioned.sanitize_portal_type(pt) in inuse or not filter: yield dict(type=t, name=f.getName(), portal_type=pt, label=f.widget.Label(self).decode(site_encoding)) def _getKupuFields(self): """Yield all fields which are editable using kupu""" archetype_tool = getToolByName(self, 'archetype_tool', None) types = archetype_tool.listRegisteredTypes() for t in types: schema = t.get('schema') if schema: typename = getattr(t['klass'], 'archetype_name', t['portal_type']) for f in schema.fields(): w = f.widget if isinstance(w, (RichWidget, VisualWidget)): yield typename, f, t['portal_type'] security.declareProtected("View", "supportedCaptioning") def supportedCaptioning(self): """Returns a list of document/fields which have support for captioning""" supported = [t+'/'+f.widget.label for (t,f,pt) in self._getKupuFields() if self.canCaption(f) ] return str.join(', ', supported) security.declareProtected("View", "unsupportedCaptioning") def unsupportedCaptioning(self): """Returns a list of document/fields which do not have support for captioning""" unsupp = [t+'/'+f.widget.label for (t,f,pt) in self._getKupuFields() if not self.canCaption(f) ] return str.join(', ', unsupp) security.declareProtected("View", "transformIsEnabled") def transformIsEnabled(self): """Test whether the output transform is enabled for x-html-safe""" uid_catalog = getToolByName(self, 'uid_catalog', None) portal_transforms = getToolByName(self, 'portal_transforms', None) if not uid_catalog or not portal_transforms: return False # Find something, anything which has a UID content = uid_catalog.searchResults(sort_on='', sort_limit=1) if not content: return False uid = content[0].UID # Get an arbitrary used UID. link = 'resolveuid/%s' % uid test = '' % link txfrm = portal_transforms.convertTo('text/x-html-safe', test, mimetype='text/html', context=self) if hasattr(txfrm, 'getData'): txfrm = txfrm.getData() return txfrm and not link in txfrm security.declareProtected("View", "isUploadSupported") def isUploadSupported(self, context): """Returns True if we can upload the the current folder.""" resource_type = self.getResourceType() if resource_type.name != 'mediaobject': return False allowedContent = context.getAllowedTypes() allowed = dict.fromkeys([a.getId() for a in allowedContent]) for t in resource_type.portal_types: if t in allowed: return True return False security.declareProtected("View", "getBaseUrl") def getBaseUrl(self, context, include_factory=False, resource_type=None): base = context.absolute_url() if resource_type: rt = self.getResourceType(resource_type); sd = rt.startup_directory if sd: base = sd posfactory = base.find('/portal_factory/') if posfactory>0: if include_factory: base = base[:posfactory+15] else: base = base[:posfactory] return base def _getImageSizes(self): resource_type = self.getResourceType() portal = getToolByName(self, 'portal_url').getPortalObject() mediatypes = resource_type.get_portal_types() catalog = getToolByName(self, 'portal_catalog') adaptor = InfoAdaptor(self, resource_type, portal) sizes = {} for portal_type in mediatypes: brains = catalog.searchResults(portal_type=portal_type, limit=1)[:1] if brains: info = adaptor.get_image_sizes(brains[0], portal_type, '') if info: for i in info: sizes[i['uri']] = 1 return sizes security.declareProtected("View", "convertUidsToPaths") def convertUidsToPaths(self, value=None): """Convert a list of uids (or a single space or newline separated string) to a list of paths""" uid_catalog = getToolByName(self, 'uid_catalog') ppath = getToolByName(self, 'portal_url').getPortalPath()[1:]+'/' if isinstance(value, basestring): value = value.split() if not value: return [] brains = uid_catalog.searchResults(UID=value) paths = [ppath+b.getPath() for b in brains] return paths InitializeClass(PloneDrawers)