PropertySheets.py :  » Web-Frameworks » Zope » Zope-2.6.0 » lib » python » OFS » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Frameworks » Zope 
Zope » Zope 2.6.0 » lib » python » OFS » PropertySheets.py
##############################################################################
#
# 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
#
##############################################################################

"""Property sheets"""

__version__='$Revision: 1.87.6.1 $'[11:-2]

import time,  App.Management, Globals
from webdav.WriteLockInterface import WriteLockInterface
from ZPublisher.Converters import type_converters
from Globals import DTMLFile,MessageDialog
from Acquisition import Implicit,Explicit
from App.Common import rfc1123_date,iso8601_date
from webdav.common import urlbase
from ExtensionClass import Base
from Globals import Persistent
from Traversable import Traversable
from Acquisition import aq_base
from AccessControl import getSecurityManager
from webdav.common import isDavCollection
from cgi import escape

class View(App.Management.Tabs, Base):
    """A view of an object, typically used for management purposes

    This class provides bits of management framework needed by propertysheets
    to be used as a view on an object.
    """

    def manage_workspace(self, URL1, RESPONSE):
        '''Implement a "management" interface
        '''
        RESPONSE.redirect(URL1+'/manage')

    def tpURL(self): return self.getId()

    def manage_options(self):
        """Return a manage option data structure for me instance
        """
        try: r=self.REQUEST
        except: r=None
        if r is None:
            pre='../../'
        else:
            pre=r['URL']
            for i in (1,2,3):
                l=pre.rfind('/')
                if l >= 0:
                    pre=pre[:l]
            pre=pre+'/'

        r=[]
        for d in self.aq_parent.aq_parent.manage_options:
            path=d['action']
            option={'label': d['label'],
                      'action': pre+path,
                      'path': '../../'+path}
            help=d.get('help')
            if help is not None:
                option['help']=help
            r.append(option)
        return r

    def tabs_path_info(self, script, path):
        l=path.rfind('/')
        if l >= 0:
            path=path[:l]
            l=path.rfind('/')
            if l >= 0: path=path[:l]
        return View.inheritedAttribute('tabs_path_info')(
            self, script, path)

    def meta_type(self):
        try: return self.aq_parent.aq_parent.meta_type
        except: return ''


