Back to index

plone3  3.1.7
plonelibrarytool.py
Go to the documentation of this file.
00001 # -*- coding: latin-1 -*-
00002 ##############################################################################
00003 #
00004 # Copyright (c) 2003-2005 Kupu Contributors. All rights reserved.
00005 #
00006 # This software is distributed under the terms of the Kupu
00007 # License. See LICENSE.txt for license text. For a list of Kupu
00008 # Contributors see CREDITS.txt.
00009 #
00010 ##############################################################################
00011 """Plone Kupu library tool
00012 
00013 This module contains the Plone specific version of the Kupu library
00014 tool.
00015 
00016 $Id: plonelibrarytool.py 58620 2008-10-06 09:46:29Z duncan $
00017 """
00018 import os
00019 from ZODB.PersistentList import PersistentList
00020 from ZODB.PersistentMapping import PersistentMapping
00021 from AccessControl import ClassSecurityInfo
00022 from OFS.SimpleItem import SimpleItem
00023 import Globals
00024 from Globals import InitializeClass
00025 
00026 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
00027 from Products.CMFCore.utils import UniqueObject
00028 from Products.PythonScripts.standard import Object
00029 
00030 from Products.kupu.plone.librarytool import KupuLibraryTool
00031 from Products.kupu.plone import permissions, scanner, plonedrawers, util, helpers
00032 from Products.CMFCore.utils import getToolByName
00033 from Products.kupu import kupu_globals
00034 from Products.kupu.config import TOOLNAME, TOOLTITLE
00035 from StringIO import StringIO
00036 from urllib import quote_plus, unquote_plus
00037 import html2captioned
00038 try:
00039     from plone.app.controlpanel import filter
00040     HAVE_PLONE_FILTERING = True
00041 except:
00042     HAVE_PLONE_FILTERING = False
00043 
00044 # Zope 3 interfaces, but on older zopes we'll just skip this bit.
00045 try:
00046     from zope.interface import implements
00047 except ImportError:
00048     def implements(*args): pass
00049 
00050 from z3interfaces import IPloneKupuLibraryTool
00051 
00052 _default_libraries = (
00053     dict(id="root",
00054          title="string:Home",
00055          uri="string:${globals_view/navigationRootUrl|portal_url}",
00056          src="string:${globals_view/navigationRootUrl|portal_url}/kupucollection.xml",
00057          icon="string:${globals_view/navigationRootUrl|portal_url}/misc_/CMFPlone/plone_icon"),
00058     dict(id="current",
00059          title="string:Current folder",
00060          uri="string:${folder_url}",
00061          src="string:${folder_url}/kupucollection.xml",
00062          icon="string:${folder/getIcon}"),
00063     dict(id="myitems",
00064          title="string:My recent items",
00065          uri="string:${portal_url}/kupumyitems.xml",
00066          src="string:${portal_url}/kupumyitems.xml",
00067          icon="string:${portal_url}/kupuimages/kupusearch_icon.gif"),
00068     dict(id="recentitems",
00069          title="string:Recent items",
00070          uri="string:${portal_url}/kupurecentitems.xml",
00071          src="string:${portal_url}/kupurecentitems.xml",
00072          icon="string:${portal_url}/kupuimages/kupusearch_icon.gif")
00073     )
00074 
00075 _default_resource_types = {
00076     'collection': ('Plone Site', 'Folder', 'Large Plone Folder'),
00077     'mediaobject': ('Image',),
00078     'linkable': ('Document', 'Image', 'File', 'News Item', 'Event', 'Folder', 'Large Plone Folder'),
00079     'containsanchors': ('Document', 'News Item', 'Event'),
00080     }
00081 
00082 # Tidy up html by exlcluding lots of things.
00083 _excluded_html = [
00084   (('center', 'tt', 'big', 'small', 'basefont', 'font'), ()),
00085   ((), ('lang','valign','halign','border','frame','rules','cellspacing','cellpadding','bgcolor')),
00086   (('table','th','td'),('width','height')),
00087 ]
00088 
00089 # Default should list all styles used by Kupu
00090 _style_whitelist = ['text-align', 'list-style-type', 'float']
00091 
00092 _default_paragraph_styles = (
00093     "Heading|h2",
00094     "Subheading|h3",
00095     "Literal|pre",
00096     "Discreet|p|discreet",
00097     "Pull-quote|div|pullquote",
00098     "Call-out|p|callout",
00099     "Highlight|span|visualHighlight",
00100     "Odd row|tr|odd",
00101     "Even row|tr|even",
00102     "Heading cell|th|",
00103 )
00104 
00105 class PloneKupuLibraryTool(UniqueObject, SimpleItem, KupuLibraryTool,
00106     plonedrawers.PloneDrawers):
00107     """Plone specific version of the kupu library tool"""
00108 
00109     id = TOOLNAME
00110     meta_type = "Kupu Library Tool"
00111     title = TOOLTITLE
00112     security = ClassSecurityInfo()
00113     implements(IPloneKupuLibraryTool)
00114 
00115     # protect methods provided by super class KupuLibraryTool
00116     security.declareProtected(permissions.QueryLibraries, "getLibraries",
00117                               "getPortalTypesForResourceType")
00118     security.declareProtected(permissions.ManageLibraries, "addLibrary",
00119                               "deleteLibraries", "updateLibraries",
00120                               "moveUp", "moveDown")
00121     security.declareProtected(permissions.ManageLibraries, "addResourceType",
00122                               "updateResourceTypes", "deleteResourceTypes")
00123 
00124     def __init__(self):
00125         self._libraries = PersistentList()
00126         self._res_types = PersistentMapping()
00127         self.linkbyuid = False
00128 
00129     def manage_afterAdd(self, item, container):
00130         # We load default values here, so __init__ can still be used
00131         # in unit tests. Plus, it only makes sense to load these if
00132         # we're being added to a Plone site anyway
00133         if not len(self._libraries):
00134             for lib in _default_libraries:
00135                 self.addLibrary(**lib)
00136             self._res_types.update(_default_resource_types)
00137 
00138     security.declareProtected('View', "getLinkbyuid")
00139     def getLinkbyuid(self):
00140         """Returns 'is linking by UID enabled'?"""
00141         try:
00142             return bool(self.linkbyuid)
00143         except AttributeError:
00144             return False
00145 
00146     security.declareProtected('View', "getRefBrowser")
00147     def getRefBrowser(self):
00148         """Returns True if kupu_references is in all skin layers"""
00149         return util.layer_installed(self, 'kupu_references')
00150 
00151     security.declareProtected(permissions.ManageLibraries, 'ensureReferencesLayer')
00152     def ensureReferencesLayer(self, add=False):
00153         """Called from the link tab code: we must have the
00154         kupu_references directory view at least present for
00155         the link tab to work."""
00156         out = StringIO()
00157         util.register_layer(self, 'plone/kupu_references', 'kupu_references', out, add)
00158 
00159     security.declareProtected('View', "getCaptioning")
00160     def getCaptioning(self):
00161         """Returns True if captioning is enabled"""
00162         try:
00163             return bool(self.captioning)
00164         except AttributeError:
00165             return False
00166 
00167     security.declareProtected('View', "getTableClassnames")
00168     def getTableClassnames(self):
00169         """Return a list of classnames supported in tables"""
00170         try:
00171             return self.table_classnames
00172         except AttributeError:
00173             return ('plain', 'listing', 'vertical listing', 'listing nosort|unsorted listing')
00174 
00175     security.declareProtected('View', "getParagraphStyles")
00176     def getParagraphStyles(self):
00177         """Return a list of classnames supported by paragraphs"""
00178         try:
00179             return self.paragraph_styles
00180         except AttributeError:
00181             return _default_paragraph_styles
00182 
00183     security.declareProtected('View', "getStyleList")
00184     def getStyleList(self, field=None):
00185         """Return the styles for a field."""
00186         gstyles = self.getParagraphStyles()
00187         if field:
00188             widget = field.widget
00189             redefine = getattr(widget, 'redefine_parastyles', False)
00190             lstyles = getattr(widget, 'parastyles', ())
00191         else:
00192             redefine = False
00193             lstyles = []
00194 
00195         result = []
00196         __traceback_info__ = (gstyles, lstyles)
00197         if redefine:
00198             styles = lstyles
00199         else:
00200             styles = list(gstyles) + list(lstyles)
00201             
00202         for style in styles:
00203             parts = style.split('|',1)+['','']
00204             value = parts[1]
00205             content = parts[0]
00206             result.append({'value':value, 'content':content})
00207 
00208         return result
00209 
00210     security.declareProtected('View', "filterToolbar")
00211     def filterToolbar(self, context, field=None):
00212         return helpers.ButtonFilter(self, context, field)
00213 
00214     security.declareProtected('View', "getFilterOptions")
00215     def getFilterOptions(self, field=None):
00216         filters = helpers.FILTERS
00217         config = self._getToolbarFilterOptions()
00218         res = []
00219         for (id, title, default, klass) in filters:
00220             cfg = config.get(id, {})
00221             visible = cfg.get('visible', default)
00222             expr = cfg.get('override', None)
00223             if expr is not None:
00224                 expr = expr.text
00225             res.append(dict(id=id, title=title, visible=visible, override=expr, classname=klass))
00226         return res
00227 
00228     security.declareProtected(permissions.ManageLibraries, "set_toolbar_filters")
00229     def set_toolbar_filters(self, filters, globalfilter, REQUEST=None):
00230         """Set the toolbar filtering
00231         filter is a list of records with: id, checked, expression"""
00232         DEFAULTS = helpers.FILTERDICT
00233         def nonstandard(f):
00234             expr = f['override']
00235             id = f['id']
00236             visible = bool(f.get('visible', False))
00237             return expr != '' or visible != DEFAULTS.get(id, False)
00238             
00239         cleaned = [ f for f in filters if nonstandard(f) ]
00240         self._setToolbarFilters(cleaned, globalfilter)
00241         if REQUEST:
00242             REQUEST.RESPONSE.redirect(self.absolute_url() + '/zmi_toolbar')
00243 
00244     security.declareProtected("View", "getGlobalButtonFilter")
00245     def getGlobalButtonFilter(self):
00246         gfilter = getattr(self, '_global_toolbar_filter', None)
00247         if gfilter is not None:
00248             return gfilter.text
00249         return ''
00250 
00251     security.declareProtected('View', "getHtmlExclusions")
00252     def getHtmlExclusions(self):
00253         try:
00254             excl = self.html_exclusions
00255         except AttributeError:
00256             excl = self.html_exclusions = _excluded_html
00257 
00258         res = []
00259         for (t,a) in excl:
00260             if t and t[0]=='':
00261                 t = []
00262             if a and a[0]=='':
00263                 a = []
00264             res.append((t,a))
00265         return res
00266 
00267     security.declareProtected('View', "getStyleWhitelist")
00268     def getStyleWhitelist(self):
00269         try:
00270             return self.style_whitelist
00271         except AttributeError:
00272             self.style_whitelist = _style_whitelist
00273             return self.style_whitelist
00274 
00275     security.declareProtected('View', "getClassBlacklist")
00276     def getClassBlacklist(self):
00277         return getattr(self, 'class_blacklist', [])
00278 
00279     security.declareProtected('View', "getDefaultResource")
00280     def getDefaultResource(self):
00281         return getattr(self, 'default_resource', 'linkable')
00282 
00283     security.declareProtected(permissions.ManageLibraries, "setDefaultResource")
00284     def setDefaultResource(self, resource_type):
00285         self.default_resource = resource_type
00286 
00287     security.declareProtected('View', "installBeforeUnload")
00288     def installBeforeUnload(self):
00289         return getattr(self, 'install_beforeunload', False)
00290 
00291     security.declareProtected('View', "getFiltersourceedit")
00292     def getFiltersourceedit(self):
00293         return getattr(self, 'filtersourceedit', True)
00294 
00295     security.declareProtected('View', "getAllowOriginalImageSize")
00296     def getAllowOriginalImageSize(self):
00297         return getattr(self, 'allowOriginalImageSize', False)
00298 
00299     security.declareProtected('View', 'isKupuEnabled')
00300     def isKupuEnabled(self, useragent='', allowAnonymous=False, REQUEST=None, context=None, fieldName=None):
00301         if not REQUEST:
00302             REQUEST = self.REQUEST
00303         def numerics(s):
00304             '''Convert a string into a tuple of all digit sequences
00305             '''
00306             seq = ['']
00307             for c in s:
00308                 if c.isdigit():
00309                     seq[-1] = seq[-1] + c
00310                 elif seq[-1]:
00311                     seq.append('')
00312             return tuple([ int(val) for val in seq if val])
00313 
00314         # First check whether the user actually wants kupu
00315         pm = getToolByName(self, 'portal_membership')
00316         if pm.isAnonymousUser() and not allowAnonymous:
00317             return False
00318 
00319         user = pm.getAuthenticatedMember()
00320         if not pm.isAnonymousUser():
00321             editor = user.getProperty('wysiwyg_editor')
00322             if editor and editor.lower() != 'kupu':
00323                 return False
00324 
00325         # Then check whether the current content allows html
00326         if context is not None and fieldName and hasattr(context, 'getWrappedField'):
00327             field = context.getWrappedField(fieldName)
00328             if field:
00329                 allowedTypes = getattr(field, 'allowable_content_types', None)
00330                 if allowedTypes is not None and not 'text/html' in [t.lower() for t in allowedTypes]:
00331                     return False
00332 
00333         # Then check whether their browser supports it.
00334         if not useragent:
00335             useragent = REQUEST['HTTP_USER_AGENT']
00336 
00337         if 'BEOS' in useragent:
00338             return False
00339 
00340         def getver(s):
00341             """Extract a version number given the string which precedes it"""
00342             pos = useragent.find(s)
00343             if pos >= 0:
00344                 tail = useragent[pos+len(s):].strip()
00345                 verno = numerics(tail.split(' ')[0])
00346                 return verno
00347             return None
00348             
00349         try:
00350             v = getver('Opera/')
00351             if not v:
00352                 v = getver('Opera ')
00353             if v:
00354                 return v >= (9,0)
00355 
00356             mozillaver = getver('Mozilla/')
00357             if mozillaver > (5,0):
00358                 return True
00359             elif mozillaver == (5,0):
00360                 verno = getver(' rv:')
00361                 if verno:
00362                     return verno >= (1,3,1)
00363                 verno = getver(' AppleWebKit/')
00364                 if verno:
00365                     return verno >= (525,1)
00366                     verno = getver(' Safari/')
00367                     if verno:
00368                         return verno >= (522,12)
00369 
00370             verno = getver('MSIE')
00371             if verno:
00372                 return verno >= (5,5)
00373         except:
00374             # In case some weird browser makes the test code blow up.
00375             pass
00376         return False
00377 
00378     security.declarePublic("getWysiwygmacros")
00379     def getWysiwygmacros(self):
00380         """Find the appropriate template to use for the kupu widget"""
00381         pm = getToolByName(self, 'portal_membership')
00382         user = pm.getAuthenticatedMember()
00383         editor = user.getProperty('wysiwyg_editor', '')
00384         if editor: editor = editor.lower()
00385         if editor=='fck editor':
00386             editor = 'editor_fck'
00387 
00388         portal = getToolByName(self, 'portal_url').getPortalObject()
00389         for path in ('%s_wysiwyg_support' % editor,
00390             '%s/wysiwyg_support' % editor,
00391             'portal_skins/plone_wysiwyg/wysiwyg_support'):
00392                 template = portal.restrictedTraverse(path, None)
00393                 if template:
00394                     break
00395 
00396         return template.macros
00397 
00398     security.declarePublic("forcekupu_url")
00399     def forcekupu_url(self, fieldName):
00400         args = {'kupu.convert':fieldName,
00401             'kupu.suppress':None,
00402             'portal_status_message':None
00403             }
00404         qs = self.query_string(args);
00405         return "%s?%s" % (self.REQUEST.URL0, qs)
00406 
00407     security.declarePublic("query_string")
00408     def query_string(self, replace={}, original=None):
00409         """ Updates 'original' dictionary by the values in the 'replace'
00410             dictionary and returns the result as url quoted query string.
00411 
00412             The 'original' dictionary defaults to 'REQUEST.form' if no
00413             parameter is passed to it. Keys in the 'replace' dictionary
00414             with a value of 'None' (or _.None in DTML) will be deleted
00415             from 'original' dictionary before being quoted.
00416 
00417             The original 'REQUEST.form' will remain unchanged.
00418         """
00419         # Based on code by Grégoire Weber
00420         if original is None:
00421             query = self.REQUEST.form.copy()
00422         else:
00423             query = original.copy()
00424 
00425         # delete key/value pairs if value is None
00426         for k,v in replace.items():
00427             if v is None:
00428                 if query.has_key(k):
00429                     del query[k]
00430                 del replace[k]
00431 
00432         # update dictionary
00433         query.update(replace)
00434         qs = '&'.join(["%s=%s" % (quote_plus(str(k)), quote_plus(str(v)))
00435             for k,v in query.items()])
00436 
00437         return qs
00438 
00439     security.declareProtected("View", "url_plus_query")
00440     def url_plus_query(self, url, query=None):
00441         """Adds query segment to an existing URL.
00442         Existing query parameters are may be overridden by query,
00443         otherwise they are preserved.
00444         """
00445         if query is None:
00446             query = {}
00447         parts = url.split('?', 1)
00448         oldargs = {}
00449         if len(parts) > 1:
00450             for arg in parts[1].split('&'):
00451                 k,v = [unquote_plus(s) for s in arg.split('=',1)]
00452                 oldargs[k] = v
00453 
00454         return "%s?%s" % (parts[0], self.query_string(query, oldargs))
00455 
00456     security.declareProtected('View', 'kupuUrl')
00457     def kupuUrl(self, url, query=None):
00458         """Generate a url which includes resource_type and instance"""
00459         request = self.REQUEST
00460         resource_type = request.get('resource_type', 'mediaobject')
00461         instance = request.get('instance', None)
00462         newquery = { 'instance':instance, 'resource_type':resource_type }
00463         if query is not None:
00464             newquery.update(query)
00465         return self.url_plus_query(url, newquery)
00466         
00467     security.declareProtected('View', "getCookedLibraries")
00468     def getCookedLibraries(self, context):
00469         """Return a list of libraries with our own parameters included.
00470         The library with id 'search' is excluded from this list."""
00471         libraries = [l for l in self.getLibraries(context) if not l['id'].startswith('_')]
00472         default_library = getattr(self, '_default_library', '')
00473 
00474         for l in libraries:
00475             l['src'] = self.kupuUrl(l['src'])
00476             l['selected'] = l['id']==default_library or None
00477         return libraries
00478 
00479     security.declareProtected('View', "getSingleLibrary")
00480     def getSingleLibrary(self, context, id):
00481         """Return the library with id=search or None"""
00482         libraries = [l for l in self.getLibraries(context) if l['id']==id]
00483 
00484         for l in libraries:
00485             l['src'] = self.kupuUrl(l['src'])
00486         if libraries:
00487             return libraries[0]
00488         return None
00489 
00490     # ZMI views
00491     manage_options = (SimpleItem.manage_options[1:] + (
00492          dict(label='Config', action='kupu_config'),
00493          dict(label='Libraries', action='zmi_libraries'),
00494          dict(label='Resource types', action='zmi_resource_types'),
00495          dict(label='Documentation', action='zmi_docs'),
00496          ))
00497 
00498 
00499     security.declarePublic('scanIds')
00500     def scanIds(self):
00501         """Finds the relevant source files and the doller/Id/dollar strings they contain"""
00502         return scanner.scanIds()
00503 
00504     security.declarePublic('scanKWS')
00505     def scanKWS(self):
00506         """Check that kupu_wysiwyg_support is up to date"""
00507         return scanner.scanKWS()
00508 
00509     security.declarePublic('docs')
00510     def docs(self):
00511         """Returns Kupu docs formatted as HTML"""
00512         docpath = os.path.join(Globals.package_home(kupu_globals), 'doc')
00513         f = open(os.path.join(docpath, 'PLONE2.txt'), 'r')
00514         _docs = f.read()
00515         return _docs
00516 
00517     security.declareProtected(permissions.ManageLibraries, "link_migration")
00518     def link_migration(self, action=None):
00519         """Do link checking or conversion, a little bit at a time"""
00520         if action is None:
00521             action = self.REQUEST.form.get('button', '')
00522 
00523         commit = self.REQUEST.form.get('commit', False)
00524         migrator = html2captioned.Migration(self)
00525         if action=='continue':
00526             migrator.restoreState()
00527             res = migrator.docontinue()
00528             return migrator.getInfo()
00529         elif action=='status':
00530             try:
00531                 migrator.restoreState()
00532             except KeyError:
00533                 return "state cleared"
00534             return migrator.status()
00535         elif action=='query':
00536             migrator.initFromRequest()
00537             return migrator.mkQuery()
00538         elif commit:
00539             migrator.initCommit()
00540             return migrator.getInfo()
00541         else:
00542             migrator.initFromRequest()
00543             return migrator.getInfo()
00544 
00545     security.declareProtected(permissions.ManageLibraries, "zmi_links")
00546     zmi_links = PageTemplateFile("zmi_links.pt", globals())
00547     zmi_links.title = 'kupu link maintenance'
00548 
00549     security.declareProtected(permissions.ManageLibraries, "zmi_toolbar")
00550     zmi_toolbar = PageTemplateFile("zmi_toolbar.pt", globals())
00551     zmi_toolbar.title = 'kupu toolbar customisation'
00552 
00553     security.declareProtected(permissions.ManageLibraries, "zmi_docs")
00554     zmi_docs = PageTemplateFile("zmi_docs.pt", globals())
00555     zmi_docs.title = 'kupu configuration documentation'
00556 
00557     security.declareProtected(permissions.ManageLibraries, "kupu_config")
00558     kupu_config = PageTemplateFile("kupu_config.pt", globals())
00559     kupu_config.title = 'kupu configuration'
00560 
00561     security.declareProtected(permissions.ManageLibraries, "zmi_libraries")
00562     zmi_libraries = PageTemplateFile("libraries.pt", globals())
00563     zmi_libraries.title = 'kupu configuration'
00564 
00565     security.declareProtected(permissions.ManageLibraries, "zmi_resource_types")
00566     zmi_resource_types = PageTemplateFile("resource_types.pt", globals())
00567     zmi_resource_types.title = 'kupu configuration'
00568 
00569     security.declareProtected(permissions.ManageLibraries,
00570                               "zmi_get_libraries")
00571     def zmi_get_libraries(self):
00572         """Return the libraries sequence for the ZMI view"""
00573         #return ()
00574         def text(value):
00575             return getattr(value, 'text', value)
00576         return [dict([(key, text(value)) for key, value in lib.items()])
00577                 for lib in self._libraries]
00578 
00579     security.declareProtected(permissions.ManageLibraries,
00580                               "zmi_add_library")
00581     def zmi_add_library(self, id, title, uri, src, icon, REQUEST):
00582         """Add a library through the ZMI"""
00583         self.addLibrary(id, title, uri, src, icon)
00584         REQUEST.RESPONSE.redirect(self.absolute_url() + '/zmi_libraries')
00585 
00586     security.declareProtected(permissions.ManageLibraries,
00587                               "zmi_update_libraries")
00588     def zmi_update_libraries(self, libraries, REQUEST):
00589         """Update libraries through the ZMI"""
00590         self.updateLibraries(libraries)
00591         REQUEST.RESPONSE.redirect(self.absolute_url() + '/zmi_libraries')
00592 
00593     security.declareProtected(permissions.ManageLibraries,
00594                               "zmi_delete_libraries")
00595     def zmi_delete_libraries(self, indices, REQUEST):
00596         """Delete libraries through the ZMI"""
00597         self.deleteLibraries(indices)
00598         REQUEST.RESPONSE.redirect(self.absolute_url() + '/zmi_libraries')
00599 
00600     security.declareProtected(permissions.ManageLibraries,
00601                               "zmi_move_up")
00602     def zmi_move_up(self, indices, REQUEST):
00603         """Move libraries up through the ZMI"""
00604         self.moveUp(indices)
00605         REQUEST.RESPONSE.redirect(self.absolute_url() + '/zmi_libraries')
00606 
00607     security.declareProtected(permissions.ManageLibraries,
00608                               "zmi_move_down")
00609     def zmi_move_down(self, indices, REQUEST):
00610         """Move libraries down through the ZMI"""
00611         self.moveDown(indices)
00612         REQUEST.RESPONSE.redirect(self.absolute_url() + '/zmi_libraries')
00613 
00614     security.declarePublic("zmi_get_default_library")
00615     def zmi_get_default_library(self):
00616         """Return the default selected library for the ZMI view"""
00617         return getattr(self, '_default_library', '')
00618 
00619     security.declareProtected(permissions.ManageLibraries,
00620                               "zmi_set_default_library")
00621     def zmi_set_default_library(self, defid=''):
00622         """Return the libraries sequence for the ZMI view"""
00623         self._default_library = defid
00624 
00625     security.declareProtected(permissions.ManageLibraries, "zmi_get_type_mapping")
00626     def zmi_get_type_mapping(self):
00627         """Return the type mapping for the ZMI view:
00628            Old version of code. Returns name,types pairs plus a dummy"""
00629         return [(t.name, t.types) for t in self.zmi_get_resourcetypes()] + [('',())]
00630 
00631     security.declareProtected(permissions.ManageLibraries, "export_resource_types")
00632     def export_resource_types(self):
00633         """Build a list of resource types formatted for export.
00634         'blacklist' type lists are inverted so the listed types are the ones we don't want.
00635         """
00636         types = self.get_resourcetypes()
00637         typetool = getToolByName(self, 'portal_types')
00638         portal_types = dict([ (t.id, 1) for t in typetool.listTypeInfo()])
00639         for t in types:
00640             if t.newtype:
00641                 t.types = self.invertTypeList(t.types)
00642                 t.mode = 'blacklist'
00643             else:
00644                 t.mode = 'whitelist'
00645         return types
00646 
00647     security.declareProtected("View", "get_resourcetypes")
00648     def get_resourcetypes(self):
00649         """Return the type mapping, but without the ZMI dummy entry"""
00650         keys = self._res_types.keys()
00651         keys.sort()
00652         real = []
00653         for name in keys:
00654             value = self._res_types[name]
00655             wrapped = Object(name=name, types=tuple(value), newtype=self.getNewTypeHandler(name))
00656             real.append(wrapped)
00657         return real
00658 
00659     security.declareProtected("View", "zmi_get_resourcetypes")
00660     def zmi_get_resourcetypes(self):
00661         """Return the type mapping for the ZMI view"""
00662         real = self.get_resourcetypes()
00663         real.append(Object(name='', types=()))
00664         return real
00665 
00666     security.declareProtected(permissions.ManageLibraries,
00667                               "zmi_update_resource_types")
00668     def zmi_update_resource_types(self, type_info=None, preview_action=None, default_resource=None, REQUEST=None):
00669         """Update resource types through the ZMI"""
00670 
00671         if type_info:
00672             self.updateResourceTypes(type_info)
00673 
00674         if preview_action:
00675             self.updatePreviewActions(preview_action)
00676 
00677         if default_resource is not None:
00678             self.default_resource = default_resource
00679 
00680         if REQUEST:
00681             REQUEST.RESPONSE.redirect(self.absolute_url() + '/zmi_resource_types')
00682 
00683     security.declareProtected(permissions.ManageLibraries,
00684                               "zmi_delete_resource_types")
00685     def zmi_delete_resource_types(self, resource_types=None, preview_types=None, REQUEST=None):
00686         """Delete resource types through the ZMI"""
00687         if resource_types:
00688             self.deleteResourceTypes(resource_types)
00689         if preview_types:
00690             self.deletePreviewActions(preview_types)
00691         if (REQUEST):
00692             REQUEST.RESPONSE.redirect(self.absolute_url() + '/zmi_resource_types')
00693 
00694     security.declareProtected("View", "getPreviewForType")
00695     def getPreviewForType(self, portal_type):
00696         action_map = getattr(self, '_preview_actions', {})
00697         expr = action_map.get(portal_type, {}).get('expression', '')
00698         return getattr(expr, 'text', expr)
00699 
00700     security.declareProtected("View", "getNormalViewForType")
00701     def getNormalViewForType(self, portal_type):
00702         action_map = getattr(self, '_preview_actions', {})
00703         expr = action_map.get(portal_type, {}).get('normal', '')
00704         return getattr(expr, 'text', expr)
00705 
00706     security.declareProtected("View", "getScaleFieldForType")
00707     def getScaleFieldForType(self, portal_type):
00708         action_map = getattr(self, '_preview_actions', {})
00709         value = action_map.get(portal_type, {}).get('scalefield', 'image')
00710         return value
00711 
00712     security.declareProtected("View", "getDefaultImageType")
00713     def getDefaultImageType(self):
00714         return 'Image'
00715 
00716     security.declareProtected("View", "getDefaultScaleForType")
00717     def getDefaultScaleForType(self, portal_type = None):
00718         if not portal_type:
00719             portal_type = self.getDefaultImageType()
00720         action_map = getattr(self, '_preview_actions', {})
00721         value = action_map.get(portal_type, {}).get('defscale', 'image_preview')
00722         return value
00723 
00724     security.declareProtected("View", "getClassesForType")
00725     def getClassesForType(self, portal_type):
00726         action_map = getattr(self, '_preview_actions', {})
00727         return action_map.get(portal_type, {}).get('classes', ())
00728 
00729     security.declareProtected("View", "getMediaForType")
00730     def getMediaForType(self, portal_type):
00731         action_map = getattr(self, '_preview_actions', {})
00732         value = action_map.get(portal_type, {}).get('mediatype', 'image')
00733         return value
00734 
00735     security.declareProtected(permissions.ManageLibraries, "set_html_exclusions")
00736     def set_html_exclusions(self, exclusions):
00737         """Set the html_exclusions.
00738         Expects a list/tuple of 2-tuples [(tags,attrs),...]
00739         """
00740         excl = []
00741         for (tags,attrs) in exclusions:
00742             if len(tags)==1 and tags[0]=="":
00743                 tags = []
00744             if len(attrs)==1 and attrs[0]=="":
00745                 attrs = []
00746             excl.append((tags, attrs))
00747         self.html_exclusions = excl
00748 
00749     security.declareProtected("View", "get_stripped_tags")
00750     def get_stripped_tags(self):
00751         """Returns a list of tags to be stripped"""
00752         stripped = []
00753         for (tags, attrs) in self.getHtmlExclusions():
00754             if not attrs:
00755                 stripped.extend(tags)
00756         return stripped
00757 
00758     security.declareProtected(permissions.ManageLibraries, "set_stripped_tags")
00759     def set_stripped_tags(self, stripped):
00760         """Sets a list of tags to be stripped"""
00761         exclusions = [(tags, attrs) for (tags, attrs) in self.getHtmlExclusions() if attrs]
00762         exclusions.append((tuple(stripped), ()))
00763         self.set_html_exclusions(exclusions)
00764 
00765     security.declareProtected('View', "get_stripped_attributes")
00766     def get_stripped_attributes(self):
00767         """Returns a list of attributes to be stripped"""
00768         stripped = []
00769         for (tags, attrs) in self.getHtmlExclusions():
00770             if not tags:
00771                 stripped.extend(attrs)
00772         return stripped
00773         
00774     security.declareProtected(permissions.ManageLibraries, "set_stripped_attributes")
00775     def set_stripped_attributes(self, stripped):
00776         """Sets a list of attributes to be stripped"""
00777         exclusions = [(tags, attrs) for (tags, attrs) in self.html_exclusions if tags]
00778         exclusions.append(((), tuple(stripped)))
00779         self.set_html_exclusions(exclusions)
00780 
00781     security.declareProtected('View', "get_stripped_combinations")
00782     def get_stripped_combinations(self):
00783         """Returns a list of tag/attribute combinations to be stripped"""
00784         stripped = [(tags, attrs) for (tags, attrs) in self.getHtmlExclusions() if tags and attrs]
00785         return stripped
00786 
00787     security.declareProtected(permissions.ManageLibraries, "set_stripped_combinations")
00788     def set_stripped_combinations(self, stripped):
00789         """Sets a list of tag/attribute pairs to be stripped"""
00790         exclusions = [(tags, attrs) for (tags, attrs) in self.getHtmlExclusions() if not (tags and attrs)]
00791         self.set_html_exclusions(stripped + exclusions)
00792 
00793 
00794     security.declareProtected('View', "have_plone_filtering")
00795     def have_plone_filtering(self):
00796         return HAVE_PLONE_FILTERING
00797 
00798     security.declareProtected(permissions.ManageLibraries,
00799                               "configure_kupu")
00800     def configure_kupu(self,
00801         linkbyuid=None,
00802         table_classnames=None,
00803         html_exclusions=None,
00804         style_whitelist=None,
00805         class_blacklist=None,
00806         installBeforeUnload=None, parastyles=None, refbrowser=None,
00807         captioning=None,
00808         filterSourceEdit=None,
00809         allowOriginalImageSize=None,
00810         REQUEST=None):
00811         """Delete resource types through the ZMI"""
00812         if linkbyuid is not None:
00813             self.linkbyuid = bool(linkbyuid)
00814         if table_classnames is not None:
00815             self.table_classnames = [t for t in table_classnames if t]
00816         if installBeforeUnload is not None:
00817             self.install_beforeunload = bool(installBeforeUnload)
00818         if filterSourceEdit is not None:
00819             self.filtersourceedit = bool(filterSourceEdit)
00820         if allowOriginalImageSize is not None:
00821             self.allowOriginalImageSize = bool(allowOriginalImageSize)
00822 
00823         if parastyles is not None:
00824             self.paragraph_styles = [ line.strip() for line in parastyles if line.strip() ]
00825 
00826         if html_exclusions is not None:
00827             newex = html_exclusions[-1]
00828             html_exclusions = html_exclusions[:-1]
00829             
00830             html_exclusions = [ (tuple(h.get('tags', ())), tuple(h.get('attributes', ())))
00831                 for h in html_exclusions if h.get('keep')]
00832 
00833             tags = newex.get('tags', '').replace(',',' ').split()
00834             attr = newex.get('attributes', '').replace(',',' ').split()
00835             if tags or attr:
00836                 html_exclusions.append((tuple(tags), tuple(attr)))
00837             self.set_html_exclusions(html_exclusions)
00838 
00839         if style_whitelist is not None:
00840             self.style_whitelist = list(style_whitelist)
00841         if class_blacklist is not None:
00842             self.class_blacklist = list(class_blacklist)
00843 
00844         if refbrowser is not None:
00845             out = StringIO()
00846             if refbrowser:
00847                 self.ensureReferencesLayer(True);
00848             else:
00849                 util.unregister_layers(self, ['kupu_references'], out)
00850             # Force compressed javascript to be recomputed.
00851             try:
00852                 self.portal_javascripts.cookResources()
00853             except AttributeError:
00854                 pass
00855 
00856         if captioning is not None:
00857             self.captioning = bool(captioning)
00858 
00859         if self.linkbyuid or self.getCaptioning():
00860             util.install_transform(self)
00861         else:
00862             util.remove_transform(self)
00863 
00864         if REQUEST:
00865             REQUEST.RESPONSE.redirect(self.absolute_url() + '/kupu_config')
00866         
00867 InitializeClass(PloneKupuLibraryTool)