[CMF-checkins] SVN: CMF/trunk/C CMFDefault.MetadataTool: support
arbitrary additional schemas.
Tres Seaver
tseaver at palladion.com
Mon Nov 28 22:29:20 EST 2005
Log message for revision 40396:
CMFDefault.MetadataTool: support arbitrary additional schemas.
The "stock" DublinCore-specific API is still accessible, implemented via a
special "DCMI" subobject.
Changed:
U CMF/trunk/CHANGES.txt
U CMF/trunk/CMFCalendar/setuphandlers.py
U CMF/trunk/CMFCore/interfaces/_tools.py
U CMF/trunk/CMFDefault/MetadataTool.py
U CMF/trunk/CMFDefault/dtml/metadataElementPolicies.dtml
U CMF/trunk/CMFDefault/dtml/metadataProperties.dtml
U CMF/trunk/CMFDefault/tests/test_MetadataTool.py
-=-
Modified: CMF/trunk/CHANGES.txt
===================================================================
--- CMF/trunk/CHANGES.txt 2005-11-29 01:58:24 UTC (rev 40395)
+++ CMF/trunk/CHANGES.txt 2005-11-29 03:29:18 UTC (rev 40396)
@@ -2,6 +2,10 @@
New Features
+ - CMFDefault.MetadataTool: support arbitrary additional schemas.
+ The "stock" DublinCore-specific API is still accessible, implemented
+ via a special "DCMI" subobject.
+
- WorkflowTool and DCWorkflow: Improved add form for workflow objects.
Presettings can now be loaded from workflow settings in setup profiles.
This replaces the feature that did allow to load presettings from the
Modified: CMF/trunk/CMFCalendar/setuphandlers.py
===================================================================
--- CMF/trunk/CMFCalendar/setuphandlers.py 2005-11-29 01:58:24 UTC (rev 40395)
+++ CMF/trunk/CMFCalendar/setuphandlers.py 2005-11-29 03:29:18 UTC (rev 40396)
@@ -32,15 +32,19 @@
# Set up a MetadataTool element policy for events
try:
_ = str # MetadataTool ist not aware of MessageIDs
- mdtool.addElementPolicy(
+ mdtool.DCMI.addElementPolicy(
element='Subject',
content_type='Event',
is_required=0,
supply_default=0,
default_value='',
enforce_vocabulary=0,
- allowed_vocabulary=(_('Appointment'), _('Convention'),
- _('Meeting'), _('Social Event'), _('Work')),
+ allowed_vocabulary=(_('Appointment'),
+ _('Convention'),
+ _('Meeting'),
+ _('Social Event'),
+ _('Work'),
+ ),
REQUEST=None)
except MetadataError:
pass
Modified: CMF/trunk/CMFCore/interfaces/_tools.py
===================================================================
--- CMF/trunk/CMFCore/interfaces/_tools.py 2005-11-29 01:58:24 UTC (rev 40395)
+++ CMF/trunk/CMFCore/interfaces/_tools.py 2005-11-29 03:29:18 UTC (rev 40396)
@@ -911,14 +911,17 @@
o Must be set to 'portal_metadata'.
""")
+ id = Attribute('id', 'Must be set to "portal_metadata"')
+
#
- # Site-wide queries.
+ # Site-wide queries, specific to Dublin Core metadata.
#
def getFullName(userid):
- """ Convert an internal userid to a "formal" name, if possible
+ """ Convert an internal userid to a "formal" name.
+
+ o Convert only if possible, perhaps using the 'portal_membership'
+ tool; otherwise, return 'userid'.
- o 'userid' is the ID of the user within the user folder.
-
o Used to map userid's for Creator, Contributor DCMI queries.
"""
@@ -927,67 +930,100 @@
"""
#
- # Content-specific queries.
+ # Content-specific queries, for Dublin Core metadata.
#
- def listAllowedSubjects(content=None):
+ def listAllowedSubjects(content=None, content_type=None):
""" List the allowed values of the 'Subject' DCMI element.
- o If 'content' is not None, return only values appropriate for
- content's type; otherwise, return "default" values.
+ o 'Subject' elements should be keywords categorizing their resource.
- o 'Subject' elements should be keywords categorizing their resource.
+ o Return only values appropriate for content's type, or all values if
+ both 'content' and 'content_type' are None.
"""
- def listAllowedFormats(content=None):
+ def listAllowedFormats(content=None, content_type=None):
""" List the allowed values of the 'Format' DCMI element.
- o If 'content' is not None, return only values appropriate for
- content's type; otherwise, return "default" values.
+ o These items should be usable as HTTP 'Content-type' values.
- o 'Format' elements should be usable as HTTP 'Content-type' values.
+ o Return only values appropriate for content's type, or all values if
+ both 'content' and 'content_type' are None.
"""
- def listAllowedLanguages(content=None):
+ def listAllowedLanguages(content=None, content_type=None):
""" List the allowed values of the 'Language' DCMI element.
- o If 'content' is not None, return only values appropriate for
- content's type; otherwise, return "default" values.
-
o 'Language' element values should be suitable for generating
HTTP headers.
+
+ o Return only values appropriate for content's type, or all values if
+ both 'content' and 'content_type' are None.
"""
- def listAllowedRights(content=None):
+ def listAllowedRights(content=None, content_type=None):
""" List the allowed values of the 'Rights' DCMI element.
- o If 'content' is not None, return only values appropriate for
- content's type; otherwise, return "default" values.
+ o The 'Rights' element describes copyright or other IP declarations
+ pertaining to a resource.
- o The 'Rights' element describes copyright or other IP
- declarations pertaining to a resource.
+ o Return only values appropriate for content's type, or all values if
+ both 'content' and 'content_type' are None.
"""
#
- # Validation policy hooks.
+ # Content-specific queries, for generic metadata.
#
- def setInitialMetadata(content):
- """ Set default initial values for content metatdata.
+ def listAllowedVocabulary( schema
+ , element
+ , content=None
+ , content_type=None
+ ):
+ """ List allowed values for a given schema element and content object.
+
+ o List possible keywords if both 'content' and 'content_type' are None.
"""
- def validateMetadata(content):
- """ Enforce portal-wide policies about DCMI elements.
+ #
+ # Schema manipulation
+ #
+ def listSchemas():
+ """ Return a list of (id, schema) tuples enumerating our schema.
+ """
- o Such policy may, e.g., require non-empty title/description, etc.
+ def addSchema( schema_id ):
+ """ Create a new schema with the given ID.
- o Called by the CMF immediately before saving changes to the
- metadata of an object.
+ o Return the newly-created schema object.
- o XXX: Note that the default skins / edit methods do *not*
- call this method; the choice of when to apply the validation
- is policy.
+ o Raise KeyError if such a schema already exists.
"""
+ def removeSchema( schema_id ):
+ """ Remove an existing schema with the given ID.
+ o Raise KeyError if no such schema exists.
+ """
+
+ #
+ # Validation policy hooks.
+ #
+ def setInitialMetadata(content):
+ """ Set initial values for content metatdata.
+
+ o Supply any site-specific defaults.
+ """
+
+ def validateMetadata(content):
+ """ Enforce portal-wide policies about metadata.
+
+ o E.g., policies may require non-empty title/description, etc.
+
+ o This method may be called by view / workflow code at "appropriate"
+ times, such as immediately before saving changes to the metadata of
+ an object.
+ """
+
+
#
# Site Properties tool interface
#
Modified: CMF/trunk/CMFDefault/MetadataTool.py
===================================================================
--- CMF/trunk/CMFDefault/MetadataTool.py 2005-11-29 01:58:24 UTC (rev 40395)
+++ CMF/trunk/CMFDefault/MetadataTool.py 2005-11-29 03:29:18 UTC (rev 40396)
@@ -20,6 +20,7 @@
from Globals import DTMLFile
from Globals import InitializeClass
from Globals import PersistentMapping
+from OFS.Folder import Folder
from OFS.SimpleItem import SimpleItem
from zope.interface import implements
@@ -37,10 +38,8 @@
class MetadataElementPolicy( SimpleItem ):
-
+ """ Represent a type-specific policy about a particular metadata element.
"""
- Represent a type-specific policy about a particular DCMI element.
- """
security = ClassSecurityInfo()
#
@@ -77,62 +76,47 @@
#
security.declareProtected(View , 'isMultiValued')
def isMultiValued( self ):
+ """ Can this element hold multiple values?
"""
- Can this element hold multiple values?
- """
return self.is_multi_valued
security.declareProtected(View , 'isRequired')
def isRequired( self ):
+ """ Must this element be supplied?
"""
- Must this element be supplied?
- """
return self.is_required
security.declareProtected(View , 'supplyDefault')
def supplyDefault( self ):
+ """ Should the tool supply a default?
"""
- Should the tool supply a default?
- """
return self.supply_default
security.declareProtected(View , 'defaultValue')
def defaultValue( self ):
+ """ If so, what is the default?
"""
- If so, what is the default?
- """
return self.default_value
security.declareProtected(View , 'enforceVocabulary')
def enforceVocabulary( self ):
+ """ Should the tool enforce the policy's vocabulary?
"""
- """
return self.enforce_vocabulary
security.declareProtected(View , 'allowedVocabulary')
def allowedVocabulary( self ):
+ """ What are the allowed values?
"""
- """
return self.allowed_vocabulary
InitializeClass( MetadataElementPolicy )
-DEFAULT_ELEMENT_SPECS = ( ( 'Title', 0 )
- , ( 'Description', 0 )
- , ( 'Subject', 1 )
- , ( 'Format', 0 )
- , ( 'Language', 0 )
- , ( 'Rights', 0 )
- )
-
class ElementSpec( SimpleItem ):
-
+ """ Represent all the tool knows about a single metadata element.
"""
- Represent all the tool knows about a single metadata element.
- """
-
security = ClassSecurityInfo()
#
@@ -158,10 +142,10 @@
security.declareProtected(View , 'getPolicy')
def getPolicy( self, typ=None ):
+ """ Find the policy for this element for objects of the given type.
+
+ o Return a default, if none found.
"""
- Find the policy this element for objects whose type
- object name is 'typ'; return a default, if none found.
- """
try:
return self.policies[ typ ].__of__(self)
except KeyError:
@@ -169,9 +153,8 @@
security.declareProtected(View , 'listPolicies')
def listPolicies( self ):
+ """ Return a list of all policies for this element.
"""
- Return a list of all policies for this element.
- """
res = []
for k, v in self.policies.items():
res.append((k, v.__of__(self)))
@@ -179,10 +162,8 @@
security.declareProtected(ManagePortal , 'addPolicy')
def addPolicy( self, typ ):
+ """ Add a policy to this element for objects of the given type.
"""
- Add a policy to this element for objects whose type
- object name is 'typ'.
- """
if typ is None:
raise MetadataError, "Can't replace default policy."
@@ -193,10 +174,10 @@
security.declareProtected(ManagePortal, 'removePolicy')
def removePolicy( self, typ ):
+ """ Remove the policy from this element for objects of the given type.
+
+ o Do *not* remvoe the default, however.
"""
- Remove the policy from this element for objects whose type
- object name is 'typ' (*not* the default, however).
- """
if typ is None:
raise MetadataError, "Can't remove default policy."
del self.policies[ typ ]
@@ -204,90 +185,32 @@
InitializeClass( ElementSpec )
-class MetadataTool( UniqueObject, SimpleItem, ActionProviderBase ):
-
- implements(IMetadataTool)
- __implements__ = (z2IMetadataTool, ActionProviderBase.__implements__)
-
- id = 'portal_metadata'
- meta_type = 'Default Metadata Tool'
-
- #
- # Default values.
- #
- publisher = ''
- element_specs = None
- #initial_values_hook = None
- #validation_hook = None
-
+class MetadataSchema( SimpleItem ):
+ """ Describe a metadata schema.
+ """
security = ClassSecurityInfo()
- def __init__( self
- , publisher=None
- #, initial_values_hook=None
- #, validation_hook=None
- , element_specs=DEFAULT_ELEMENT_SPECS
- ):
+ meta_type = 'Metadata Schema'
+ publisher = ''
- self.editProperties( publisher
- #, initial_values_hook
- #, validation_hook
- )
-
+ def __init__( self, id, element_specs=() ):
+ self._setId( id )
self.element_specs = PersistentMapping()
-
for name, is_multi_valued in element_specs:
self.element_specs[ name ] = ElementSpec( is_multi_valued )
+
#
# ZMI methods
#
- manage_options = ( ActionProviderBase.manage_options +
- ( { 'label' : 'Overview'
- , 'action' : 'manage_overview'
- }
- , { 'label' : 'Properties'
- , 'action' : 'propertiesForm'
- }
- , { 'label' : 'Elements'
+ manage_options = ( ( { 'label' : 'Elements'
, 'action' : 'elementPoliciesForm'
}
- # TODO , { 'label' : 'Types'
- # , 'action' : 'typesForm'
- # }
+ ,
)
+ SimpleItem.manage_options
)
- security.declareProtected(ManagePortal, 'manage_overview')
- manage_overview = DTMLFile( 'explainMetadataTool', _dtmldir )
-
- security.declareProtected(ManagePortal, 'propertiesForm')
- propertiesForm = DTMLFile( 'metadataProperties', _dtmldir )
-
- security.declareProtected(ManagePortal, 'editProperties')
- def editProperties( self
- , publisher=None
- # TODO , initial_values_hook=None
- # TODO , validation_hook=None
- , REQUEST=None
- ):
- """
- Form handler for "tool-wide" properties (including list of
- metadata elements).
- """
- if publisher is not None:
- self.publisher = publisher
-
- # TODO self.initial_values_hook = initial_values_hook
- # TODO self.validation_hook = validation_hook
-
- if REQUEST is not None:
- REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
- + '/propertiesForm'
- + '?manage_tabs_message=Tool+updated.'
- )
-
security.declareProtected(ManagePortal, 'elementPoliciesForm')
elementPoliciesForm = DTMLFile( 'metadataElementPolicies', _dtmldir )
@@ -302,9 +225,8 @@
, allowed_vocabulary
, REQUEST=None
):
+ """ Add a type-specific policy for one of our elements.
"""
- Add a type-specific policy for one of our elements.
- """
if content_type == '<default>':
content_type = None
@@ -330,9 +252,8 @@
, content_type
, REQUEST=None
):
+ """ Remvoe a type-specific policy for one of our elements.
"""
- Remvoe a type-specific policy for one of our elements.
- """
if content_type == '<default>':
content_type = None
@@ -356,10 +277,10 @@
, allowed_vocabulary
, REQUEST=None
):
+ """ Update a policy for one of our elements
+
+ o 'content_type' will be '<default>' when we edit the default.
"""
- Update a policy for one of our elements ('content_type'
- will be '<default>' when we edit the default).
- """
if content_type == '<default>':
content_type = None
spec = self.getElementSpec( element )
@@ -383,10 +304,8 @@
#
security.declareProtected(ManagePortal, 'listElementSpecs')
def listElementSpecs( self ):
+ """ Return a list of ElementSpecs representing the elements we manage.
"""
- Return a list of ElementSpecs representing
- the elements managed by the tool.
- """
res = []
for k, v in self.element_specs.items():
res.append((k, v.__of__(self)))
@@ -394,17 +313,14 @@
security.declareProtected(ManagePortal, 'getElementSpec')
def getElementSpec( self, element ):
+ """ Return an ElementSpec for the given 'element'.
"""
- Return an ElementSpec representing the tool's knowledge
- of 'element'.
- """
return self.element_specs[ element ].__of__( self )
security.declareProtected(ManagePortal, 'addElementSpec')
def addElementSpec( self, element, is_multi_valued, REQUEST=None ):
+ """ Add 'element' to our list of managed elements.
"""
- Add 'element' to our list of managed elements.
- """
# Don't replace.
if self.element_specs.has_key( element ):
return
@@ -419,9 +335,8 @@
security.declareProtected(ManagePortal, 'removeElementSpec')
def removeElementSpec( self, element, REQUEST=None ):
+ """ Remove 'element' from our list of managed elements.
"""
- Remove 'element' from our list of managed elements.
- """
del self.element_specs[ element ]
if REQUEST is not None:
@@ -432,122 +347,276 @@
security.declareProtected(ManagePortal, 'listPolicies')
def listPolicies( self, typ=None ):
+ """ Show all policies for a given content type
+
+ o If 'typ' is none, return the list of default policies.
"""
- Show all policies for a given content type, or the default
- if None.
- """
result = []
for element, spec in self.listElementSpecs():
result.append( ( element, spec.getPolicy( typ ) ) )
return result
+InitializeClass(MetadataSchema)
+
+
+_DCMI_ELEMENT_SPECS = ( ( 'Title', 0 )
+ , ( 'Description', 0 )
+ , ( 'Subject', 1 )
+ , ( 'Format', 0 )
+ , ( 'Language', 0 )
+ , ( 'Rights', 0 )
+ )
+
+class MetadataTool( UniqueObject, Folder, ActionProviderBase ):
+
+ implements(IMetadataTool)
+ __implements__ = (z2IMetadataTool, ActionProviderBase.__implements__)
+
+ id = 'portal_metadata'
+ meta_type = 'Default Metadata Tool'
+ _actions = ()
+
+ _DCMI = None
+ def _get_DCMI( self ):
+
+ if self._DCMI is None:
+ dcmi = self._DCMI = MetadataSchema( 'DCMI', _DCMI_ELEMENT_SPECS )
+
+ old_specs = getattr( self, 'element_specs', None )
+ if old_specs is not None:
+ del self.element_specs
+ for element_id, old_spec in old_specs.items():
+ new_spec = dcmi.getElementSpec( element_id )
+ for typ, policy in old_spec.listPolicies():
+ if typ is not None:
+ new_spec.addPolicy( typ )
+ tp = new_spec.getPolicy( typ )
+ tp.edit( is_required=policy.isRequired()
+ , supply_default=policy.supplyDefault()
+ , default_value=policy.defaultValue()
+ , enforce_vocabulary=policy.enforceVocabulary()
+ , allowed_vocabulary=policy.allowedVocabulary()
+ )
+
+ return self._DCMI
+
+ DCMI = property(_get_DCMI, None)
+
#
- # 'portal_metadata' interface
+ # Default values.
#
+ publisher = ''
+
+ security = ClassSecurityInfo()
+
+ def __init__( self, publisher=None ):
+
+ self.editProperties( publisher )
+
+ #
+ # ZMI methods
+ #
+ manage_options = ( ( { 'label' : 'Schema'
+ , 'action' : 'propertiesForm'
+ }
+ , { 'label' : 'Overview'
+ , 'action' : 'manage_overview'
+ }
+ )
+ + Folder.manage_options[:1]
+ + ActionProviderBase.manage_options +
+ Folder.manage_options[1:]
+ )
+
+ security.declareProtected(ManagePortal, 'manage_overview')
+ manage_overview = DTMLFile( 'explainMetadataTool', _dtmldir )
+
+ security.declareProtected(ManagePortal, 'propertiesForm')
+ propertiesForm = DTMLFile( 'metadataProperties', _dtmldir )
+
+ security.declareProtected(ManagePortal, 'editProperties')
+ def editProperties( self
+ , publisher=None
+ , REQUEST=None
+ ):
+ """ Form handler for "tool-wide" properties
+ """
+ if publisher is not None:
+ self.publisher = publisher
+
+ if REQUEST is not None:
+ REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
+ + '/propertiesForm'
+ + '?manage_tabs_message=Tool+updated.'
+ )
+
+ security.declareProtected(ManagePortal, 'manage_addSchema')
+ def manage_addSchema( self, schema_id, elements, REQUEST ):
+ """ ZMI wrapper around addSchema
+ """
+ massaged = []
+ for element in elements:
+ if isinstance(element, basestring):
+ element = element.split(',')
+ if len( element ) < 2:
+ element.append(0)
+ massaged.append( element )
+ self.addSchema( schema_id, massaged )
+
+ REQUEST['RESPONSE'].redirect( self.absolute_url()
+ + '/propertiesForm'
+ + '?manage_tabs_message=Schema+added.'
+ )
+
+ security.declareProtected(ManagePortal, 'manage_removeSchemas')
+ def manage_removeSchemas( self, schema_ids, REQUEST ):
+ """ ZMI wrapper around removeSchema
+ """
+ if not schema_ids:
+ raise ValueError, 'No schemas selected!'
+
+ for schema_id in schema_ids:
+ self.removeSchema( schema_id )
+
+ REQUEST['RESPONSE'].redirect( self.absolute_url()
+ + '/propertiesForm'
+ + '?manage_tabs_message=Schemas+removed.'
+ )
+
security.declarePrivate( 'getFullName' )
def getFullName( self, userid ):
+ """ See IMetadataTool.
"""
- Convert an internal userid to a "formal" name, if
- possible, perhaps using the 'portal_membership' tool.
-
- Used to map userid's for Creator, Contributor DCMI
- queries.
- """
return userid # TODO: do lookup here
security.declarePublic( 'getPublisher' )
def getPublisher( self ):
+ """ See IMetadataTool.
"""
- Return the "formal" name of the publisher of the
- portal.
- """
return self.publisher
- security.declarePublic( 'listAllowedVocabulary' )
- def listAllowedVocabulary( self, element, content=None, content_type=None ):
- """
- List allowed keywords for a given portal_type, or all
- possible keywords if none supplied.
- """
- spec = self.getElementSpec( element )
- if content_type is None and content:
- content_type = content.getPortalTypeName()
- return spec.getPolicy( content_type ).allowedVocabulary()
-
security.declarePublic( 'listAllowedSubjects' )
def listAllowedSubjects( self, content=None, content_type=None ):
+ """ See IMetadataTool.
"""
- List allowed keywords for a given portal_type, or all
- possible keywords if none supplied.
- """
- return self.listAllowedVocabulary( 'Subject', content, content_type )
+ return self.listAllowedVocabulary( 'DCMI'
+ , 'Subject'
+ , content
+ , content_type
+ )
security.declarePublic( 'listAllowedFormats' )
def listAllowedFormats( self, content=None, content_type=None ):
+ """ See IMetadataTool.
"""
- List the allowed 'Content-type' values for a particular
- portal_type, or all possible formats if none supplied.
- """
- return self.listAllowedVocabulary( 'Format', content, content_type )
+ return self.listAllowedVocabulary( 'DCMI'
+ , 'Format'
+ , content
+ , content_type
+ )
security.declarePublic( 'listAllowedLanguages' )
def listAllowedLanguages( self, content=None, content_type=None ):
+ """ See IMetadataTool.
"""
- List the allowed language values.
- """
- return self.listAllowedVocabulary( 'Language', content, content_type )
+ return self.listAllowedVocabulary( 'DCMI'
+ , 'Language'
+ , content
+ , content_type
+ )
security.declarePublic( 'listAllowedRights' )
def listAllowedRights( self, content=None, content_type=None ):
+ """ See IMetadata Tool.
"""
- List the allowed values for a "Rights"
- selection list; this gets especially important where
- syndication is involved.
+ return self.listAllowedVocabulary( 'DCMI'
+ , 'Rights'
+ , content
+ , content_type
+ )
+
+ security.declarePublic( 'listAllowedVocabulary' )
+ def listAllowedVocabulary( self
+ , schema
+ , element
+ , content=None
+ , content_type=None
+ ):
+ """ See IMetadataTool.
"""
- return self.listAllowedVocabulary( 'Rights', content, content_type )
+ schema_def = getattr( self, schema )
+ spec = schema_def.getElementSpec( element )
+ if content_type is None and content:
+ content_type = content.getPortalTypeName()
+ return spec.getPolicy( content_type ).allowedVocabulary()
+ security.declarePublic( 'listSchemas' )
+ def listSchemas( self ):
+ """ See IMetadataTool.
+ """
+ result = [ ( 'DCMI', self.DCMI ) ]
+ result.extend( self.objectItems( [ MetadataSchema.meta_type ] ) )
+ return result
+
+ security.declareProtected(ModifyPortalContent, 'addSchema')
+ def addSchema( self, schema_id, elements=() ):
+ """ See IMetadataTool.
+ """
+ if schema_id == 'DCMI' or schema_id in self.objectIds():
+ raise KeyError, 'Duplicate schema ID: %s' % schema_id
+
+ schema = MetadataSchema( schema_id, elements )
+ self._setObject( schema_id, schema )
+
+ return self._getOb( schema_id )
+
+ security.declareProtected(ModifyPortalContent, 'removeSchema')
+ def removeSchema( self, schema_id ):
+ """ See IMetadataTool.
+ """
+ if schema_id == 'DCMI' or schema_id not in self.objectIds():
+ raise KeyError, 'Invalid schema ID: %s' % schema_id
+
+ self._delObject( schema_id )
+
security.declareProtected(ModifyPortalContent, 'setInitialMetadata')
def setInitialMetadata( self, content ):
+ """ See IMetadataTool.
"""
- Set initial values for content metatdata, supplying
- any site-specific defaults.
- """
- for element, policy in self.listPolicies(content.getPortalTypeName()):
+ for schema_id, schema in self.listSchemas():
+ for element, policy in schema.listPolicies(
+ content.getPortalTypeName()):
- if not getattr( content, element )():
+ if not getattr( content, element )():
- if policy.supplyDefault():
- setter = getattr( content, 'set%s' % element )
- setter( policy.defaultValue() )
- elif policy.isRequired():
- raise MetadataError, \
- 'Metadata element %s is required.' % element
+ if policy.supplyDefault():
+ setter = getattr( content, 'set%s' % element )
+ setter( policy.defaultValue() )
+ elif policy.isRequired():
+ raise MetadataError, \
+ 'Metadata element %s is required.' % element
# TODO: Call initial_values_hook, if present
security.declareProtected(View, 'validateMetadata')
def validateMetadata( self, content ):
+ """ See IMetadataTool.
"""
- Enforce portal-wide policies about DCI, e.g.,
- requiring non-empty title/description, etc. Called
- by the CMF immediately before saving changes to the
- metadata of an object.
- """
- for element, policy in self.listPolicies(content.getPortalTypeName()):
+ for schema_id, schema in self.listSchemas():
+ for element, policy in schema.listPolicies(
+ content.getPortalTypeName()):
- value = getattr( content, element )()
- if not value and policy.isRequired():
- raise MetadataError, \
- 'Metadata element %s is required.' % element
+ value = getattr( content, element )()
+ if not value and policy.isRequired():
+ raise MetadataError, \
+ 'Metadata element %s is required.' % element
- if value and policy.enforceVocabulary():
- values = policy.isMultiValued() and value or [ value ]
- for value in values:
- if not value in policy.allowedVocabulary():
- raise MetadataError, \
- 'Value %s is not in allowed vocabulary for ' \
- 'metadata element %s.' % ( value, element )
+ if value and policy.enforceVocabulary():
+ values = policy.isMultiValued() and value or [ value ]
+ for value in values:
+ if not value in policy.allowedVocabulary():
+ raise MetadataError, \
+ 'Value %s is not in allowed vocabulary for ' \
+ 'metadata element %s.' % ( value, element )
- # TODO: Call validation_hook, if present
-
InitializeClass( MetadataTool )
Modified: CMF/trunk/CMFDefault/dtml/metadataElementPolicies.dtml
===================================================================
--- CMF/trunk/CMFDefault/dtml/metadataElementPolicies.dtml 2005-11-29 01:58:24 UTC (rev 40395)
+++ CMF/trunk/CMFDefault/dtml/metadataElementPolicies.dtml 2005-11-29 03:29:18 UTC (rev 40396)
@@ -1,9 +1,9 @@
<dtml-var manage_page_header>
<dtml-var manage_tabs>
-<dtml-unless expr="REQUEST.has_key( 'element' )">
-<dtml-call expr="REQUEST.set( 'element', listElementSpecs()[0][0] )">
-</dtml-unless>
+<dtml-let specs=listElementSpecs
+ def_spec="specs and specs[0][0] or None"
+ current="REQUEST.get( 'element', def_spec )">
<h3> Update Element Metadata Policies </h3>
@@ -14,7 +14,7 @@
<td colspan="3">
<dtml-in listElementSpecs>
<dtml-let key=sequence-key>
- <dtml-if expr="key == REQUEST[ 'element' ]">
+ <dtml-if expr="key == current">
&dtml-key;
<dtml-else>
<a href="&dtml-URL;?element=&dtml-key;"> &dtml-key; </a>
@@ -24,14 +24,14 @@
</td>
</tr>
- <dtml-let spec="getElementSpec( element=REQUEST[ 'element' ] )"
+ <dtml-if def_spec>
+ <dtml-let spec="getElementSpec( element=current )"
multi="spec.isMultiValued()"
tokenz="multi and ':tokens' or ''"
>
<dtml-in expr="spec.listPolicies()" sort>
- <dtml-let element="REQUEST[ 'element']"
- key=sequence-key
+ <dtml-let key=sequence-key
typ="key or '<default>'"
policy=sequence-item
rqd="policy.isRequired() and 'checked' or ''"
@@ -44,7 +44,7 @@
>
<form action="&dtml-absolute_url;" method="POST">
- <input type="hidden" name="element" value="&dtml-element;">
+ <input type="hidden" name="element" value="&dtml-current;">
<input type="hidden" name="content_type" value="&dtml-typ;">
<tr style="background-color: DarkGray; color: DarkBlue">
@@ -110,7 +110,7 @@
</dtml-in>
<form action="&dtml-absolute_url;" method="POST">
- <input type="hidden" name="element" value="&dtml-element;">
+ <input type="hidden" name="element" value="&dtml-current;">
<tr style="background-color: DarkGray; color: DarkBlue">
<th colspan="4"> <new type> </th>
@@ -171,6 +171,9 @@
</tr>
</dtml-let>
+ </dtml-if>
</table>
+</dtml-let>
+
<dtml-var manage_page_footer>
Modified: CMF/trunk/CMFDefault/dtml/metadataProperties.dtml
===================================================================
--- CMF/trunk/CMFDefault/dtml/metadataProperties.dtml 2005-11-29 01:58:24 UTC (rev 40395)
+++ CMF/trunk/CMFDefault/dtml/metadataProperties.dtml 2005-11-29 03:29:18 UTC (rev 40396)
@@ -20,54 +20,76 @@
</table>
</form>
-<h3> Add Metadata Element </h3>
+<h3> Metadata Schemas </h3>
-<form action="&dtml-absolute_url;/addElementSpec" method="POST">
-<input type="hidden" name="is_multi_valued:int:default" value="0">
+<form action="&dtml-absolute_url;" method="POST">
<table>
+ <dtml-in listSchemas>
+ <dtml-if sequence-start>
+
<tr valign="middle">
- <th width="100" align="right"> Element: </th>
- <td> <input type="text" name="element" size="20"> </td>
+ <td width="16">
+ <br /> <!-- can't remove DCMI schema! -->
+ </td>
+ <td>
+ <a href="&dtml-absolute_url;/manage_workspace">DCMI</a>
+ </td>
</tr>
+ <dtml-else>
+
<tr valign="middle">
- <th width="100" align="right"> Multi-valued? </th>
- <td> <input type="checkbox" name="is_multi_valued:boolean"> </td>
+ <td>
+ <input type="checkbox" name="schema_ids:list" value="&dtml-sequence-key;" />
+ </td>
+ <td>
+ <a href="&dtml-absolute_url;/manage_workspace"
+ > &dtml-sequence-key; </a>
+ </td>
</tr>
+ </dtml-if>
+ </dtml-in>
+
<tr valign="middle">
- <td> <br> </td>
- <td> <input type="submit" value=" Add "> </td>
+ <td colspan="2">
+ <input type="submit" name="manage_removeSchemas:method"
+ value=" Remove Schemas ">
+ </td>
</tr>
-
</table>
-</form>
-<h3> Remove Metadata Element </h3>
+<br />
-<form action="&dtml-absolute_url;/removeElementSpec" method="POST">
<table>
- <tr valign="middle">
- <th width="100" align="right"> Element: </th>
- <td> <dtml-in listElementSpecs>
- <dtml-if sequence-start>
- <select name="element">
- </dtml-if>
- <option value="&dtml-sequence-key;"> &dtml-sequence-key; </option>
- <dtml-if sequence-end>
- </select>
- </dtml-if>
- </dtml-in>
+ <tr>
+ <th colspan="2"
+ style="background-color:#CCCCCC; color:#000088">
+ Add a Schema
+ </th>
+ <tr>
+ <th> Schema ID </th>
+ <td>
+ <input type="text" name="schema_id" size="20"><br />
</td>
</tr>
- <tr valign="middle">
- <td> <br> </td>
- <td> <input type="submit" value=" Remove "> </td>
+ <tr>
+ <th> Elements </th>
+ <td>
+ <textarea name="elements:lines" cols="30" rows="10"></textarea>
+ </td>
</tr>
+ <tr>
+ <td> <br /> </td>
+ <td>
+ <input type="submit" name="manage_addSchema:method" value=" Add Schema ">
+ </td>
+ </tr>
+
</table>
</form>
Modified: CMF/trunk/CMFDefault/tests/test_MetadataTool.py
===================================================================
--- CMF/trunk/CMFDefault/tests/test_MetadataTool.py 2005-11-29 01:58:24 UTC (rev 40395)
+++ CMF/trunk/CMFDefault/tests/test_MetadataTool.py 2005-11-29 03:29:18 UTC (rev 40396)
@@ -14,118 +14,132 @@
$Id$
"""
-
-from unittest import TestCase, TestSuite, makeSuite, main
+import unittest
import Testing
import Zope2
Zope2.startup()
from Acquisition import aq_base
-from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl
-from Products.CMFDefault.exceptions import MetadataError
-from Products.CMFDefault.MetadataTool import DEFAULT_ELEMENT_SPECS
-from Products.CMFDefault.MetadataTool import ElementSpec
-from Products.CMFDefault.MetadataTool import MetadataElementPolicy
-from Products.CMFDefault.MetadataTool import MetadataTool
+class TestMetadataElementPolicy( unittest.TestCase ):
-class TestMetadataElementPolicy( TestCase ):
+ def _getTargetClass( self ):
+ from Products.CMFDefault.MetadataTool import MetadataElementPolicy
+ return MetadataElementPolicy
- def setUp( self ):
- self.sv_policy = MetadataElementPolicy( 0 )
- self.mv_policy = MetadataElementPolicy( 1 )
+ def _makeOne( self, *args, **kw ):
+ return self._getTargetClass()( *args, **kw )
- def tearDown( self ):
- del self.sv_policy
- del self.mv_policy
+ def test_empty_single_valued( self ):
+ sv_policy = self._makeOne( 0 )
+ self.failIf( sv_policy.isMultiValued() )
+ self.failIf( sv_policy.isRequired() )
+ self.failIf( sv_policy.supplyDefault() )
+ self.failIf( sv_policy.defaultValue() )
+ self.failIf( sv_policy.enforceVocabulary() )
+ self.failIf( sv_policy.allowedVocabulary() )
- def test_emptySV( self ):
- assert not self.sv_policy.isMultiValued()
- assert not self.sv_policy.isRequired()
- assert not self.sv_policy.supplyDefault()
- assert not self.sv_policy.defaultValue()
- assert not self.sv_policy.enforceVocabulary()
- assert not self.sv_policy.allowedVocabulary()
+ def test_edit_single_valued( self ):
+ sv_policy = self._makeOne( 0 )
+ sv_policy.edit( 1, 1, 'xxx', 0, '' )
+ self.failIf( sv_policy.isMultiValued() )
+ self.failUnless( sv_policy.isRequired() )
+ self.failUnless( sv_policy.supplyDefault() )
+ self.assertEquals( sv_policy.defaultValue(), 'xxx' )
+ self.failIf( sv_policy.enforceVocabulary() )
+ self.failIf( sv_policy.allowedVocabulary() )
- def test_editSV( self ):
- self.sv_policy.edit( 1, 1, 'xxx', 0, '' )
- assert not self.sv_policy.isMultiValued()
- assert self.sv_policy.isRequired()
- assert self.sv_policy.supplyDefault()
- assert self.sv_policy.defaultValue() == 'xxx'
- assert not self.sv_policy.enforceVocabulary()
- assert not self.sv_policy.allowedVocabulary()
+ def test_empty_multi_valued( self ):
+ mv_policy = self._makeOne( 1 )
+ self.failUnless( mv_policy.isMultiValued() )
+ self.failIf( mv_policy.isRequired() )
+ self.failIf( mv_policy.supplyDefault() )
+ self.failIf( mv_policy.defaultValue() )
+ self.failIf( mv_policy.enforceVocabulary() )
+ self.failIf( mv_policy.allowedVocabulary() )
- def test_emptyMV( self ):
- assert self.mv_policy.isMultiValued()
- assert not self.mv_policy.isRequired()
- assert not self.mv_policy.supplyDefault()
- assert not self.mv_policy.defaultValue()
- assert not self.mv_policy.enforceVocabulary()
- assert not self.mv_policy.allowedVocabulary()
+ def test_edit_multi_valued( self ):
+ mv_policy = self._makeOne( 1 )
+ mv_policy.edit( 1, 1, 'xxx', 1, ( 'xxx', 'yyy' ) )
+ self.failUnless( mv_policy.isMultiValued() )
+ self.failUnless( mv_policy.isRequired() )
+ self.failUnless( mv_policy.supplyDefault() )
+ self.assertEqual( mv_policy.defaultValue(), 'xxx' )
+ self.failUnless( mv_policy.enforceVocabulary() )
+ self.assertEqual( len( mv_policy.allowedVocabulary() ), 2 )
+ self.failUnless( 'xxx' in mv_policy.allowedVocabulary() )
+ self.failUnless( 'yyy' in mv_policy.allowedVocabulary() )
- def test_editMV( self ):
- self.mv_policy.edit( 1, 1, 'xxx', 1, ( 'xxx', 'yyy' ) )
- assert self.mv_policy.isMultiValued()
- assert self.mv_policy.isRequired()
- assert self.mv_policy.supplyDefault()
- assert self.mv_policy.defaultValue() == 'xxx'
- assert self.mv_policy.enforceVocabulary()
- assert len( self.mv_policy.allowedVocabulary() ) == 2
- assert 'xxx' in self.mv_policy.allowedVocabulary()
- assert 'yyy' in self.mv_policy.allowedVocabulary()
-class TestElementSpec( TestCase ):
+class TestElementSpec( unittest.TestCase ):
- def setUp( self ):
- self.sv_spec = ElementSpec( 0 )
- self.mv_spec = ElementSpec( 1 )
+ def _getTargetClass( self ):
+ from Products.CMFDefault.MetadataTool import ElementSpec
+ return ElementSpec
- def tearDown( self ):
- del self.sv_spec
- del self.mv_spec
+ def _makeOne( self, *args, **kw ):
+ return self._getTargetClass()( *args, **kw )
- def test_empty( self ):
- assert not self.sv_spec.isMultiValued()
- assert self.sv_spec.getPolicy() == self.sv_spec.getPolicy( 'XYZ' )
- policies = self.sv_spec.listPolicies()
- assert len( policies ) == 1
- assert policies[0][0] is None
+ def test_empty_single_valued( self ):
+ sv_spec = self._makeOne( 0 )
+ self.failIf( sv_spec.isMultiValued() )
+ self.assertEqual( sv_spec.getPolicy(), sv_spec.getPolicy( 'XYZ' ) )
+ policies = sv_spec.listPolicies()
+ self.assertEqual( len( policies ), 1 )
+ self.assertEqual( policies[0][0], None )
- assert self.mv_spec.isMultiValued()
- assert self.mv_spec.getPolicy() == self.mv_spec.getPolicy( 'XYZ' )
- policies = self.mv_spec.listPolicies()
- assert len( policies ) == 1
- assert policies[0][0] is None
+ def test_empty_multi_valued( self ):
+ mv_spec = self._makeOne( 1 )
+ self.failUnless( mv_spec.isMultiValued() )
+ self.assertEqual( mv_spec.getPolicy(), mv_spec.getPolicy( 'XYZ' ) )
+ policies = mv_spec.listPolicies()
+ self.assertEqual( len( policies ), 1 )
+ self.assertEqual( policies[0][0], None )
-class Foo( DefaultDublinCoreImpl ):
- description = title = language = format = rights = ''
- subject = ()
+class TestMetadataSchema( unittest.TestCase ):
- def __init__( self ):
- pass # skip DDCI's default values
+ def _getTargetClass( self ):
+ from Products.CMFDefault.MetadataTool import MetadataSchema
+ return MetadataSchema
- def getPortalTypeName( self ):
- return 'Foo'
+ def _makeOne( self, *args, **kw ):
+ return self._getTargetClass()( *args, **kw )
-class Bar( Foo ):
+class TestMetadataTool( unittest.TestCase ):
- def getPortalTypeName( self ):
- return 'Bar'
+ def _getTargetClass( self ):
+ from Products.CMFDefault.MetadataTool import MetadataTool
+ return MetadataTool
+ def _makeOne( self, *args, **kw ):
+ return self._getTargetClass()( *args, **kw )
-class TestMetadataTool( TestCase ):
+ def _makeTestObjects( self ):
- def setUp( self ):
- self.tool = MetadataTool()
+ from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl
+ class Foo( DefaultDublinCoreImpl ):
- def tearDown( self ):
- del self.tool
+ description = title = language = format = rights = ''
+ subject = ()
+ def __init__( self ):
+ pass # skip DDCI's default values
+
+ def getPortalTypeName( self ):
+ return 'Foo'
+
+
+ class Bar( Foo ):
+
+ def getPortalTypeName( self ):
+ return 'Bar'
+
+ return Foo(), Bar()
+
def test_z2interfaces(self):
from Interface.Verify import verifyClass
from Products.CMFCore.interfaces.portal_actions \
@@ -133,89 +147,92 @@
from Products.CMFCore.interfaces.portal_metadata \
import portal_metadata as IMetadataTool
- verifyClass(IActionProvider, MetadataTool)
- verifyClass(IMetadataTool, MetadataTool)
+ verifyClass(IActionProvider, self._getTargetClass())
+ verifyClass(IMetadataTool, self._getTargetClass())
def test_z3interfaces(self):
from zope.interface.verify import verifyClass
from Products.CMFCore.interfaces import IActionProvider
from Products.CMFCore.interfaces import IMetadataTool
- verifyClass(IActionProvider, MetadataTool)
- verifyClass(IMetadataTool, MetadataTool)
+ verifyClass(IActionProvider, self._getTargetClass())
+ verifyClass(IMetadataTool, self._getTargetClass())
def test_empty( self ):
+ from Products.CMFDefault.MetadataTool import _DCMI_ELEMENT_SPECS
- assert not self.tool.getPublisher()
- assert self.tool.getFullName( 'foo' ) == 'foo'
+ tool = self._makeOne()
+ self.failIf( tool.getPublisher() )
+ self.assertEqual( tool.getFullName( 'foo' ), 'foo' )
- specs = list( self.tool.listElementSpecs() )
- defaults = list( DEFAULT_ELEMENT_SPECS )
+ dcmi = tool.DCMI
+ specs = list( dcmi.DCMI.listElementSpecs() )
+ defaults = list( _DCMI_ELEMENT_SPECS )
specs.sort(); defaults.sort()
- assert len( specs ) == len( defaults )
+ self.assertEqual( len( specs ), len( defaults ) )
for i in range( len( specs ) ):
- assert specs[i][0] == defaults[i][0]
- assert specs[i][1].isMultiValued() == defaults[i][1]
+ self.assertEqual( specs[i][0], defaults[i][0] )
+ self.assertEqual( specs[i][1].isMultiValued(), defaults[i][1] )
policies = specs[i][1].listPolicies()
- assert len( policies ) == 1
- assert policies[0][0] is None
+ self.assertEqual( len( policies ), 1 )
+ self.failUnless( policies[0][0] is None )
- assert not self.tool.getElementSpec( 'Title' ).isMultiValued()
- assert not self.tool.getElementSpec( 'Description' ).isMultiValued()
- assert self.tool.getElementSpec( 'Subject' ).isMultiValued()
- assert not self.tool.getElementSpec( 'Format' ).isMultiValued()
- assert not self.tool.getElementSpec( 'Language' ).isMultiValued()
- assert not self.tool.getElementSpec( 'Rights' ).isMultiValued()
+ self.failIf( dcmi.getElementSpec( 'Title' ).isMultiValued() )
+ self.failIf( dcmi.getElementSpec( 'Description' ).isMultiValued() )
+ self.failUnless( dcmi.getElementSpec( 'Subject' ).isMultiValued() )
+ self.failIf( dcmi.getElementSpec( 'Format' ).isMultiValued() )
+ self.failIf( dcmi.getElementSpec( 'Language' ).isMultiValued() )
+ self.failIf( dcmi.getElementSpec( 'Rights' ).isMultiValued() )
try:
- dummy = self.tool.getElementSpec( 'Foo' )
+ dummy = dcmi.getElementSpec( 'Foo' )
except KeyError:
pass
else:
- assert 0, "Expected KeyError"
+ self.failUnless( 0, "Expected KeyError" )
- assert not self.tool.listAllowedSubjects()
- assert not self.tool.listAllowedFormats()
- assert not self.tool.listAllowedLanguages()
- assert not self.tool.listAllowedRights()
+ self.failIf( dcmi.listAllowedSubjects() )
+ self.failIf( dcmi.listAllowedFormats() )
+ self.failIf( dcmi.listAllowedLanguages() )
+ self.failIf( dcmi.listAllowedRights() )
- def test_add( self ):
- self.tool.addElementSpec( 'Rating', 1 )
- assert len( self.tool.listElementSpecs() ) \
- == len( DEFAULT_ELEMENT_SPECS ) + 1
- rating = self.tool.getElementSpec( 'Rating' )
- assert rating.isMultiValued()
+ def test_DCMI_addElementSpec( self ):
+ from Products.CMFDefault.MetadataTool import _DCMI_ELEMENT_SPECS
- def test_remove( self ):
- self.tool.removeElementSpec( 'Rights' )
+ tool = self._makeOne()
+ dcmi = tool.DCMI
+ dcmi.addElementSpec( 'Rating', 1 )
+ self.assertEqual( len( dcmi.listElementSpecs() )
+ , len( _DCMI_ELEMENT_SPECS ) + 1 )
+ rating = dcmi.getElementSpec( 'Rating' )
+ self.failUnless( rating.isMultiValued() )
- assert len( self.tool.listElementSpecs() ) \
- == len( DEFAULT_ELEMENT_SPECS ) - 1
+ def test_DCMI_removeElementSpec( self ):
+ from Products.CMFDefault.MetadataTool import _DCMI_ELEMENT_SPECS
- try:
- dummy = self.tool.getElementSpec( 'Rights' )
- except KeyError:
- pass
- else:
- assert 0, "Expected KeyError"
+ tool = self._makeOne()
+ dcmi = tool.DCMI
+ dcmi.removeElementSpec( 'Rights' )
- try:
- self.tool.removeElementSpec( 'Foo' )
- except KeyError:
- pass
- else:
- assert 0, "Expected KeyError"
+ self.assertEqual( len( dcmi.listElementSpecs() )
+ , len( _DCMI_ELEMENT_SPECS ) - 1
+ )
+ self.assertRaises( KeyError, dcmi.getElementSpec, 'Rights' )
+ self.assertRaises( KeyError, dcmi.removeElementSpec, 'Foo' )
+
def test_simplePolicies( self ):
- tSpec = self.tool.getElementSpec( 'Title' )
+ tool = self._makeOne()
+ dcmi = tool.DCMI
+ tSpec = dcmi.getElementSpec( 'Title' )
# Fetch default policy.
tDef = tSpec.getPolicy()
- assert not tDef.isRequired()
- assert not tDef.supplyDefault()
- assert not tDef.defaultValue()
+ self.failIf( tDef.isRequired() )
+ self.failIf( tDef.supplyDefault() )
+ self.failIf( tDef.defaultValue() )
# Fetch (default) policy for a type.
tDoc = tSpec.getPolicy( 'Document' )
@@ -223,40 +240,42 @@
# Changing default changes policies found from there.
tDef.edit( 1, 1, 'xyz', 0, () )
- assert tDef.isRequired()
- assert tDef.supplyDefault()
- assert tDef.defaultValue() == 'xyz'
- assert tDoc.isRequired()
- assert tDoc.supplyDefault()
- assert tDoc.defaultValue() == 'xyz'
+ self.failUnless( tDef.isRequired() )
+ self.failUnless( tDef.supplyDefault() )
+ self.assertEqual( tDef.defaultValue(), 'xyz' )
+ self.failUnless( tDoc.isRequired() )
+ self.failUnless( tDoc.supplyDefault() )
+ self.assertEqual( tDoc.defaultValue(), 'xyz' )
tSpec.addPolicy( 'Document' )
- assert len( tSpec.listPolicies() ) == 2
+ self.assertEqual( len( tSpec.listPolicies() ), 2 )
tDoc = tSpec.getPolicy( 'Document' )
self.assertNotEqual(aq_base(tDoc), aq_base(tDef))
- assert not tDoc.isRequired()
- assert not tDoc.supplyDefault()
- assert not tDoc.defaultValue()
+ self.failIf( tDoc.isRequired() )
+ self.failIf( tDoc.supplyDefault() )
+ self.failIf( tDoc.defaultValue() )
tSpec.removePolicy( 'Document' )
tDoc = tSpec.getPolicy( 'Document' )
self.assertEqual(aq_base(tDoc), aq_base(tDef))
- assert tDoc.isRequired()
- assert tDoc.supplyDefault()
- assert tDoc.defaultValue() == 'xyz'
+ self.failUnless( tDoc.isRequired() )
+ self.failUnless( tDoc.supplyDefault() )
+ self.assertEqual( tDoc.defaultValue(), 'xyz' )
def test_multiValuedPolicies( self ):
- sSpec = self.tool.getElementSpec( 'Subject' )
+ tool = self._makeOne()
+ dcmi = tool.DCMI
+ sSpec = dcmi.getElementSpec( 'Subject' )
# Fetch default policy.
sDef = sSpec.getPolicy()
- assert not sDef.isRequired()
- assert not sDef.supplyDefault()
- assert not sDef.defaultValue()
- assert not sDef.enforceVocabulary()
- assert not sDef.allowedVocabulary()
+ self.failIf( sDef.isRequired() )
+ self.failIf( sDef.supplyDefault() )
+ self.failIf( sDef.defaultValue() )
+ self.failIf( sDef.enforceVocabulary() )
+ self.failIf( sDef.allowedVocabulary() )
# Fetch (default) policy for a type.
sDoc = sSpec.getPolicy( 'Document' )
@@ -264,150 +283,266 @@
# Changing default changes policies found from there.
sDef.edit( 1, 1, 'xyz', 1, ( 'foo', 'bar' ) )
- assert sDef.isRequired()
- assert sDef.supplyDefault()
- assert sDef.defaultValue() == 'xyz'
- assert sDoc.isRequired()
- assert sDoc.supplyDefault()
- assert sDoc.defaultValue() == 'xyz'
- assert sDef.enforceVocabulary()
- assert len( sDef.allowedVocabulary() ) == 2
- assert 'foo' in sDef.allowedVocabulary()
- assert 'bar' in sDef.allowedVocabulary()
- assert sDoc.enforceVocabulary()
- assert len( sDoc.allowedVocabulary() ) == 2
- assert 'foo' in sDoc.allowedVocabulary()
- assert 'bar' in sDoc.allowedVocabulary()
+ self.failUnless( sDef.isRequired() )
+ self.failUnless( sDef.supplyDefault() )
+ self.assertEqual( sDef.defaultValue(), 'xyz' )
+ self.failUnless( sDoc.isRequired() )
+ self.failUnless( sDoc.supplyDefault() )
+ self.assertEqual( sDoc.defaultValue(), 'xyz' )
+ self.failUnless( sDef.enforceVocabulary() )
+ self.assertEqual( len( sDef.allowedVocabulary() ), 2 )
+ self.failUnless( 'foo' in sDef.allowedVocabulary() )
+ self.failUnless( 'bar' in sDef.allowedVocabulary() )
+ self.failUnless( sDoc.enforceVocabulary() )
+ self.assertEqual( len( sDoc.allowedVocabulary() ), 2 )
+ self.failUnless( 'foo' in sDoc.allowedVocabulary() )
+ self.failUnless( 'bar' in sDoc.allowedVocabulary() )
sSpec.addPolicy( 'Document' )
- assert len( sSpec.listPolicies() ) == 2
+ self.assertEqual( len( sSpec.listPolicies() ), 2 )
sDoc = sSpec.getPolicy( 'Document' )
self.assertNotEqual(aq_base(sDoc), aq_base(sDef))
- assert not sDoc.isRequired()
- assert not sDoc.supplyDefault()
- assert not sDoc.defaultValue()
- assert not sDoc.enforceVocabulary()
- assert not sDoc.allowedVocabulary()
+ self.failIf( sDoc.isRequired() )
+ self.failIf( sDoc.supplyDefault() )
+ self.failIf( sDoc.defaultValue() )
+ self.failIf( sDoc.enforceVocabulary() )
+ self.failIf( sDoc.allowedVocabulary() )
sSpec.removePolicy( 'Document' )
sDoc = sSpec.getPolicy( 'Document' )
self.assertEqual(aq_base(sDoc), aq_base(sDef))
- assert sDoc.isRequired()
- assert sDoc.supplyDefault()
- assert sDoc.defaultValue() == 'xyz'
- assert sDoc.enforceVocabulary()
- assert len( sDoc.allowedVocabulary() ) == 2
- assert 'foo' in sDoc.allowedVocabulary()
- assert 'bar' in sDoc.allowedVocabulary()
+ self.failUnless( sDoc.isRequired() )
+ self.failUnless( sDoc.supplyDefault() )
+ self.assertEqual( sDoc.defaultValue(), 'xyz' )
+ self.failUnless( sDoc.enforceVocabulary() )
+ self.assertEqual( len( sDoc.allowedVocabulary() ), 2 )
+ self.failUnless( 'foo' in sDoc.allowedVocabulary() )
+ self.failUnless( 'bar' in sDoc.allowedVocabulary() )
def test_vocabularies( self ):
- fSpec = self.tool.getElementSpec( 'Format' )
+ tool = self._makeOne()
+ dcmi = tool.DCMI
+ fSpec = dcmi.getElementSpec( 'Format' )
fDef = fSpec.getPolicy()
formats = ( 'text/plain', 'text/html' )
fDef.edit( 0, 0, '', 0, ( 'text/plain', 'text/html' ) )
- assert self.tool.listAllowedFormats() == formats
+ self.assertEqual( tool.listAllowedFormats(), formats )
- foo = Foo()
- assert self.tool.listAllowedFormats( foo ) == formats
+ foo, bar = self._makeTestObjects()
+
+ self.assertEqual( tool.listAllowedFormats( foo ), formats )
+
fSpec.addPolicy( 'Foo' )
- assert not self.tool.listAllowedFormats( foo )
+ self.failIf( tool.listAllowedFormats( foo ) )
+
foo_formats = ( 'image/jpeg', 'image/gif', 'image/png' )
fFoo = fSpec.getPolicy( 'Foo' )
fFoo.edit( 0, 0, '', 0, foo_formats )
- assert self.tool.listAllowedFormats( foo ) == foo_formats
+ self.assertEqual( tool.listAllowedFormats( foo ), foo_formats )
- def test_initialValues( self ):
- foo = Foo()
- assert not foo.Title()
- assert not foo.Description()
- assert not foo.Subject()
- assert not foo.Format(), foo.Format()
- assert not foo.Language()
- assert not foo.Rights()
+ def test_initialValues_defaults( self ):
+ tool = self._makeOne()
+ foo, bar = self._makeTestObjects()
+ self.failIf( foo.Title() )
+ self.failIf( foo.Description() )
+ self.failIf( foo.Subject() )
+ self.failIf( foo.Format(), foo.Format() )
+ self.failIf( foo.Language() )
+ self.failIf( foo.Rights() )
- self.tool.setInitialMetadata( foo )
- assert not foo.Title()
- assert not foo.Description()
- assert not foo.Subject()
- assert not foo.Format()
- assert not foo.Language()
- assert not foo.Rights()
+ tool.setInitialMetadata( foo )
+ self.failIf( foo.Title() )
+ self.failIf( foo.Description() )
+ self.failIf( foo.Subject() )
+ self.failIf( foo.Format() )
+ self.failIf( foo.Language() )
+ self.failIf( foo.Rights() )
+ def test_initialValues_implicit( self ):
# Test default policy.
- foo = Foo()
- fSpec = self.tool.getElementSpec( 'Format' )
+ tool = self._makeOne()
+ dcmi = tool.DCMI
+ foo, bar = self._makeTestObjects()
+ fSpec = dcmi.getElementSpec( 'Format' )
fPolicy = fSpec.getPolicy()
fPolicy.edit( 0, 1, 'text/plain', 0, () )
- self.tool.setInitialMetadata( foo )
- assert not foo.Title()
- assert not foo.Description()
- assert not foo.Subject()
- assert foo.Format() == 'text/plain'
- assert not foo.Language()
- assert not foo.Rights()
+ tool.setInitialMetadata( foo )
+ self.failIf( foo.Title() )
+ self.failIf( foo.Description() )
+ self.failIf( foo.Subject() )
+ self.assertEqual( foo.Format(), 'text/plain' )
+ self.failIf( foo.Language() )
+ self.failIf( foo.Rights() )
+ def test_initialValues_explicit_raises_if_constraint_fails( self ):
+ from Products.CMFDefault.exceptions import MetadataError
+
# Test type-specific policy.
- foo = Foo()
- tSpec = self.tool.getElementSpec( 'Title' )
+ tool = self._makeOne()
+ dcmi = tool.DCMI
+ foo, bar = self._makeTestObjects()
+ tSpec = dcmi.getElementSpec( 'Title' )
tSpec.addPolicy( 'Foo' )
tPolicy = tSpec.getPolicy( foo.getPortalTypeName() )
tPolicy.edit( 1, 0, '', 0, () )
- try:
- self.tool.setInitialMetadata( foo )
- except MetadataError:
- pass
- else:
- assert 0, "Expected MetadataError"
+ self.assertRaises( MetadataError, tool.setInitialMetadata, foo )
+ def test_initialValues_explicit_mutliple_types( self ):
+ from Products.CMFDefault.exceptions import MetadataError
+
+ tool = self._makeOne()
+ dcmi = tool.DCMI
+ foo, bar = self._makeTestObjects()
foo.setTitle( 'Foo title' )
- self.tool.setInitialMetadata( foo )
- assert foo.Title() == 'Foo title'
- assert not foo.Description()
- assert not foo.Subject()
- assert foo.Format() == 'text/plain'
- assert not foo.Language()
- assert not foo.Rights()
+ fSpec = dcmi.getElementSpec( 'Format' )
+ fSpec.addPolicy( foo.getPortalTypeName() )
+ fPolicy = fSpec.getPolicy( foo.getPortalTypeName() )
+ fPolicy.edit( 0, 1, 'text/plain', 0, () )
+
+ tool.setInitialMetadata( foo )
+ self.assertEqual( foo.Title(), 'Foo title' )
+ self.failIf( foo.Description() )
+ self.failIf( foo.Subject() )
+ self.assertEqual( foo.Format(), 'text/plain' )
+ self.failIf( foo.Language() )
+ self.failIf( foo.Rights() )
+
# Ensure Foo's policy doesn't interfere with other types.
- bar = Bar()
- self.tool.setInitialMetadata( bar )
- assert not bar.Title()
- assert not bar.Description()
- assert not bar.Subject()
- assert bar.Format() == 'text/plain'
- assert not bar.Language()
- assert not bar.Rights()
+ tool.setInitialMetadata( bar )
+ self.failIf( bar.Title() )
+ self.failIf( bar.Description() )
+ self.failIf( bar.Subject() )
+ self.assertEqual( bar.Format(), '' )
+ self.failIf( bar.Language() )
+ self.failIf( bar.Rights() )
def test_validation( self ):
+ from Products.CMFDefault.exceptions import MetadataError
- foo = Foo()
- self.tool.setInitialMetadata( foo )
- self.tool.validateMetadata( foo )
+ tool = self._makeOne()
+ foo, bar = self._makeTestObjects()
+ tool.setInitialMetadata( foo )
+ tool.validateMetadata( foo )
- tSpec = self.tool.getElementSpec( 'Title' )
+ dcmi = tool.DCMI
+ tSpec = dcmi.getElementSpec( 'Title' )
tSpec.addPolicy( 'Foo' )
tPolicy = tSpec.getPolicy( foo.getPortalTypeName() )
tPolicy.edit( 1, 0, '', 0, () )
- try:
- self.tool.validateMetadata( foo )
- except MetadataError:
- pass
- else:
- assert 0, "Expected MetadataError"
+ self.assertRaises( MetadataError, tool.validateMetadata, foo )
foo.setTitle( 'Foo title' )
- self.tool.validateMetadata( foo )
+ tool.validateMetadata( foo )
+ def test_addSchema_normal( self ):
+ from Products.CMFDefault.MetadataTool import MetadataSchema
+ tool = self._makeOne()
+ before = tool.listSchemas()
+ self.assertEqual( len( before ), 1 )
+ self.assertEqual( before[0][0], 'DCMI' )
+ self.failUnless( isinstance( before[0][1], MetadataSchema ) )
+
+ tool.addSchema( 'Arbitrary' )
+
+ after = tool.listSchemas()
+ self.assertEqual( len( after ), 2 )
+ self.assertEqual( after[0][0], 'DCMI' )
+ self.failUnless( isinstance( after[0][1], MetadataSchema ) )
+ self.assertEqual( after[1][0], 'Arbitrary' )
+ self.failUnless( isinstance( after[1][1], MetadataSchema ) )
+
+ def test_addSchema_duplicate( self ):
+ tool = self._makeOne()
+ before = tool.listSchemas()
+ tool.addSchema( 'Arbitrary' )
+ self.assertRaises( KeyError, tool.addSchema, 'Arbitrary' )
+ self.assertRaises( KeyError, tool.addSchema, 'DCMI' )
+
+ def test_removeSchema_normal( self ):
+ tool = self._makeOne()
+ before = tool.listSchemas()
+ self.assertEqual( len( before ), 1 )
+ self.assertEqual( before[0][0], 'DCMI' )
+
+ tool.addSchema( 'Arbitrary' )
+ tool.addSchema( 'Beneficent' )
+ tool.addSchema( 'Grouchy' )
+
+ middle = tool.listSchemas()
+ self.assertEqual( len( middle ), 4 )
+ self.assertEqual( middle[0][0], 'DCMI' )
+ self.assertEqual( middle[1][0], 'Arbitrary' )
+ self.assertEqual( middle[2][0], 'Beneficent' )
+ self.assertEqual( middle[3][0], 'Grouchy' )
+
+ tool.removeSchema( 'Beneficent' )
+
+ after = tool.listSchemas()
+ self.assertEqual( len( after ), 3 )
+ self.assertEqual( after[0][0], 'DCMI' )
+ self.assertEqual( after[1][0], 'Arbitrary' )
+ self.assertEqual( after[2][0], 'Grouchy' )
+
+ def test_removeSchema_invalid( self ):
+ tool = self._makeOne()
+ self.assertRaises( KeyError, tool.removeSchema, 'DCMI' )
+ tool.addSchema( 'Arbitrary' )
+ tool.removeSchema( 'Arbitrary' )
+ self.assertRaises( KeyError, tool.removeSchema, 'Arbitrary' )
+
+ def test_migration( self ):
+ # Test that we forward-migrate old-style DCMI policies.
+ from Products.CMFDefault.MetadataTool import ElementSpec
+ from Products.CMFDefault.MetadataTool import _DCMI_ELEMENT_SPECS
+
+ tool = self._makeOne()
+ tool.element_specs = { 'Title' : ElementSpec( 0 )
+ , 'Description' : ElementSpec( 0 )
+ , 'Subject' : ElementSpec( 1 )
+ , 'Format' : ElementSpec( 0 )
+ , 'Language' : ElementSpec( 0 )
+ , 'Rights' : ElementSpec( 0 )
+ }
+ subj = tool.element_specs[ 'Subject' ]
+ subj.addPolicy( 'Foo' )
+ subj.getPolicy( 'Foo' ).edit( False
+ , False
+ , None
+ , True
+ , ( 'bar', 'baz' )
+ )
+
+ dcmi = tool.DCMI
+
+ self.assertEqual( dcmi.getId(), 'DCMI' )
+
+ # Accessing the DCMI property converts and clears 'element_specs'
+ self.assertRaises(AttributeError, lambda: tool.element_specs )
+
+ subj2 = dcmi.getElementSpec( 'Subject' )
+ subj_default = subj2.getPolicy( None )
+ subj_foo = subj2.getPolicy( 'Foo' )
+
+ self.assertEqual( subj_foo.isRequired(), False )
+ self.assertEqual( subj_foo.supplyDefault(), False )
+ self.assertEqual( subj_foo.defaultValue(), None )
+ self.assertEqual( subj_foo.enforceVocabulary(), True )
+ self.assertEqual( len( subj_foo.allowedVocabulary() ), 2 )
+ self.failUnless( 'bar' in subj_foo.allowedVocabulary() )
+ self.failUnless( 'baz' in subj_foo.allowedVocabulary() )
+
+
def test_suite():
- return TestSuite((
- makeSuite(TestMetadataElementPolicy),
- makeSuite(TestElementSpec),
- makeSuite(TestMetadataTool),
+ return unittest.TestSuite((
+ unittest.makeSuite(TestMetadataElementPolicy),
+ unittest.makeSuite(TestElementSpec),
+ unittest.makeSuite(TestMetadataTool),
))
if __name__ == '__main__':
- main(defaultTest='test_suite')
+ unittest.main(defaultTest='test_suite')
More information about the CMF-checkins
mailing list