class PropertySheet(Traversable, Persistent, Implicit):
    """A PropertySheet is a container for a set of related properties and
       metadata describing those properties. PropertySheets may or may not
       provide a web interface for managing its properties."""

    _properties=()
    _extensible=1
    icon='p_/Properties_icon'

    __ac_permissions__=(
        ('Manage properties', ('manage_addProperty',
                               'manage_editProperties',
                               'manage_delProperties',
                               'manage_changeProperties',
                               'manage_propertiesForm',
                               )),
        ('Access contents information',
         ('xml_namespace', 'hasProperty', 'getProperty', 'getPropertyType',
          'propertyIds', 'propertyValues','propertyItems', 'propertyInfo',
          'propertyMap', ''),
         ('Anonymous', 'Manager'),
         ),
        )

    __reserved_ids= ('values','items')

    def property_extensible_schema__(self):
        """Return a flag indicating whether new properties may be
        added or removed."""
        return self._extensible

    def __init__(self, id, md=None):
        # Create a new property set, using the given id and namespace
        # string. The namespace string should be usable as an xml name-
        # space identifier.

        if id in self.__reserved_ids:
            raise ValueError(
                  "'%s' is a reserved Id (forbidden Ids are: %s) " % (
                  id, self.__reserved_ids
                  ))

        self.id=id
        self._md=md or {}

    def getId(self):
        return self.id

    def xml_namespace(self):
        # Return a namespace string usable as an xml namespace
        # for this property set.
        return self._md.get('xmlns', '')

    def v_self(self):
        return self

    def p_self(self):
        return self.v_self()

    def valid_property_id(self, id):
        if not id or id[:1]=='_' or (id[:3]=='aq_') \
           or (' ' in id) or escape(id) != id:
            return 0
        return 1

    def hasProperty(self, id):
        # Return a true value if a property exists with the given id.
        for prop in self._propertyMap():
            if id==prop['id']:
                return 1
        return 0

    def getProperty(self, id, default=None):
        # Return the property with the given id, returning the optional
        # second argument or None if no such property is found.
        if self.hasProperty(id):
            return getattr(self.v_self(), id)
        return default

    def getPropertyType(self, id):
        """Get the type of property 'id', returning None if no
           such property exists"""
        pself=self.p_self()
        for md in pself._properties:
            if md['id']==id:
                return md.get('type', 'string')
        return None

    def _wrapperCheck(self, object):
        # Raise an error if an object is wrapped.
        if hasattr(object, 'aq_base'):
            raise ValueError, 'Invalid property value: wrapped object'
        return

    def _setProperty(self, id, value, type='string', meta=None):
        # Set a new property with the given id, value and optional type.
        # Note that different property sets may support different typing
        # systems.
        self._wrapperCheck(value)
        if not self.valid_property_id(id):
            raise 'Bad Request', 'Invalid property id, %s.' % escape(id)

        if not self.property_extensible_schema__():
            raise 'Bad Request', (
                'Properties cannot be added to this property sheet')
        pself=self.p_self()
        self=self.v_self()
        if hasattr(aq_base(self),id):
            if not (id=='title' and not self.__dict__.has_key(id)):
                raise 'Bad Request', (
                    'Invalid property id, <em>%s</em>. It is in use.' %
                        escape(id))
        if meta is None: meta={}
        prop={'id':id, 'type':type, 'meta':meta}
        pself._properties=pself._properties+(prop,)
        if type in ('selection', 'multiple selection'):
            if not value:
                raise 'Bad Request', (
                    'The value given for a new selection property '
                    'must be a variable name<p>')
            prop['select_variable']=value
            if type=='selection': value=None
            else: value=[]
        setattr(self, id, value)

    def _updateProperty(self, id, value, meta=None):
        # Update the value of an existing property. If value is a string,
        # an attempt will be made to convert the value to the type of the
        # existing property. If a mapping containing meta-data is passed,
        # it will used to _replace_ the properties meta data.
        self._wrapperCheck(value)
        if not self.hasProperty(id):
            raise 'Bad Request', 'The property %s does not exist.' % escape(id)
        propinfo=self.propertyInfo(id)
        if not 'w' in propinfo.get('mode', 'wd'):
            raise 'Bad Request', '%s cannot be changed.' % escape(id)
        if type(value)==type(''):
            proptype=propinfo.get('type', 'string')
            if type_converters.has_key(proptype):
                value=type_converters[proptype](value)
        if meta is not None:
            props=[]
            pself=self.p_self()
            for prop in pself._properties:
                if prop['id']==id: prop['meta']=meta
                props.append(prop)
            pself._properties=tuple(props)
        setattr(self.v_self(), id, value)

    def _delProperty(self, id):
        # Delete the property with the given id. If a property with the
        # given id does not exist, a ValueError is raised.
        if not self.hasProperty(id):
            raise 'Bad Request', 'The property %s does not exist.' % escape(id)
        vself=self.v_self()
        if hasattr(vself, '_reserved_names'):
            nd=vself._reserved_names
        else: nd=()
        if (not 'd' in self.propertyInfo(id).get('mode', 'wd')) or (id in nd):
            raise 'Bad Request', '%s cannot be deleted.' % escape(id)
        delattr(vself, id)
        pself=self.p_self()
        pself._properties=tuple(filter(lambda i, n=id: i['id'] != n,
                                       pself._properties))

    def propertyIds(self):
        # Return a list of property ids.
        return map(lambda i: i['id'], self._propertyMap())

    def propertyValues(self):
        # Return a list of property values.
        return map(lambda i, s=self: s.getProperty(i['id']),
                   self._propertyMap())

    def propertyItems(self):
        # Return a list of (id, property) tuples.
        return map(lambda i, s=self: (i['id'], s.getProperty(i['id'])),
                   self._propertyMap())

    def propertyInfo(self, id):
        # Return a mapping containing property meta-data
        for p in self._propertyMap():
            if p['id']==id: return p
        raise ValueError, 'The property %s does not exist.' % escape(id)

    def _propertyMap(self):
        # Return a tuple of mappings, giving meta-data for properties.
        # Some ZClass instances dont seem to have an _properties, so
        # we have to fake it...
        return self.p_self()._properties

    def propertyMap(self):
        # Returns a secure copy of the property definitions.
        return tuple(map(lambda dict: dict.copy(), self._propertyMap()))

    def _propdict(self):
        dict={}
        for p in self._propertyMap():
            dict[p['id']]=p
        return dict

    propstat='<d:propstat xmlns:n="%s">\n' \
             '  <d:prop>\n' \
             '%s\n' \
             '  </d:prop>\n' \
             '  <d:status>HTTP/1.1 %s</d:status>\n%s' \
             '</d:propstat>\n'

    propdesc='  <d:responsedescription>\n' \
             '  %s\n' \
             '  </d:responsedescription>\n'

    def dav__allprop(self, propstat=propstat ):
        # DAV helper method - return one or more propstat elements
        # indicating property names and values for all properties.
        result=[]
        for item in self._propertyMap():
            name, type=item['id'], item.get('type','string')
            value=self.getProperty(name)

            if type=='tokens':
                value=' '.join(str(value))
            elif type=='lines':
                value='\n'.join(str(value))
            # check for xml property
            attrs=item.get('meta', {}).get('__xml_attrs__', None)
            if attrs is not None:
                attrs=map(lambda n: ' %s="%s"' % n, attrs.items())
                attrs=''.join(attrs)
            else:
                # Quote non-xml items here?
                attrs=''

            if hasattr(self,"dav__"+name):
                prop='  <n:%s%s>%s</n:%s>' % (name, attrs, value, name)
            else:
                prop='  <n:%s%s>%s</n:%s>' % (name, attrs, xml_escape(value), name)

            result.append(prop)
        if not result: return ''
        result='\n'.join(result)

        return propstat % (self.xml_namespace(), result, '200 OK', '')

    def dav__propnames(self, propstat=propstat):
        # DAV helper method - return a propstat element indicating
        # property names for all properties in this PropertySheet.
        result=[]
        for name in self.propertyIds():
            result.append('  <n:%s/>' % name)
        if not result: return ''
        result='\n'.join(result)
        return propstat % (self.xml_namespace(), result, '200 OK', '')


    def dav__propstat(self, name, result,
                      propstat=propstat, propdesc=propdesc):
        # DAV helper method - return a propstat element indicating
        # property name and value for the requested property.
        xml_id=self.xml_namespace()
        propdict=self._propdict()
        if not propdict.has_key(name):
            prop='<n:%s xmlns:n="%s"/>\n' % (name, xml_id)
            code='404 Not Found'
            if not result.has_key(code):
                result[code]=[prop]
            else: result[code].append(prop)
            return
        else:
            item=propdict[name]
            name, type=item['id'], item.get('type','string')
            value=self.getProperty(name)
            if type=='tokens':
                value=' '.join(str(value))
            elif type=='lines':
                value='\n'.join(str(value))
            # allow for xml properties
            attrs=item.get('meta', {}).get('__xml_attrs__', None)
            if attrs is not None:
                attrs=map(lambda n: ' %s="%s"' % n, attrs.items())
                attrs=''.join(attrs)
            else:
                # quote non-xml items here?
                attrs=''
            prop='<n:%s%s xmlns:n="%s">%s</n:%s>\n' % (
                 name, attrs, xml_id, value, name)
            code='200 OK'
            if not result.has_key(code):
                result[code]=[prop]
            else: result[code].append(prop)
            return


    del propstat
    del propdesc


    # Web interface

    manage=DTMLFile('dtml/properties', globals())
    def manage_propertiesForm(self, URL1):
        " "
        raise 'Redirect', URL1+'/manage'

    def manage_addProperty(self, id, value, type, REQUEST=None):
        """Add a new property via the web. Sets a new property with
        the given id, type, and value."""
        if type_converters.has_key(type):
            value=type_converters[type](value)
        self._setProperty(id, value, type)
        if REQUEST is not None:
            return self.manage(self, REQUEST)

    def manage_editProperties(self, REQUEST):
        """Edit object properties via the web."""
        for prop in self._propertyMap():
            name=prop['id']
            if 'w' in prop.get('mode', 'wd'):
                value=REQUEST.get(name, '')
                self._updateProperty(name, value)
        return MessageDialog(
               title  ='Success!',
               message='Your changes have been saved',
               action ='manage')

    def manage_changeProperties(self, REQUEST=None, **kw):
        """Change existing object properties by passing either a mapping
           object of name:value pairs {'foo':6} or passing name=value
           parameters."""
        if REQUEST is None:
            props={}
        else: props=REQUEST
        if kw:
            for name, value in kw.items():
                props[name]=value
        propdict=self._propdict()
        for name, value in props.items():
            if self.hasProperty(name):
                if not 'w' in propdict[name].get('mode', 'wd'):
                    raise 'BadRequest', '%s cannot be changed' % escape(name)
                self._updateProperty(name, value)
        if REQUEST is not None:
            return MessageDialog(
                title  ='Success!',
                message='Your changes have been saved.',
                action ='manage')

    def manage_delProperties(self, ids=None, REQUEST=None):
        """Delete one or more properties specified by 'ids'."""
        if REQUEST:
            # Bugfix for properties named "ids" (casey)
            if ids == self.getProperty('ids', None): ids = None
            ids = REQUEST.get('_ids', ids)
        if ids is None:
            return MessageDialog(
                   title='No property specified',
                   message='No properties were specified!',
                   action ='./manage',)
        for id in ids:
            self._delProperty(id)
        if REQUEST is not None:
            return self.manage(self, REQUEST)

