Back to index

plone3  3.1.7
Composite.py
Go to the documentation of this file.
00001 from types import ListType, TupleType
00002 
00003 from Products.Archetypes.Schema import Schema
00004 from Products.Archetypes.interfaces.layer import ILayerContainer, \
00005      ILayerRuntime
00006 from Products.Archetypes.interfaces.schema import ICompositeSchema, \
00007      IBindableSchema
00008 
00009 from AccessControl import ClassSecurityInfo
00010 from Globals import InitializeClass
00011 from Acquisition import Implicit, aq_parent, aq_inner
00012 from Products.CMFCore.permissions import View, ModifyPortalContent
00013 
00014 class CompositeSchema(Implicit):
00015     """Act on behalf of a set of Schemas, pretending it
00016     was a single one.
00017 
00018     Note that if field names overlap, they last schema wins.
00019     """
00020 
00021     __implements__ = ICompositeSchema, ILayerRuntime, ILayerContainer
00022 
00023     security = ClassSecurityInfo()
00024     security.setDefaultAccess('allow')
00025 
00026     def __init__(self, schemas=None):
00027         self._schemas = [Schema()]
00028         self.addSchemas(schemas)
00029 
00030     def getSchemas(self):
00031         """Return the underlying schemas"""
00032         schemas = []
00033         context = aq_parent(aq_inner(self))
00034         for s in self._schemas:
00035             if IBindableSchema.isImplementedBy(s):
00036                 s.bind(context)
00037             schemas.append(s)
00038         return schemas
00039 
00040     def addSchemas(self, schemas):
00041         """Append to the underlying schemas"""
00042         if type(schemas) not in [ListType, TupleType]:
00043             schemas = (schemas, )
00044 
00045         for schema in schemas:
00046             self._schemas.append(schema)
00047 
00048     security.declareProtected(View, 'getName')
00049     def getName(self):
00050         """Return Schemata name"""
00051         return '-'.join([s.getName() for s in self.getSchemas()])
00052 
00053     def __add__(self, other):
00054         """Add two composite schemas"""
00055         c = CompositeSchema()
00056         c.addSchemas((self, other))
00057         return c
00058 
00059     security.declareProtected(View, 'copy')
00060     def copy(self):
00061         """Return a deep copy"""
00062         c = CompositeSchema()
00063         c.addSchemas([s.copy() for s in self._schemas()])
00064         return c
00065 
00066     security.declareProtected(View, 'fields')
00067     def fields(self):
00068         """Return a list of fields"""
00069         result = []
00070         for s in self.getSchemas():
00071             result.extend(s.fields())
00072         return result
00073 
00074     security.declareProtected(View, 'widgets')
00075     def widgets(self):
00076         """Return a dictionary that contains a widget for
00077         each field, using the field name as key.
00078 
00079         Note that if there are fields with the same name, they
00080         will be overriden by the last schema.
00081         """
00082         result = {}
00083         for s in self.getSchemas():
00084             result.update(s.widgets())
00085         return result
00086 
00087     security.declareProtected(View, 'filterFields')
00088     def filterFields(self, *predicates, **values):
00089         """Returns a subset of self.fields(), containing only fields that
00090         satisfy the given conditions.
00091 
00092         You can either specify predicates or values or both. If you provide
00093         both, all conditions must be satisfied.
00094 
00095         For each ``predicate`` (positional argument), ``predicate(field)`` must
00096         return 1 for a Field ``field`` to be returned as part of the result.
00097 
00098         Each ``attr=val`` function argument defines an additional predicate:
00099         A field must have the attribute ``attr`` and field.attr must be equal
00100         to value ``val`` for it to be in the returned list.
00101         """
00102         result = []
00103         for s in self.getSchemas():
00104             result.extend(s.filterFields(*predicates, **values))
00105         return result
00106 
00107     def __setitem__(self, name, field):
00108         """Add a field under key ``name`` (possibly
00109         overriding an existing one)
00110         """
00111         for s in self.getSchemas():
00112             if s.has_key(name):
00113                 s[name] = field
00114                 return
00115         self.getSchemas()[0][name] = field
00116 
00117     security.declareProtected(ModifyPortalContent, 'addField')
00118     def addField(self, field):
00119         """Add a field (possibly overriding an existing one)"""
00120         name = field.getName()
00121         for s in self.getSchemas():
00122             if s.has_key(name):
00123                 s.addField(field)
00124                 return
00125         self.getSchemas()[0].addField(field)
00126 
00127     security.declareProtected(ModifyPortalContent, 'updateField')
00128     updateField = addField
00129 
00130     def __delitem__(self, name):
00131         """Delete field by name ``name`` """
00132         for s in self.getSchemas():
00133             if s.has_key(name):
00134                 del s[name]
00135                 return
00136         del self.getSchemas()[0][name]
00137 
00138     security.declareProtected(ModifyPortalContent, 'delField')
00139     delField = __delitem__
00140 
00141     def __getitem__(self, name):
00142         """Get field by name.
00143 
00144         Raises KeyError if the field does not exist.
00145         """
00146         for s in self.getSchemas():
00147             if s.has_key(name):
00148                 return s[name]
00149         return self.getSchemas()[0][name]
00150 
00151     security.declareProtected(View, 'get')
00152     def get(self, name, default=None):
00153         """Get field by name, using a default value
00154         for missing
00155         """
00156         for s in self.getSchemas():
00157             if s.has_key(name):
00158                 return s.get(name)
00159         return self.getSchemas()[0].get(name, default)
00160 
00161     security.declareProtected(View, 'has_key')
00162     def has_key(self, name):
00163         """Check if a field by the given name exists"""
00164         for s in self.getSchemas():
00165             if s.has_key(name):
00166                 return True
00167         return self.getSchemas()[0].has_key(name)
00168 
00169 
00170     security.declareProtected(View, 'keys')
00171     def keys(self, name):
00172         """Return the names of the fields present
00173         on this schema
00174         """
00175         result = []
00176         for s in self.getSchemas():
00177             result.extend(s.keys())
00178         return result
00179 
00180     security.declareProtected(View, 'searchable')
00181     def searchable(self):
00182         """Return a list containing names of all
00183         the fields present on this schema that are
00184         searchable.
00185         """
00186         result = []
00187         for s in self.getSchemas():
00188             result.extend(s.searchable())
00189         return result
00190 
00191     security.declareProtected(ModifyPortalContent, 'edit')
00192     def edit(self, instance, name, value):
00193         """Call the mutator by name on instance,
00194         setting the value.
00195         """
00196         if self.has_key(name):
00197             instance[name] = value
00198 
00199     security.declareProtected(ModifyPortalContent, 'setDefaults')
00200     def setDefaults(self, instance):
00201         """Only call during object initialization.
00202 
00203         Sets fields to schema defaults.
00204         """
00205         for s in self.getSchemas():
00206             s.setDefaults(instance)
00207 
00208     security.declareProtected(ModifyPortalContent, 'updateAll')
00209     def updateAll(self, instance, **kwargs):
00210         """This method mutates fields in the given instance.
00211 
00212         For each keyword argument k, the key indicates the name of the
00213         field to mutate while the value is used to call the mutator.
00214 
00215         E.g. updateAll(instance, id='123', amount=500) will, depending on the
00216         actual mutators set, result in two calls: ``instance.setId('123')`` and
00217         ``instance.setAmount(500)``.
00218         """
00219         for s in self.getSchemas():
00220             s.updateAll(instance, **kwargs)
00221 
00222     security.declareProtected(View, 'allow')
00223     allow = has_key
00224 
00225     security.declareProtected(ModifyPortalContent, 'validate')
00226     def validate(self, instance=None, REQUEST=None,
00227                  errors=None, data=None, metadata=None):
00228         """Validate the state of the entire object.
00229 
00230         The passed dictionary ``errors`` will be filled with human readable
00231         error messages as values and the corresponding fields' names as
00232         keys.
00233         """
00234         for s in self.getSchemas():
00235             s.validate(instance=instance, REQUEST=REQUEST,
00236                        errors=errors, data=data, metadata=metadata)
00237         return errors
00238 
00239     security.declarePrivate('toString')
00240     def toString(self):
00241         """Utility method for converting a Schema to a string for the
00242         purpose of comparing schema.
00243 
00244         This is used for determining whether a schema
00245         has changed in the auto update function.
00246         """
00247         result = ''
00248         for s in self.getSchemas():
00249             result += s.toString()
00250         return result
00251 
00252     security.declarePrivate('signature')
00253     def signature(self):
00254         """Return an md5 sum of the the schema.
00255 
00256         This is used for determining whether a schema
00257         has changed in the auto update function.
00258         """
00259         from md5 import md5
00260         return md5(self.toString()).digest()
00261 
00262     security.declarePrivate('changeSchemataForField')
00263     def changeSchemataForField(self, fieldname, schemataname):
00264         """Change the schemata for a field """
00265         for s in self.getSchemas():
00266             if s.has_key(fieldname):
00267                 s.changeSchemataForField(fieldname, schemataname)
00268 
00269     security.declarePrivate('replaceField')
00270     def replaceField(self, name, field):
00271         """Replace field under ``name`` with ``field``"""
00272         for s in self.getSchemas():
00273             if s.has_key(name):
00274                 s.replaceField(name, field)
00275 
00276     security.declarePrivate('initializeLayers')
00277     def initializeLayers(self, instance, item=None, container=None):
00278         """Layer initialization"""
00279         for s in self.getSchemas():
00280             if ILayerContainer.isImplementedBy(s):
00281                 s.initializeLayers(instance, item, container)
00282 
00283     security.declarePrivate('cleanupLayers')
00284     def cleanupLayers(self, instance, item=None, container=None):
00285         """Layer cleaning"""
00286         for s in self.getSchemas():
00287             if ILayerContainer.isImplementedBy(s):
00288                 s.cleanupLayers(instance, item, container)
00289 
00290 InitializeClass(CompositeSchema)