Back to index

plone3  3.1.7
librarytool.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 """Kupu library tool
00011 
00012 This module contains Kupu's library tool to support drawers.
00013 
00014 $Id: librarytool.py 54981 2008-05-20 02:40:08Z ldr $
00015 """
00016 import Acquisition
00017 from Acquisition import aq_parent, aq_inner, aq_base
00018 from Products.CMFCore.Expression import Expression, createExprContext
00019 from Products.PageTemplates.Expressions import getEngine, SecureModuleImporter
00020 from Products.kupu.plone.interfaces import IKupuLibraryTool
00021 from Products.CMFCore.utils import getToolByName
00022 from zope.interface import implements
00023 
00024 class KupuError(Exception): pass
00025 NEWTYPE_IGNORE, NEWTYPE_ADD = 0, 1
00026 
00027 class Resource:
00028     """Class to hold resources"""
00029     
00030 class KupuLibraryTool(Acquisition.Implicit):
00031     """A tool to aid Kupu libraries"""
00032 
00033     implements(IKupuLibraryTool)
00034 
00035     def __init__(self):
00036         self._libraries = []
00037         self._res_types = {}
00038 
00039     def _getExpressionContext(self, object):
00040         portal = aq_parent(aq_inner(self))
00041         if object is None or not hasattr(object, 'aq_base'):
00042             folder = portal
00043         else:
00044             folder = object
00045             # Search up the containment hierarchy until we find an
00046             # object that claims it's a folder.
00047             while folder is not None:
00048                 if getattr(aq_base(folder), 'isPrincipiaFolderish', 0):
00049                     # found it.
00050                     break
00051                 else:
00052                     folder = aq_parent(aq_inner(folder))
00053         ec = createExprContext(folder, portal, object)
00054         return ec
00055 
00056     def addLibrary(self, id, title, uri, src, icon):
00057         """See ILibraryManager"""
00058         lib = dict(id=id, title=title, uri=uri, src=src, icon=icon)
00059         for key, value in lib.items():
00060             if key=='id':
00061                 lib[key] = value
00062             else:
00063                 if not(value.startswith('string:') or value.startswith('python:')):
00064                     value = 'string:' + value
00065                 lib[key] = Expression(value)
00066         self._libraries.append(lib)
00067 
00068     def getLibraries(self, context):
00069         """See ILibraryManager"""
00070         expr_context = self._getExpressionContext(context)
00071         libraries = []
00072         for library in self._libraries:
00073             lib = {}
00074             for key in library.keys():
00075                 if isinstance(library[key], str):
00076                     lib[key] = library[key]
00077                 else:
00078                     # Automatic migration from old version.
00079                     if key=='id':
00080                         lib[key] = library[key] = library[key].text
00081                     else:
00082                         lib[key] = library[key](expr_context)
00083             libraries.append(lib)
00084         return tuple(libraries)
00085 
00086     def deleteLibraries(self, indices):
00087         """See ILibraryManager"""
00088         indices.sort()
00089         indices.reverse()
00090         for index in indices:
00091             del self._libraries[index]
00092 
00093     def updateLibraries(self, libraries):
00094         """See ILibraryManager"""
00095         for index, lib in enumerate(self._libraries):
00096             dic = libraries[index]
00097             for key in lib.keys():
00098                 if dic.has_key(key):
00099                     value = dic[key]
00100                     if key=='id':
00101                         lib[key] = value
00102                     else:
00103                         if not(value.startswith('string:') or
00104                                value.startswith('python:')):
00105                             value = 'string:' + value
00106                         lib[key] = Expression(value)
00107             self._libraries[index] = lib
00108 
00109     def moveUp(self, indices):
00110         """See ILibraryManager"""
00111         indices.sort()
00112         libraries = self._libraries[:]
00113         for index in indices:
00114             new_index = index - 1
00115             libraries[index], libraries[new_index] = \
00116                               libraries[new_index], libraries[index]
00117         self._libraries = libraries
00118 
00119     def moveDown(self, indices):
00120         """See ILibraryManager"""
00121         indices.sort()
00122         indices.reverse()
00123         libraries = self._libraries[:]
00124         for index in indices:
00125             new_index = index + 1
00126             if new_index >= len(libraries):
00127                 new_index = 0
00128                 #new_index = ((index + 1) % len(libraries)) - 1
00129             libraries[index], libraries[new_index] = \
00130                               libraries[new_index], libraries[index]
00131         self._libraries = libraries
00132 
00133     def getNewTypeHandler(self, resource_type):
00134         """Should unknown portal types be added to the list or ignored"""
00135         _res_newtype = getattr(self, '_res_newtype', None)
00136         if _res_newtype is None:
00137             self._res_newtype = _res_newtype = {}
00138             for k in self._res_types:
00139                 if k in ('linkable', 'containsanchors', 'composable'):
00140                     _res_newtype[k] = NEWTYPE_ADD
00141                 else:
00142                     _res_newtype[k] = NEWTYPE_IGNORE
00143             self._res_newtype = _res_newtype
00144 
00145         return _res_newtype.get(resource_type, NEWTYPE_IGNORE)
00146 
00147     def setNewTypeHandler(self, resource_type, mode):
00148         """Update how unknown types are handled."""
00149         if self.getNewTypeHandler(resource_type) != mode:
00150             self._res_newtype[resource_type] = mode
00151             self._res_newtype = self._res_newtype # Flag ourselves as modified.
00152 
00153     def checkNewResourceTypes(self, resource_type=None):
00154         # Check for new types added. It would be nice if this
00155         # was called automatically but not every time we query a
00156         # resource.
00157         if resource_type != None:
00158             handle_new = self.getNewTypeHandler(resource_type)
00159             if handle_new == NEWTYPE_IGNORE:
00160                 return
00161                 
00162         typetool = getToolByName(self, 'portal_types')
00163         new_portal_types = dict([ (t.id, 1) for t in typetool.listTypeInfo()])
00164         if getattr(self, '_last_known_types', None) is None:
00165             # Migrate from old version
00166             self._last_known_types = new_portal_types
00167         else:
00168             for t in self._last_known_types:
00169                 if t in new_portal_types:
00170                     del new_portal_types[t]
00171             if new_portal_types:
00172                 self._addNewTypesToResources()
00173 
00174     def _addNewTypesToResources(self):
00175         """This method is called when the list of types in the system has changed.
00176         It updates all current resource types to include or exclude new types as
00177         appropriate.
00178         """
00179         typetool = getToolByName(self, 'portal_types')
00180         alltypes = typetool.listTypeInfo()
00181         lastknown = self._last_known_types
00182         newtypes = dict.fromkeys([ t.id for t in alltypes if t.id not in lastknown])
00183 
00184         for resource_type in self._res_types.keys():
00185             handle_new = self.getNewTypeHandler(resource_type)
00186             if handle_new==NEWTYPE_ADD:
00187                 types = dict.fromkeys(self._res_types[resource_type])
00188                 types.update(newtypes)
00189                 self._res_types[resource_type] = types.keys()
00190         self._res_types = self._res_types
00191         
00192     def getPortalTypesForResourceType(self, resource_type):
00193         """See IResourceTypeMapper"""
00194         self.checkNewResourceTypes()
00195         types = self._res_types[resource_type]
00196         return types[:]
00197 
00198     def queryPortalTypesForResourceType(self, resource_type, default=None):
00199         """See IResourceTypeMapper"""
00200         if not self._res_types.has_key(resource_type):
00201             return default
00202         return self.getPortalTypesForResourceType(resource_type)
00203 
00204     def _validate_portal_types(self, resource_type, portal_types):
00205         typetool = getToolByName(self, 'portal_types')
00206         all_portal_types = dict([ (t.id, 1) for t in typetool.listTypeInfo()])
00207 
00208         portal_types = [ptype.strip() for ptype in portal_types if ptype]
00209         for p in portal_types:
00210             if p not in all_portal_types:
00211                 raise KupuError, "Resource type: %s, invalid type: %s" % (resource_type, p)
00212         return portal_types
00213 
00214     def invertTypeList(self, types):
00215         """Convert a list of portal_types to a list of all the types not in the list"""
00216         typetool = getToolByName(self, 'portal_types')
00217         portal_types = dict([ (t.id, 1) for t in typetool.listTypeInfo()])
00218         res = [ name for name in portal_types if name not in types ]
00219         res.sort()
00220         return res
00221 
00222     def addResourceType(self, resource_type, portal_types, mode='whitelist'):
00223         """See IResourceTypeMapper"""
00224         newtype = NEWTYPE_IGNORE
00225         if mode != 'whitelist':
00226             portal_types = self.invertTypeList(portal_types)
00227             newtype = NEWTYPE_ADD
00228         portal_types = self._validate_portal_types(resource_type, portal_types)
00229         self._res_types[resource_type] = tuple(portal_types)
00230         self.setNewTypeHandler(resource_type, newtype)
00231 
00232     def updateResourceTypes(self, type_info):
00233         """See IResourceTypeMapper"""
00234         type_map = self._res_types
00235         
00236         for type in type_info:
00237             resource_type = type['resource_type']
00238             if not resource_type:
00239                 continue
00240             portal_types = self._validate_portal_types(resource_type, type.get('portal_types', ()))
00241             old_type = type.get('old_type', None)
00242             if old_type:
00243                 del type_map[old_type]
00244             type_map[resource_type] = tuple(portal_types)
00245             nt = type.get('newtypes', None)
00246             if nt is not None:
00247                 self.setNewTypeHandler(resource_type, nt)
00248 
00249     def updatePreviewActions(self, preview_actions):
00250         """Now a misnomer: actually updates preview, normal, and scaling data"""
00251         action_map = {}
00252 
00253         for a in preview_actions:
00254             portal_type = a.get('portal_type', '')
00255             preview = a.get('expression', '')
00256             normal = a.get('normal', None)
00257             if normal:
00258                 normal = Expression(normal)
00259             scalefield = a.get('scalefield', 'image')
00260             defscale = a.get('defscale', 'image_preview')
00261             classes = a.get('classes', '')
00262             if isinstance(classes, basestring):
00263                 classes = classes.split('\n')
00264             classes = tuple(classes)
00265             mediatype = a.get('mediatype', 'image')
00266             if not portal_type:
00267                 continue
00268             action_map[portal_type] = {
00269                 'expression': Expression(preview),
00270                 'normal': normal,
00271                 'scalefield': scalefield,
00272                 'defscale': defscale,
00273                 'classes': classes,
00274                 'mediatype': mediatype,
00275             }
00276         self._preview_actions = action_map
00277 
00278     def deleteResourceTypes(self, resource_types):
00279         """See IResourceTypeMapper"""
00280         existing = self._res_types
00281         for type in resource_types:
00282             if existing.has_key(type):
00283                 del existing[type]
00284 
00285     def deletePreviewActions(self, preview_types):
00286         """See IResourceTypeMapper"""
00287         action_map = getattr(self, '_preview_actions', {})
00288         for type in preview_types:
00289             del action_map[type]
00290         self._preview_actions = action_map
00291 
00292     def getPreviewUrl(self, portal_type, url):
00293         action_map = getattr(self, '_preview_actions', {})
00294         if portal_type in action_map:
00295             expr = action_map[portal_type]['expression']
00296             if expr:
00297                 data = {
00298                     'object_url':   url,
00299                     'portal_type':  portal_type,
00300                     'modules':      SecureModuleImporter,
00301                 }
00302                 context = getEngine().getContext(data)
00303                 return expr(context)
00304         return None
00305 
00306     def getNormalUrl(self, portal_type, url):
00307         action_map = getattr(self, '_preview_actions', {})
00308         if portal_type in action_map:
00309             expr = action_map[portal_type].get('normal', None)
00310             if expr:
00311                 data = {
00312                     'object_url':   url,
00313                     'portal_type':  portal_type,
00314                     'modules':      SecureModuleImporter,
00315                 }
00316                 context = getEngine().getContext(data)
00317                 return expr(context)
00318         return url
00319 
00320     def _setToolbarFilters(self, filters, globalfilter):
00321         """Set the toolbar filtering
00322         filter is a list of records with: id, visible, override"""
00323         clean = {}
00324         for f in filters:
00325             id = f['id']
00326             visible = bool(f.get('visible', False))
00327             expr = f.get('override', None)
00328 
00329             if not id:
00330                 continue
00331 
00332             if expr:
00333                 expr = Expression(expr)
00334             else:
00335                 expr = None
00336             clean[id] = dict(id=id, visible=visible, override=expr)
00337 
00338         self._toolbar_filters = clean
00339         if globalfilter:
00340             self._global_toolbar_filter = Expression(globalfilter)
00341         else:
00342             self._global_toolbar_filter = None
00343 
00344     def getToolbarFilters(self, context, field=None):
00345         expr_context = self._getExpressionContext(context)
00346         expr_context.setGlobal('field', field)
00347         filters = getattr(self, '_toolbar_filters', {})
00348         gfilter = getattr(self, '_global_toolbar_filter', None)
00349         if gfilter:
00350             gvisible = gfilter(expr_context)
00351         else:
00352             gvisible = None
00353 
00354         visible = {}
00355         for k in filters:
00356             f = filters[k]
00357             override = f.get('override', None)
00358             if override:
00359                 visible[k] = bool(override(expr_context))
00360             else:
00361                 visible[k] = f['visible']
00362         return visible, gvisible
00363 
00364     def _getToolbarFilterOptions(self):
00365         return getattr(self, '_toolbar_filters', {})
00366 
00367     def spellcheck(self, REQUEST):
00368         """Call spellchecker: WARNING this functionality may not work,
00369         it is not part of the standard Plone+kupu implementation.
00370         Some user assembly required."""
00371         from Products.kupu.python.spellcheck import SpellChecker, format_result
00372         data = REQUEST["text"]
00373         c = SpellChecker()
00374         result = c.check(data)
00375         if result == None:
00376             result = ""
00377         else:
00378             result = format_result(result)
00379         REQUEST.RESPONSE.setHeader("Content-Type","text/xml, charset=utf-8")
00380         REQUEST.RESPONSE.setHeader("Content-Length",str(len(result)))
00381         return result