Globals.default__class_init__(PropertySheet)


class Virtual:
    """A virtual propertysheet stores it's properties in it's instance."""

    def __init__(self):
        pass

    def v_self(self):
        return self.aq_parent.aq_parent

class DefaultProperties(Virtual, PropertySheet, View):
    """The default property set mimics the behavior of old-style Zope
       properties -- it stores its property values in the instance of
       its owner."""

    id='default'
    _md={'xmlns': 'http://www.zope.org/propsets/default'}

Globals.default__class_init__(DefaultProperties)


class DAVProperties(Virtual, PropertySheet, View):
    """WebDAV properties"""

    id='webdav'
    _md={'xmlns': 'DAV:'}
    pm=({'id':'creationdate',     'mode':'r'},
        {'id':'displayname',      'mode':'r'},
        {'id':'resourcetype',     'mode':'r'},
        {'id':'getcontenttype',   'mode':'r'},
        {'id':'getcontentlength', 'mode':'r'},
        {'id':'source',           'mode':'r'},
        {'id':'supportedlock',    'mode':'r'},
        {'id':'lockdiscovery',    'mode':'r'},
        )

    def getProperty(self, id, default=None):
        method='dav__%s' % id
        if not hasattr(self, method):
            return default
        return getattr(self, method)()

    def _setProperty(self, id, value, type='string', meta=None):
        raise ValueError, '%s cannot be set.' % escape(id)

    def _updateProperty(self, id, value):
        raise ValueError, '%s cannot be updated.' % escape(id)

    def _delProperty(self, id):
        raise ValueError, '%s cannot be deleted.' % escape(id)

    def _propertyMap(self):
        # Only use getlastmodified if returns a value
        if hasattr(self.v_self(), '_p_mtime'):
            return self.pm + ({'id':'getlastmodified',  'mode':'r'},)
        return self.pm

    def propertyMap(self):
        return map(lambda dict: dict.copy(), self._propertyMap())

    def dav__creationdate(self):
        return iso8601_date(43200.0)

    def dav__displayname(self):
        return absattr(self.v_self().id)

    def dav__resourcetype(self):
        vself=self.v_self()
        if (isDavCollection(vself) or
            getattr(aq_base(vself), 'isAnObjectManager', None)):
            return '<n:collection/>'
        return ''

    def dav__getlastmodified(self):
        return rfc1123_date(self.v_self()._p_mtime)

    def dav__getcontenttype(self):
        vself=self.v_self()
        if hasattr(vself, 'content_type'):
            return absattr(vself.content_type)
        if hasattr(vself, 'default_content_type'):
            return absattr(vself.default_content_type)
        return ''

    def dav__getcontentlength(self):
        vself=self.v_self()
        if hasattr(vself, 'get_size'):
            return vself.get_size()
        return ''

    def dav__source(self):
        vself=self.v_self()
        if hasattr(vself, 'document_src'):
            url=urlbase(vself.absolute_url())
            return '\n  <n:link>\n' \
                   '  <n:src>%s</n:src>\n' \
                   '  <n:dst>%s/document_src</n:dst>\n' \
                   '  </n:link>\n  ' % (url, url)
        return ''

    def dav__supportedlock(self):
        return '\n  <n:lockentry>\n' \
               '  <d:lockscope><d:exclusive/></d:lockscope>\n' \
               '  <d:locktype><d:write/></d:locktype>\n' \
               '  </n:lockentry>\n  '

    def dav__lockdiscovery(self):
        security = getSecurityManager()
        user = security.getUser().getId()


        vself = self.v_self()
        out = '\n'
        if WriteLockInterface.isImplementedBy(vself):
            locks = vself.wl_lockValues(killinvalids=1)
            for lock in locks:

                creator = lock.getCreator()[-1]
                if creator == user: fake=0
                else:               fake=1

                out = '%s\n%s' % (out, lock.asLockDiscoveryProperty('n',fake=fake))

            out = '%s\n' % out

        return out


