diff ZTUtils/Zope.py @ 983:7fe79c67aaa9

Adding ZTUtils to the dist
author Richard Jones <richard@users.sourceforge.net>
date Fri, 30 Aug 2002 08:25:34 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ZTUtils/Zope.py	Fri Aug 30 08:25:34 2002 +0000
@@ -0,0 +1,302 @@
+##############################################################################
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+# 
+##############################################################################
+__doc__='''Zope-specific versions of ZTUTils classes
+
+$Id: Zope.py,v 1.1 2002-08-30 08:25:34 richard Exp $'''
+__version__='$Revision: 1.1 $'[11:-2]
+
+import sys, cgi, urllib, cgi
+from Tree import encodeExpansion, decodeExpansion, TreeMaker
+from SimpleTree import SimpleTreeMaker
+from Batch import Batch
+from Products.ZCatalog.Lazy import Lazy
+from AccessControl import getSecurityManager
+from string import split, join
+from types import StringType, ListType, IntType, FloatType
+from DateTime import DateTime
+
+try:
+    from AccessControl.ZopeGuards import guarded_getitem
+except ImportError:
+    Unauthorized = 'Unauthorized'
+    def guarded_getitem(object, index):
+        v = object[index]
+        if getSecurityManager().validate(object, object, index, v):
+            return v
+        raise Unauthorized, 'unauthorized access to element %s' % `i`
+else:
+    from AccessControl import Unauthorized
+
+class LazyFilter(Lazy):
+    # A LazyFilter that checks with the security policy
+
+    def __init__(self, seq, test=None, skip=None):
+        self._seq=seq
+        self._data=[]
+        self._eindex=-1
+        self._test=test
+        if not (skip is None or str(skip) == skip): 
+            raise TypeError, 'Skip must be None or a string'
+        self._skip = skip 
+
+    def __getitem__(self,index):
+        data=self._data
+        try: s=self._seq
+        except AttributeError: return data[index]
+
+        i=index
+        if i < 0: i=len(self)+i
+        if i < 0: raise IndexError, index
+
+        ind=len(data)
+        if i < ind: return data[i]
+        ind=ind-1
+
+        test=self._test
+        e=self._eindex
+        skip = self._skip
+        while i > ind:
+            e = e + 1
+            try:
+                try: v = guarded_getitem(s, e)
+                except Unauthorized, vv:
+                    if skip is None:
+                        self._eindex = e
+                        msg = '(item %s): %s' % (index, vv)
+                        raise Unauthorized, msg, sys.exc_info()[2]
+                    skip_this = 1
+                else:
+                    skip_this = 0
+            except IndexError:
+                del self._test
+                del self._seq
+                del self._eindex
+                raise IndexError, index
+            if skip_this: continue
+            if skip and not getSecurityManager().checkPermission(skip, v):
+                continue
+            if test is None or test(v):
+                data.append(v)
+                ind=ind+1
+        self._eindex=e
+        return data[i]
+
+class TreeSkipMixin:
+    '''Mixin class to make trees test security, and allow
+    skipping of unauthorized objects. '''
+    skip = None
+    def setSkip(self, skip):
+        self.skip = skip
+        return self
+    def getChildren(self, object):
+        return LazyFilter(self._getChildren(object), skip=self.skip)
+
+class TreeMaker(TreeSkipMixin, TreeMaker):
+    _getChildren = TreeMaker.getChildren
+
+class SimpleTreeMaker(TreeSkipMixin, SimpleTreeMaker):
+    _getChildren = SimpleTreeMaker.getChildren
+    def cookieTree(self, root_object, default_state=None):
+        '''Make a tree with state stored in a cookie.'''
+        tree_pre = self.tree_pre
+        state_name = '%s-state' % tree_pre
+        set_name = '%s-setstate' % tree_pre
+
+        req = root_object.REQUEST
+        state = req.get(state_name)
+        if state:
+            setst = req.form.get(set_name)
+            if setst:
+                st, pn, expid = split(setst, ',')
+                state, (m, obid) = decodeExpansion(state, int(pn))
+                if m is None:
+                    pass
+                elif st == 'e':
+                    if m[obid] is None:
+                        m[obid] = {expid: None}
+                    else:
+                        m[obid][expid] = None
+                elif st == 'c' and m is not state and obid==expid:
+                    del m[obid]
+            else:
+                state = decodeExpansion(state)
+        else:
+            state = default_state
+        tree = self.tree(root_object, state)
+        rows = tree.flat()
+        req.RESPONSE.setCookie(state_name, encodeExpansion(rows))
+        return tree, rows
+
+# Make the Batch class test security, and let it skip unauthorized.
+_Batch = Batch
+class Batch(Batch):
+    def __init__(self, sequence, size, start=0, end=0,
+                 orphan=0, overlap=0, skip_unauthorized=None):
+        sequence = LazyFilter(sequence, skip=skip_unauthorized)
+        _Batch.__init__(self, sequence, size, start, end,
+                        orphan, overlap)
+
+# These functions are meant to be used together in templates that use
+# trees or batches.  For example, given a batch with a 'bstart' query
+# argument, you would use "url_query(request, omit='bstart')" to get
+# the base for the batching links, then append 
+# "make_query(bstart=batch.previous.first)" to one and
+# "make_query(bstart=batch.end)" to the other.
+
+def make_query(*args, **kwargs):
+    '''Construct a URL query string, with marshalling markup.
+
+    If there are positional arguments, they must be dictionaries.
+    They are combined with the dictionary of keyword arguments to form
+    a dictionary of query names and values.
+
+    Query names (the keys) must be strings.  Values may be strings,
+    integers, floats, or DateTimes, and they may also be lists or
+    namespaces containing these types.  Names and string values
+    should not be URL-quoted.  All arguments are marshalled with
+    complex_marshal().
+    '''
+
+    d = {}
+    for arg in args:
+        d.update(arg)
+    d.update(kwargs)
+
+    uq = urllib.quote
+    qlist = complex_marshal(d.items())
+    for i in range(len(qlist)):
+        k, m, v = qlist[i]
+        qlist[i] = '%s%s=%s' % (uq(k), m, uq(str(v)))
+
+    return join(qlist, '&')
+                
+def make_hidden_input(*args, **kwargs):
+    '''Construct a set of hidden input elements, with marshalling markup.
+
+    If there are positional arguments, they must be dictionaries.
+    They are combined with the dictionary of keyword arguments to form
+    a dictionary of query names and values.
+
+    Query names (the keys) must be strings.  Values may be strings,
+    integers, floats, or DateTimes, and they may also be lists or
+    namespaces containing these types.  All arguments are marshalled with
+    complex_marshal().
+    '''
+
+    d = {}
+    for arg in args:
+        d.update(arg)
+    d.update(kwargs)
+
+    hq = cgi.escape
+    qlist = complex_marshal(d.items())
+    for i in range(len(qlist)):
+        k, m, v = qlist[i]
+        qlist[i] = ('<input type="hidden" name="%s%s" value="%s">'
+                    % (hq(k), m, hq(str(v))))
+
+    return join(qlist, '\n')
+                
+def complex_marshal(pairs):
+    '''Add request marshalling information to a list of name-value pairs.
+
+    Names must be strings.  Values may be strings,
+    integers, floats, or DateTimes, and they may also be lists or
+    namespaces containing these types.
+
+    The list is edited in place so that each (name, value) pair
+    becomes a (name, marshal, value) triple.  The middle value is the
+    request marshalling string.  Integer, float, and DateTime values
+    will have ":int", ":float", or ":date" as their marshal string.
+    Lists will be flattened, and the elements given ":list" in
+    addition to their simple marshal string.  Dictionaries will be
+    flattened and marshalled using ":record".
+    '''
+    i = len(pairs)
+    while i > 0:
+        i = i - 1
+        k, v = pairs[i]
+        m = ''
+        sublist = None
+        if isinstance(v, StringType):
+            pass
+        elif hasattr(v, 'items'):
+            sublist = []
+            for sk, sv in v.items():
+                sm = simple_marshal(sv)
+                sublist.append(('%s.%s' % (k, sk), '%s:record' % sm,  sv))
+        elif isinstance(v, ListType):
+            sublist = []
+            for sv in v:
+                sm = simple_marshal(sv)
+                sublist.append((k, '%s:list' % sm, sv))
+        else:
+            m = simple_marshal(v)
+        if sublist is None:
+            pairs[i] = (k, m, v)
+        else:
+            pairs[i:i + 1] = sublist
+
+    return pairs
+
+def simple_marshal(v):
+    if isinstance(v, StringType):
+        return ''
+    if isinstance(v, IntType):
+        return ':int'
+    if isinstance(v, FloatType):
+        return ':float'
+    if isinstance(v, DateTime):
+        return ':date'
+    return ''
+
+def url_query(request, req_name="URL", omit=None):
+    '''Construct a URL with a query string, using the current request.
+
+    request: the request object
+    req_name: the name, such as "URL1" or "BASEPATH1", to get from request
+    omit: sequence of name of query arguments to omit.  If a name
+    contains a colon, it is treated literally.  Otherwise, it will
+    match each argument name that starts with the name and a period or colon. 
+    '''
+
+    base = request[req_name]
+    qs = request.get('QUERY_STRING', '')
+    
+    if qs and omit:
+        qsparts = split(qs, '&')
+
+        if isinstance(omit, StringType):
+            omits = {omit: None}
+        else:
+            omits = {}
+            for name in omit:
+                omits[name] = None
+        omitted = omits.has_key
+
+        unq = urllib.unquote
+        for i in range(len(qsparts)):
+            name = unq(split(qsparts[i], '=', 1)[0])
+            if omitted(name):
+                qsparts[i] = ''
+            name = split(name, ':', 1)[0]
+            if omitted(name):
+                qsparts[i] = ''
+            name = split(name, '.', 1)[0]
+            if omitted(name):
+                qsparts[i] = ''
+            
+        qs = join(filter(None, qsparts), '&')
+
+    # We alway append '?' since arguments will be appended to the URL
+    return '%s?%s' % (base, qs)

Roundup Issue Tracker: http://roundup-tracker.org/