Back to index

plone3  3.1.7
plonedrawers.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2003-2005 Kupu Contributors. All rights reserved.
00004 #
00005 # This software is distributed under the terms of the Kupu
00006 # License. See LICENSE.txt for license text. For a list of Kupu
00007 # Contributors see CREDITS.txt.
00008 #
00009 ##############################################################################
00010 """Plone Kupu library tool
00011 
00012 This module defines a mixin class for the kupu tool which contains
00013 support code for drawers. Much of this code was formerly in separate
00014 Python scripts, but has been moved here to make it easier to maintain.
00015 
00016 """
00017 import re, string
00018 from thread import get_ident
00019 from AccessControl import Unauthorized, ClassSecurityInfo, getSecurityManager
00020 from Globals import InitializeClass
00021 from Products.Archetypes.public import *
00022 from Products.Archetypes.interfaces.referenceable import IReferenceable
00023 from Products.Archetypes.utils import shasattr
00024 from Products.PythonScripts.standard import html_quote, newline_to_br
00025 from Products.kupu.plone import util
00026 from Products.kupu.plone.librarytool import KupuError
00027 from Products.CMFCore.utils import getToolByName
00028 try:
00029     from Products.CMFPlone.utils import getSiteEncoding
00030 except ImportError:
00031     def getSiteEncoding(context):
00032         tool = getToolByName(context, 'plone_utils')
00033         return tool.getSiteEncoding()
00034     
00035 import html2captioned
00036 
00037 try:
00038     from PIL import Image
00039 except ImportError:
00040     HAS_PIL = False
00041 else:
00042     HAS_PIL = True
00043 
00044 UIDURL = re.compile(".*\\bresolveuid/([^/?#]+)")
00045 NOCC = nocc = string.maketrans(''.join([chr(c) for c in range(32) if c not in (9,10,13)]), "?"*29)
00046 uNOCC = dict([(c,ord('?')) for c in range(32) if c not in (9,10,13)])
00047 def filterControlChars(s):
00048     """convert characters which are illegal in xml to '?'"""
00049     if isinstance(s, unicode):
00050         return s.translate(uNOCC)
00051     else:
00052         return s.translate(NOCC)
00053 
00054 # mapping (thread-id, portal-physicalPath, portal_type) ->
00055 # imagefield-getAvailableSizes (as tuple sorted by dimension) (width, height, key)
00056 IMAGE_SIZES_CACHE = {}
00057 
00058 class ResourceType:
00059     '''Resource types are wrapped into a class so we can easily
00060     access attributes which may, or may not be present.
00061     '''
00062     def __init__(self, tool, name):
00063         self.name = name
00064         self._tool = tool
00065         parts = name.split('.', 1)
00066         self.subObject = None
00067 
00068         if len(parts)==1:
00069             self._portal_types = tool.queryPortalTypesForResourceType(name, ())
00070             self._field = self._widget = None
00071         else:
00072             # Must be portal_type.fieldname
00073             typename, fieldname = parts
00074             # Topic criteria have typename and fieldname embedded in
00075             # the criteria name.
00076             if fieldname.startswith('crit__'):
00077                 self.subObject = '_'.join(fieldname.split('_')[:-1])
00078                 typename, fieldname = fieldname.split('_')[-2:]
00079 
00080             __traceback_info__ = (parts, typename, fieldname)
00081             archetype_tool = getToolByName(tool, 'archetype_tool', None)
00082             types = archetype_tool.listRegisteredTypes()
00083             typeinfo = [t for t in types if t['portal_type']==typename]
00084 
00085             if len(typeinfo)==0:
00086                 raise KupuError("Unrecognised portal type for resource %s" % name)
00087 
00088             schema = typeinfo[0]['schema']
00089             self.klass = typeinfo[0]['klass']
00090             try:
00091                 self._field = schema[fieldname]
00092             except KeyError:
00093                 raise KupuError("Unrecognised fieldname for resource %s" % name)
00094                 
00095             self._widget = self._field.widget
00096 
00097     def __repr__(self):
00098         return "<ResourceType %s %s %s %s %s" % (
00099             self.name,
00100             self.portal_types, self.getQuery(), self.allow_browse, self.startup_directory
00101             )
00102 
00103     def get_portal_types(self):
00104         if not hasattr(self, '_portal_types'):
00105             field = self._field
00106             allowed_types = getattr(field, 'allowed_types', ())
00107 
00108             if allowed_types == ():
00109                 dr = self._tool.getDefaultResource()
00110                 allowed_types = self._tool.getResourceType(dr).portal_types
00111 
00112             allow_method = getattr(field, 'allowed_types_method', None)
00113             if allow_method is not None:
00114                 instance = self._instanceFromRequest()
00115                 if instance:
00116                     meth = getattr(instance, allow_method)
00117                     allowed_types = meth()
00118             self._portal_types = allowed_types
00119 
00120         return self._portal_types
00121     portal_types = property(get_portal_types)
00122 
00123     def getQuery(self):
00124         query = {'portal_type': self.portal_types }
00125         
00126         if self._field is not None:
00127             field = self._field
00128             widget = field.widget
00129             base_query = getattr(widget, 'base_query', None)
00130             if base_query and isinstance(base_query, basestring):
00131                 # We need the actual object containing the field to be
00132                 # able to call a dynamic base_query
00133                 instance = self._instanceFromRequest()
00134                 if instance:
00135                     base_query = getattr(instance, base_query, None)
00136                     if base_query:
00137                        base_query = base_query()
00138                        
00139             if base_query:
00140                 query.update(base_query)
00141 
00142         return query
00143 
00144     def _instanceFromRequest(self):
00145         """Get instance object from UID in request.
00146         Throws an error if there isn't a UID.
00147         """
00148         if not hasattr(self, '_instance'):
00149             # XXX TODO: this needs to handle the case where the object is
00150             # being created through portal_factory and hasn't yet been
00151             # saved. The UID won't be in the catalog so we need to create
00152             # a dummy object instead.
00153             tool = self._tool
00154             UID = tool.REQUEST.get('instance', None)
00155             if UID is None:
00156                 return None
00157             reference_tool = getToolByName(tool, 'reference_catalog')
00158             self._instance = reference_tool.lookupObject(UID)
00159         if self.subObject:
00160             return self._instance[self.subObject]
00161 
00162         return self._instance
00163 
00164     def _allow_browse(self):
00165         return getattr(self._widget, 'allow_browse', True)
00166     allow_browse = property(_allow_browse)
00167 
00168     def _startup_directory(self):
00169         return getattr(self._widget, 'startup_directory', '')
00170     startup_directory = property(_startup_directory)
00171 
00172 class InfoAdaptor:
00173     """Convert either an object or a brain into an information dictionary."""
00174     def __init__(self, tool, resource_type, portal):
00175         self.url_tool = getToolByName(portal, 'portal_url')
00176         self.uid_catalog = getToolByName(portal, 'uid_catalog', None)
00177         self.portal_interface = getToolByName(portal, 'portal_interface')
00178         self.workflow_tool = getToolByName(portal, 'portal_workflow')
00179         self.workflow_states = self.wfTitles()
00180         self.linkbyuid = tool.getLinkbyuid()
00181         self.coll_types = tool.getResourceType('collection').portal_types
00182         self.anchor_types =  tool.getResourceType('containsanchors').portal_types
00183         self.portal_base = self.url_tool.getPortalPath()
00184         self.prefix_length = len(self.portal_base)+1
00185         self.resource_type = resource_type
00186         self.ttool = getToolByName(portal, 'portal_types')
00187         
00188         instance = tool.REQUEST.get('instance', '')
00189         if instance:
00190             instance = 'instance=%s&' % tool.REQUEST.instance
00191 
00192         self.srctail = 'kupucollection.xml?'+instance+'resource_type=' + resource_type.name
00193         self.showimagesize = resource_type.name=='mediaobject'
00194 
00195         # The redirecting url must be absolute otherwise it won't work for
00196         # preview when the page is using portal_factory
00197         # The absolute to relative conversion when the document is saved
00198         # should strip the url right back down to resolveuid/whatever.
00199         self.base = self.url_tool()
00200         self.security = getSecurityManager()
00201         self.tool = tool
00202 
00203     def wfTitles(self):
00204         wt = self.workflow_tool
00205         try:
00206             states = wt.listWFStatesByTitle()
00207         except AttributeError:
00208             # Older Plone versions
00209             states = {}
00210             for wf in wt.objectValues():
00211                 state_folder = getattr(wf, 'states', None)
00212                 if state_folder is not None:
00213                     for s in state_folder.objectValues():
00214                         title, id = s.title, s.getId()
00215                         if title:
00216                             states[id] = title
00217             return states
00218 
00219         return dict([(id,title) for (title,id) in states])
00220 
00221     def icon(self, portal_type):
00222         type = self.ttool.getTypeInfo(portal_type)
00223         if type is None:
00224             return None
00225         return "%s/%s" % (self.base, type.getIcon())
00226 
00227     def media(self, portal_type):
00228         """Get the media type to be included in the xml.
00229         Since 'image' is the default we can omit it."""
00230         media = self.tool.getMediaForType(portal_type)
00231         if media=='image':
00232             return None
00233         return media
00234 
00235     def classes(self, portal_type):
00236         stored = self.tool.getClassesForType(portal_type)
00237         classes = []
00238         for c in stored:
00239             c = c.strip()
00240             if not c:
00241                 continue
00242             if '|' in c:
00243                 title, classname = c.split('|', 1)
00244                 classes.append({'title': title, 'classname': classname})
00245             else:
00246                 classes.append({'title': c, 'classname': c})
00247         return classes
00248 
00249     def sizes(self, obj):
00250         """Returns size, width, height"""
00251         if not self.showimagesize:
00252             return None, None, None
00253         try:
00254             if not callable(obj.getId):
00255                 obj = obj.getObject() # Must be a brain
00256             size = self.tool.getObjSize(obj)
00257         except:
00258             size = None
00259 
00260         width = getattr(obj, 'width', None)
00261         height = getattr(obj, 'height', None)
00262         if callable(width): width = width()
00263         if callable(height): height = height()
00264         return size, width, height
00265 
00266     def get_image_sizes(self, obj, portal_type, url):
00267         cache_key = (get_ident(), self.portal_base, portal_type)
00268         if not IMAGE_SIZES_CACHE.has_key(cache_key):
00269             IMAGE_SIZES_CACHE[cache_key] = {}
00270             imagefield = self.tool.getScaleFieldForType(portal_type)
00271             # if getId is not callable, we assume that we have a brain and
00272             # need to get the object
00273             if not callable(obj.getId):
00274                 if getattr(obj, 'getObject', None) is None:
00275                     return
00276                 try:
00277                     obj = obj.getObject()
00278                 except:
00279                     return 
00280                 
00281             if getattr(obj, 'getField', None) is None:
00282                 return
00283             image_field = obj.getWrappedField(imagefield)
00284             if image_field is None:
00285                 return
00286             if getattr(image_field, 'getAvailableSizes', None) is None:
00287                 return
00288             image_sizes = image_field.getAvailableSizes(obj)
00289             sizes = [(v[0], v[1], k, '%s_%s' % (imagefield,k)) for k,v in image_sizes.items()]
00290             sizes.sort()
00291             sizes.reverse()
00292             IMAGE_SIZES_CACHE[cache_key] = sizes
00293         else:
00294             sizes = IMAGE_SIZES_CACHE[cache_key]
00295         results = []
00296         for width, height, key, action in sizes:
00297             results.append({'label':"%s (%s, %s)" % (key.capitalize(), width, height),
00298                             'uri':"%s/%s" % (url, action),
00299                             'action': action},)
00300         return results
00301     
00302     def getState(self, review_state):
00303         if review_state:
00304             className = 'state-'+review_state
00305             review_state = self.workflow_states.get(review_state, review_state)
00306         else:
00307             className = None
00308         return review_state, className
00309 
00310     def info_object(self, obj, allowLink=True):
00311         '''Get information from a content object'''
00312 
00313         # Parent folder might not be accessible if we came here from a
00314         # search.
00315         if not self.security.checkPermission('View', obj):
00316             return None
00317 
00318         __traceback_info__ = (obj, allowLink)
00319         id = None
00320         UID = None
00321         try:
00322             if self.portal_interface.objectImplements(obj,
00323                 'Products.Archetypes.interfaces.referenceable.IReferenceable'):
00324                 UID = getattr(obj.aq_explicit, 'UID', None)
00325                 if UID:
00326                     UID = UID()
00327                     id = UID
00328 
00329             if not id:
00330                 id = obj.absolute_url(relative=1)
00331 
00332             portal_type = getattr(obj, 'portal_type','')
00333             collection = portal_type in self.coll_types
00334             tool = self.tool
00335             url = obj.absolute_url()
00336             preview = tool.getPreviewUrl(portal_type, url)
00337 
00338             if collection and self.resource_type.allow_browse:
00339                 src = obj.absolute_url()
00340                 if not src.endswith('/'): src += '/'
00341                 src += self.srctail
00342             else:
00343                 src = None
00344 
00345             if UID and self.linkbyuid:
00346                 url = self.base+'/resolveuid/%s' % UID
00347 
00348             if self.showimagesize:
00349                 normal = tool.getNormalUrl(portal_type, url)
00350             else:
00351                 normal = url
00352 
00353             sizes = self.get_image_sizes(obj, portal_type, url)
00354             defscale = self.tool.getDefaultScaleForType(portal_type)
00355 
00356             media = self.media(portal_type)
00357             classes = self.classes(portal_type)
00358 
00359             icon = self.icon(portal_type)
00360             size, width, height = self.sizes(obj)
00361 
00362             title = filterControlChars(obj.Title() or obj.getId())
00363             description = newline_to_br(html_quote(obj.Description()))
00364 
00365             linkable = None
00366             if allowLink:
00367                 linkable = True
00368                 collection = False
00369 
00370             anchor = portal_type in self.anchor_types
00371             review_state, className = self.getState(self.workflow_tool.getInfoFor(obj, 'review_state', None))
00372 
00373             return {
00374                 'id': id,
00375                 'url': normal,
00376                 'portal_type': portal_type,
00377                 'collection':  collection,
00378                 'icon': icon,
00379                 'size': size,
00380                 'width': width,
00381                 'height': height,
00382                 'preview': preview,
00383                 'sizes': sizes,
00384                 'defscale': defscale,
00385                 'media': media,
00386                 'classes': classes,
00387                 'title': title,
00388                 'description': description,
00389                 'linkable': linkable,
00390                 'src': src,
00391                 'anchor': anchor,
00392                 'state': review_state,
00393                 'class': className,
00394                 }
00395         except Unauthorized:
00396             return None
00397 
00398     def info(self, brain, allowLink=True):
00399         '''Get information from a brain'''
00400         __traceback_info__ = (brain, allowLink)
00401         id = brain.getId
00402         url = brain.getURL()
00403         portal_type = brain.portal_type
00404         collection = portal_type in self.coll_types
00405         tool = self.tool
00406         preview = tool.getPreviewUrl(portal_type, url)
00407 
00408         # Path for the uid catalog doesn't have the leading '/'
00409         path = brain.getPath()
00410         UID = None
00411         if path and self.uid_catalog:
00412             try:
00413                 metadata = self.uid_catalog.getMetadataForUID(path[self.prefix_length:])
00414             except KeyError:
00415                 metadata = None
00416             if metadata:
00417                 UID = metadata.get('UID', None)
00418 
00419         if UID:
00420             id = UID
00421 
00422         if collection and self.resource_type.allow_browse:
00423             src = brain.getURL()
00424             if not src.endswith('/'): src += '/'
00425             src += self.srctail
00426         else:
00427             src = None
00428 
00429         if UID and self.linkbyuid:
00430             url = self.base+'/resolveuid/%s' % UID
00431 
00432         if self.showimagesize:
00433             normal = tool.getNormalUrl(portal_type, url)
00434         else:
00435             normal = url
00436 
00437         sizes = self.get_image_sizes(brain, portal_type, url)
00438         defscale = self.tool.getDefaultScaleForType(portal_type)
00439         media = self.media(portal_type)
00440         classes = self.classes(portal_type)
00441 
00442         icon = self.icon(portal_type)
00443         size, width, height = self.sizes(brain)
00444 
00445         title = filterControlChars(brain.Title or brain.getId)
00446         description = newline_to_br(html_quote(brain.Description))
00447         linkable = None
00448         if allowLink:
00449             linkable = True
00450             collection = False
00451 
00452         anchor = portal_type in self.anchor_types
00453         review_state, className = self.getState(brain.review_state)
00454 
00455         return {
00456             'id': id,
00457             'url': normal,
00458             'portal_type': portal_type,
00459             'collection':  collection,
00460             'icon': icon,
00461             'size': size,
00462             'width': width,
00463             'height': height,
00464             'preview': preview,
00465             'sizes': sizes,
00466             'defscale': defscale,
00467             'media': media,
00468             'classes': classes,
00469             'title': title,
00470             'description': description,
00471             'linkable': linkable,
00472             'src': src,
00473             'anchor': anchor,
00474             'state': review_state,
00475             'class': className,
00476             }
00477 
00478 
00479 class PloneDrawers:
00480     security = ClassSecurityInfo()
00481 
00482     security.declareProtected("View", "getPreviewable")
00483     def getPreviewable(self):
00484         """Returns previewable types and a possible preview path"""
00485         if HAS_PIL:
00486             def best_preview(field):
00487                 sizes = getattr(field, 'sizes', None)
00488                 if not sizes:
00489                     return field.getName()
00490 
00491                 preview = None
00492                 previewsize = (0,0)
00493                 for k in sizes:
00494                     try:
00495                         if previewsize < sizes[k] <= (128,128):
00496                             preview = k
00497                             previewsize = sizes[k]
00498                     except TypeError: # Fails on Plone 2.1
00499                         return field.getName()
00500                 if not preview:
00501                     smallest = min(sizes.values())
00502                     for k in sizes:
00503                         if sizes[k]==smallest:
00504                             preview = k
00505                             break
00506                 return "%s_%s" % (field.getName(), preview)
00507 
00508         else:
00509             def best_preview(field):
00510                 return field.getName()
00511 
00512         result = []
00513         typestool = getToolByName(self, 'portal_types')
00514         archetype_tool = getToolByName(self, 'archetype_tool', None)
00515         if archetype_tool is None:
00516             return result
00517         valid = dict.fromkeys([t.getId() for t in typestool.listTypeInfo()])
00518         types = archetype_tool.listRegisteredTypes()
00519         for t in types:
00520             name = t['portal_type']
00521             if not name in valid:
00522                 continue
00523             schema = t['schema']
00524             for field in schema.fields():
00525                 if not isinstance(field, ImageField):
00526                     continue
00527                 result.append((name, best_preview(field)))
00528                 break
00529         return result
00530         
00531     security.declarePublic("getResourceType")
00532     def getResourceType(self, resource_type=None):
00533         """Convert resource type string to instance"""
00534         if isinstance(resource_type, ResourceType):
00535             return resource_type
00536 
00537         if not resource_type:
00538             resource_type = self.REQUEST.get('resource_type', 'mediaobject')
00539 
00540         return ResourceType(self, resource_type)
00541 
00542     security.declarePublic("getFolderItems")
00543     def getFolderItems(self, context, resource_type=None, portal=None):
00544         """List the contents of a folder"""
00545         resource_type = self.getResourceType(resource_type)
00546         collection_type = self.getResourceType('collection')
00547         coll_types = collection_type.portal_types
00548         link_types = resource_type.portal_types
00549         allow_browse = resource_type.allow_browse
00550 
00551         if portal is None:
00552             portal = getToolByName(self, 'portal_url').getPortalObject()
00553 
00554         query = resource_type.getQuery()
00555         content =  context.getFolderContents(contentFilter=query)
00556         linkable = dict([(o.id,1) for o in content ])
00557 
00558         if allow_browse and coll_types:
00559             # Do an extended query which includes collections whether
00560             # or not they were matched by the first query,
00561             # and which also, unfortunately may include content which
00562             # doesn't match the original query.
00563             query2 = {}
00564             if link_types:
00565                 query2['portal_type'] = tuple(link_types) + tuple(coll_types)
00566             else:
00567                 query2['portal_type'] = ()
00568 
00569             if 'sort_on' in query:
00570                 query2['sort_on'] = query['sort_on']
00571 
00572             allcontent = context.getFolderContents(contentFilter=query2)
00573             content = [c for c in allcontent
00574                 if c.portal_type in coll_types or c.id in linkable ]
00575 
00576         link_types = query['portal_type']
00577         items = self.infoForBrains(content, resource_type, portal, linkids=linkable)
00578 
00579         if allow_browse and context is not portal:
00580             parent = context.aq_parent
00581             pt = getattr(parent, 'portal_type', None)
00582             if pt in collection_type.portal_types:
00583                 data = self.getSingleObjectInfo(parent, resource_type)
00584                 data['label'] = '.. (Parent folder)'
00585                 items.insert(0, data)
00586         return items
00587 
00588     security.declarePublic("getSingleObjectInfo")
00589     def getSingleObjectInfo(self, context, resource_type=None, portal=None):
00590         """Return info for a single object"""
00591         if not resource_type: resource_type = self.getResourceType()
00592         if portal is None:
00593             portal = getToolByName(self, 'portal_url').getPortalObject()
00594         return self.infoForBrains([context], resource_type, portal)[0]
00595 
00596     security.declarePublic("getBreadCrumbs")
00597     def getBreadCrumbs(self, context, template):
00598         """Return breadcrumbs for drawer"""
00599         resource_type = self.getResourceType()
00600         if not resource_type.allow_browse:
00601             return []
00602 
00603         id = template.getId()
00604         putils = getToolByName(self, 'plone_utils')
00605         path = [ ("Home", getToolByName(self, 'portal_url')())]
00606 
00607         if getattr(putils.aq_base, 'createBreadCrumbs', None) is not None:
00608             path = path + [(x['Title'],x['absolute_url']) for x in putils.createBreadCrumbs(context)]
00609         else:
00610             path = path + self.breadcrumbs(context)[1:-1]
00611 
00612         # Last crumb should not be linked:
00613         path[-1] = (path[-1][0], None)
00614 
00615         crumbs = []
00616         for title,url in path:
00617             if url:
00618                 url = self.kupuUrl("%s/%s" % (url.rstrip('/'), id))
00619             crumbs.append({'Title':title, 'absolute_url':url})
00620 
00621         return crumbs
00622 
00623     security.declareProtected('View','getCurrentObject')
00624     def getCurrentObject(self, portal=None):
00625         '''Returns object information for a selected object'''
00626         request = self.REQUEST
00627         if portal is None:
00628             portal = getToolByName(self, 'portal_url').getPortalObject()
00629         reference_tool = getToolByName(portal, 'reference_catalog')
00630         rt = self.getResourceType()
00631         portal_types = rt.portal_types
00632         src = request.get('src')
00633         # Remove any spurious query string or fragment
00634         for c in '#?':
00635             if c in src:
00636                 src = src.split(c, 1)[0]
00637 
00638         match = UIDURL.match(src)
00639 
00640         if match:
00641             # src=http://someurl/resolveuid/<uid>
00642             uid = match.group(1)
00643             obj = reference_tool.lookupObject(uid)
00644             
00645         elif src and '://' in src:
00646             # src=http://someurl/somepath/someobject
00647             base = portal.absolute_url()
00648             if src.startswith(base):
00649                 src = src[len(base):].lstrip('/')
00650             try:
00651                 obj = portal.restrictedTraverse(src)
00652                 if portal_types:
00653                     while not shasattr(obj.aq_base, 'portal_type'):
00654                         obj = obj.aq_parent
00655                     while obj.portal_type not in portal_types:
00656                         obj = obj.aq_parent
00657                         if obj is portal:
00658                             return []
00659             except (KeyError, AttributeError):
00660                 return []
00661         else:
00662             # src=<uid1> <uid2> ... <uidn>
00663             src = src.split(' ') # src is a list of uids.
00664             objects = [ reference_tool.lookupObject(uid) for uid in src ]
00665             objects = [ o for o in objects if o is not None ]
00666             return objects
00667 
00668         if obj is None:
00669             return None
00670         return [obj]
00671 
00672     security.declarePublic("getCurrentParent")
00673     def getCurrentParent(self):
00674         """Find the parent of the object specified in the src string.
00675         If multiple objects and they don't have the same parent, or if no suitable object
00676         returns None, otherwise returns the parent."""
00677         objects = self.getCurrentObject()
00678         parent = None
00679         for obj in objects:
00680             if parent is not None and parent is not obj.aq_parent:
00681                 return None
00682             parent = obj.aq_parent
00683         return parent
00684 
00685     security.declarePublic("getCurrentSelection")
00686     def getCurrentSelection(self, portal=None):
00687         '''Returns object information for a selected object'''
00688         objects = self.getCurrentObject(portal)
00689         return self.infoForBrains(objects, self.getResourceType(), portal)
00690 
00691     security.declarePublic("getMyItems")
00692     def getMyItems(self):
00693         request = self.REQUEST
00694         response = request.RESPONSE
00695         response.setHeader('Cache-Control', 'no-cache')
00696 
00697         member_tool = getToolByName(self, 'portal_membership')
00698         member = member_tool.getAuthenticatedMember()
00699 
00700         # the default resource type is mediaobject
00701         search_params = {}
00702         search_params['sort_on'] = 'modified'
00703         search_params['sort_order'] = 'reverse'
00704         search_params['limit'] = 20
00705         search_params['Creator'] = member.getMemberId()
00706 
00707         return self.infoForQuery(search_params)
00708 
00709     security.declarePublic("getRecentItems")
00710     def getRecentItems(self):
00711         search_params = {}
00712         search_params['sort_on'] = 'modified'
00713         search_params['sort_order'] = 'reverse'
00714         search_params['limit'] = 20
00715         search_params['review_state'] = 'visible', 'published'
00716 
00717         return self.infoForQuery(search_params)
00718 
00719     security.declarePublic("kupuSearch")
00720     def kupuSearch(self):
00721         request = self.REQUEST
00722         search_params = {}
00723         search_params.update(request.form)
00724 
00725         # Get the maximum number of results with 500 being the default and
00726         # absolute maximum.
00727         abs_max = 500
00728         search_params['limit'] = min(request.get('max_results', abs_max), abs_max)
00729 
00730         return self.infoForQuery(search_params)
00731 
00732     security.declareProtected("View", "infoForQuery")
00733     def infoForQuery(self, query, resource_type=None, portal=None):
00734         resource_type = self.getResourceType()
00735         if portal is None:
00736             portal = getToolByName(self, 'portal_url').getPortalObject()
00737         
00738         baseQuery = resource_type.getQuery()
00739         query.update(baseQuery)
00740         limit = query.get('limit', None)
00741         pt = query['portal_type']
00742         if not pt:
00743             del query['portal_type']
00744         catalog = getToolByName(portal, 'portal_catalog')
00745         values = catalog.searchResults(query)
00746         if limit:
00747             values = values[:limit]
00748         return self.infoForBrains(values, resource_type, portal)
00749 
00750     security.declareProtected("View", "infoForBrains")
00751     def infoForBrains(self, values, resource_type, portal=None, linkids=None):
00752         request = self.REQUEST
00753         response = request.RESPONSE
00754         response.setHeader('Cache-Control', 'no-cache')
00755         linktypes=resource_type.portal_types
00756         if portal is None:
00757             portal = getToolByName(self, 'portal_url').getPortalObject()
00758         
00759         adaptor = InfoAdaptor(self, resource_type, portal)
00760         
00761         # For Plone 2.0.5 compatability, if getId is callable we assume
00762         # we have an object rather than a brains.
00763         if values and callable(values[0].getId):
00764             info = adaptor.info_object
00765         else:
00766             info = adaptor.info
00767 
00768         res = []
00769 
00770         for obj in values:
00771             portal_type = getattr(obj, 'portal_type', '')
00772 
00773             if linkids is not None:
00774                 linkable = obj.id in linkids
00775             elif len(linktypes)==0:
00776                 linkable = True
00777             else:
00778                 linkable = portal_type in linktypes
00779 
00780             data = info(obj, linkable)
00781             if data:
00782                 res.append(data)
00783         return res
00784 
00785     security.declareProtected("View", "canCaption")
00786     def canCaption(self, field):
00787         return (getattr(field, 'default_output_type', None) in
00788             ('text/x-html-safe', 'text/x-html-captioned'))
00789 
00790     security.declarePublic("getLabelFromWidget")
00791     def getLabelFromWidget(self, widget):
00792         """Get the label for a widget converting from i18n message if needed"""
00793         label = util.translate(widget.Label(self), self.REQUEST)
00794         if isinstance(label, str):
00795             label = label.decode('utf8', 'replace')
00796         return label
00797 
00798     security.declareProtected("View", "getKupuFields")
00799     def getKupuFields(self, filter=1):
00800         """Returns a list of all kupu editable fields"""
00801         inuse = getToolByName(self, 'portal_catalog').uniqueValuesFor('portal_type')
00802         for t,f,pt in self._getKupuFields():
00803             if html2captioned.sanitize_portal_type(pt) in inuse or not filter:
00804                 yield dict(type=t, name=f.getName(), portal_type=pt,
00805                            label=self.getLabelFromWidget(f.widget))
00806 
00807     def _getKupuFields(self):
00808         """Yield all fields which are editable using kupu"""
00809         archetype_tool = getToolByName(self, 'archetype_tool', None)
00810         types = archetype_tool.listRegisteredTypes()
00811         for t in types:
00812             schema = t.get('schema')
00813             if schema:
00814                 typename = getattr(t['klass'], 'archetype_name', t['portal_type'])
00815                 for f in schema.fields():
00816                     w = f.widget
00817                     if isinstance(w, (RichWidget, VisualWidget)):
00818                         yield typename, f, t['portal_type']
00819 
00820     security.declareProtected("View", "supportedCaptioning")
00821     def supportedCaptioning(self):
00822         """Returns a list of document/fields which have support for captioning"""
00823         supported = [t+'/'+self.getLabelFromWidget(f.widget) for (t,f,pt) in self._getKupuFields() if self.canCaption(f) ]
00824         return str.join(', ', supported)
00825 
00826     security.declareProtected("View", "unsupportedCaptioning")
00827     def unsupportedCaptioning(self):
00828         """Returns a list of document/fields which do not have support for captioning"""
00829         unsupp = [t+'/'+self.getLabelFromWidget(f.widget) for (t,f,pt) in self._getKupuFields() if not self.canCaption(f) ]
00830         return str.join(', ', unsupp)
00831 
00832     security.declareProtected("View", "transformIsEnabled")
00833     def transformIsEnabled(self):
00834         """Test whether the output transform is enabled for x-html-safe"""
00835         uid_catalog = getToolByName(self, 'uid_catalog', None)
00836         portal_transforms = getToolByName(self, 'portal_transforms', None)
00837         if not uid_catalog or not portal_transforms:
00838             return False
00839         # Find something, anything which has a UID
00840         content = uid_catalog.searchResults(sort_on='', sort_limit=1)
00841         if not content:
00842             return False
00843         uid = content[0].UID # Get an arbitrary used UID.
00844         link = 'resolveuid/%s' % uid
00845         test = '<a href="%s"></a>' % link
00846         txfrm = portal_transforms.convertTo('text/x-html-safe', test, mimetype='text/html', context=self)
00847         if hasattr(txfrm, 'getData'):
00848             txfrm = txfrm.getData()
00849         return txfrm and not link in txfrm
00850 
00851     security.declareProtected("View", "isUploadSupported")
00852     def isUploadSupported(self, context):
00853         """Returns True if we can upload the the current folder."""
00854         resource_type = self.getResourceType()
00855         if resource_type.name != 'mediaobject':
00856             return False
00857 
00858         allowedContent = context.getAllowedTypes()
00859         allowed = dict.fromkeys([a.getId() for a in allowedContent])
00860         for t in resource_type.portal_types:
00861             if t in allowed:
00862                 return True
00863         return False
00864 
00865     security.declareProtected("View", "getBaseUrl")
00866     def getBaseUrl(self, context, include_factory=False, resource_type=None):
00867         base = context.absolute_url()
00868 
00869         if resource_type:
00870             rt = self.getResourceType(resource_type);
00871             sd = rt.startup_directory
00872             if sd:
00873                 base = sd
00874 
00875         posfactory = base.find('/portal_factory/')
00876         if posfactory>0:
00877             if include_factory:
00878                 base = base[:posfactory+15]
00879             else:
00880                 base = base[:posfactory]
00881         return base
00882 
00883     security.declareProtected("View", "getUploadImageSizes")
00884     def getUploadImageSizes(self, portal_type=None):
00885         resource_type = self.getResourceType()
00886         portal = getToolByName(self, 'portal_url').getPortalObject()
00887         mediatypes = resource_type.get_portal_types()
00888         catalog = getToolByName(self, 'portal_catalog')
00889         adaptor = InfoAdaptor(self, resource_type, portal)
00890         if portal_type is None:
00891             portal_type = self.getDefaultImageType()
00892 
00893         brains = catalog.searchResults(portal_type=portal_type, limit=1)[:1]
00894         if brains:
00895             info = adaptor.get_image_sizes(brains[0], portal_type, '')
00896             return info
00897         return []
00898 
00899     security.declareProtected("View", "getUploadImageClasses")
00900     def getUploadImageClasses(self, portal_type=None):
00901         if portal_type is None:
00902             portal_type = self.getDefaultImageType()
00903         stored = self.getClassesForType(portal_type)
00904         classes = []
00905         for c in stored:
00906             c = c.strip()
00907             if not c:
00908                 continue
00909             if '|' in c:
00910                 title, classname = c.split('|', 1)
00911                 classes.append({'title': title, 'classname': classname})
00912             else:
00913                 classes.append({'title': c, 'classname': c})
00914         return classes
00915 
00916 
00917     def _getImageSizes(self):
00918         resource_type = self.getResourceType()
00919         portal = getToolByName(self, 'portal_url').getPortalObject()
00920         mediatypes = resource_type.get_portal_types()
00921         catalog = getToolByName(self, 'portal_catalog')
00922         adaptor = InfoAdaptor(self, resource_type, portal)
00923 
00924         sizes = {}
00925         for portal_type in mediatypes:
00926             brains = catalog.searchResults(portal_type=portal_type, limit=1)[:1]
00927             if brains:
00928                 info = adaptor.get_image_sizes(brains[0], portal_type, '')
00929                 if info:
00930                     for i in info:
00931                         sizes[i['uri']] = 1
00932         return sizes
00933 
00934     security.declareProtected("View", "convertUidsToPaths")
00935     def convertUidsToPaths(self, value=None):
00936         """Convert a list of uids
00937         (or a single space or newline separated string)
00938         to a list of paths"""
00939         uid_catalog = getToolByName(self, 'uid_catalog')
00940         ppath = getToolByName(self, 'portal_url').getPortalPath()[1:]+'/'
00941         
00942         if isinstance(value, basestring):
00943             value = value.split()
00944         if not value:
00945             return []
00946             
00947         brains = uid_catalog.searchResults(UID=value)
00948         paths = [ppath+b.getPath() for b in brains]
00949         return paths
00950 
00951 InitializeClass(PloneDrawers)