Globals.default__class_init__(DAVProperties)


class PropertySheets(Traversable, Implicit, App.Management.Tabs):
    """A tricky container to keep property sets from polluting
       an object's direct attribute namespace."""

    id='propertysheets'

    __ac_permissions__=(
        ('Manage properties', ('manage_addPropertySheet',
                               'addPropertySheet',
                               'delPropertySheet'
                               )),
        ('Access contents information',
         ('items', 'values', 'get', ''),
         ('Anonymous', 'Manager'),
         ),
        ('View management screens', ('manage',)),
        )


    webdav =DAVProperties()
    def _get_defaults(self):
        return (self.webdav,)

    def __propsets__(self):
        propsets=self.aq_parent.__propsets__
        __traceback_info__= propsets, type(propsets)
        return self._get_defaults() + propsets

    def __bobo_traverse__(self, REQUEST, name=None):
        for propset in self.__propsets__():
            if propset.getId()==name:
                return propset.__of__(self)
        return getattr(self, name)

    def __getitem__(self, n):
        return self.__propsets__()[n].__of__(self)

    def values(self):
        propsets=self.__propsets__()
        return map(lambda n, s=self: n.__of__(s), propsets)

    def items(self):
        propsets=self.__propsets__()
        r=[]
        for n in propsets:
            if hasattr(n,'id'): id=n.id
            else: id=''
            r.append((id, n.__of__(self)))

        return r

    def get(self, name, default=None):
        for propset in self.__propsets__():
            if propset.id==name or (hasattr(propset, 'xml_namespace') and \
                                    propset.xml_namespace()==name):
                return propset.__of__(self)
        return default

    def manage_addPropertySheet(self, id, ns):
        """ """
        md={'xmlns':ns}
        ps=PropertySheet(id, md)
        self.addPropertySheet(ps)
        return 'OK'

    def addPropertySheet(self, propset):
        propsets=self.aq_parent.__propsets__
        propsets=propsets+(propset,)
        self.aq_parent.__propsets__=propsets

    def delPropertySheet(self, name):
        result=[]
        for propset in self.aq_parent.__propsets__:
            if propset.getId() != name and  propset.xml_namespace() != name:
                result.append(propset)
        self.parent.__propsets__=tuple(result)

    def __len__(self):
        return len(self.__propsets__())

    def getId(self):
        return self.id


    # Management interface:

    manage=Globals.DTMLFile('dtml/propertysheets', globals())

    def manage_options(self):
        """Return a manage option data structure for me instance
        """
        try: r=self.REQUEST
        except: r=None
        if r is None:
            pre='../'
        else:
            pre=r['URL']
            for i in (1,2):
                l=pre.rfind('/')
                if l >= 0:
                    pre=pre[:l]
            pre=pre+'/'

        r=[]
        for d in self.aq_parent.manage_options:
            r.append({'label': d['label'], 'action': pre+d['action']})
        return r

    def tabs_path_info(self, script, path):
        l=path.rfind('/')
        if l >= 0: path=path[:l]
        return PropertySheets.inheritedAttribute('tabs_path_info')(
            self, script, path)

Globals.default__class_init__(PropertySheets)


class DefaultPropertySheets(PropertySheets):
    """A PropertySheets container that contains a default property
       sheet for compatibility with the arbitrary property mgmt
       design of Zope PropertyManagers."""
    default=DefaultProperties()
    webdav =DAVProperties()
    def _get_defaults(self):
        return (self.default, self.webdav)

Globals.default__class_init__(DefaultPropertySheets)


class FixedSchema(PropertySheet):
    """A Fixed-schema property sheet has no control over it's schema

    It gets its schema from another proprtysheet but has control over
    its value storage.

    This mix-in is used for ZClass instance proprtysheets, which store
    their data in instances, but get their schema from the
    proprtysheet managed in the ZClass.
    """

    def __init__(self, id, base, md=None):
        FixedSchema.inheritedAttribute('__init__')(self, id, md)
        self._base=base

    def _propertyMap(self):
        # Return a tuple of mappings, giving meta-data for properties.
        r = []
        for d in self._base._propertyMap():
            d = d.copy()
            mode = d.get('mode', 'wd')
            if 'd' in mode:
                d['mode']=filter(lambda c: c != 'd', mode)
            r.append(d)

        return tuple(r)

    def propertyMap(self):
        return self._propertyMap()

    def property_extensible_schema__(self):
        return 0
        return self._base._extensible

Globals.default__class_init__(FixedSchema)



class vps(Base):
    """Virtual Propertysheets

    The vps object implements a "computed attribute" - it ensures
    that a PropertySheets instance is returned when the propertysheets
    attribute of a PropertyManager is accessed.
    """
    def __init__(self, c=PropertySheets):
        self.c=c

    def __of__(self, parent):
        return self.c().__of__(parent)

def absattr(attr):
    if callable(attr):
        return attr()
    return attr


def xml_escape(v):
    """ convert any content from ISO-8859-1 to UTF-8
    The main use is to escape non-US object property values
    (e.g. containing accented characters). Also we convert "<" and ">"
    to entities to keep the properties XML compliant.
    """
    v = str(v)
    v = v.replace('&', '&amp;')
    v = v.replace('<', '&lt;')
    v = v.replace('>', '&gt;')
    return  unicode(v,"latin-1").encode("utf-8")
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.