[CMF-checkins] CVS: Products/CMFDefault - MetadataTool.py:1.23
Tres Seaver
tseaver at zope.com
Tue Jul 20 02:08:10 EDT 2004
Update of /cvs-repository/Products/CMFDefault
In directory cvs.zope.org:/tmp/cvs-serv2017/CMFDefault
Modified Files:
MetadataTool.py
Log Message:
- CMFCore/interfaces/portal_metadata.py:
o Normalize docstrings.
o Documented 'listAllowedVocabulary', and noted the element-specifc
convenience wrappers as being deprecated.
o Add new methods, 'getContentMetadata' and 'setContentMetadata',
through which the content objects may indirect their DublinCore
storage.
- CMFDefault/MetadataTool.py:
o Docstring cleanup.
o Implement new methods in interface, using propertysheet for storage.
- CMFDefault/tests/test_MetadataTool.py:
o Normalize unit tests:
+ Don't import module-under-test at module scope in the unit test
module!
+ Use unittest helper methods, rather than bare asserts.
+ Avoid unnecessary setUp and tearDown methods.
+ Split overlong tests into smaller, more coherent pieces, with
more descriptive names.
=== Products/CMFDefault/MetadataTool.py 1.22 => 1.23 ===
--- Products/CMFDefault/MetadataTool.py:1.22 Wed May 5 10:18:24 2004
+++ Products/CMFDefault/MetadataTool.py Tue Jul 20 02:07:39 2004
@@ -20,6 +20,7 @@
from Globals import DTMLFile
from Globals import InitializeClass
from Globals import PersistentMapping
+from OFS.PropertySheets import PropertySheet
from OFS.SimpleItem import SimpleItem
from Products.CMFCore.ActionProviderBase import ActionProviderBase
@@ -35,8 +36,8 @@
class MetadataElementPolicy( SimpleItem ):
- """
- Represent a type-specific policy about a particular DCMI element.
+
+ """ Represent a type-specific policy about a particular DCMI element.
"""
security = ClassSecurityInfo()
@@ -74,41 +75,43 @@
#
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 vocabulary for this element be restricted?
"""
return self.enforce_vocabulary
security.declareProtected(View , 'allowedVocabulary')
def allowedVocabulary( self ):
- """
+
+ """ If so, what are the allowed values?
"""
return self.allowed_vocabulary
@@ -125,8 +128,8 @@
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()
@@ -146,16 +149,17 @@
security.declareProtected(View , 'isMultiValued')
def isMultiValued( self ):
- """
- Is this element multi-valued?
+
+ """ Is this element multi-valued?
"""
return self.is_multi_valued
security.declareProtected(View , 'getPolicy')
def getPolicy( self, typ=None ):
- """
- Find the policy this element for objects whose type
- object name is 'typ'; return a default, if none found.
+
+ """ Find the policy this element for objects of a given type.
+
+ o Return a default, if none found.
"""
try:
return self.policies[ typ ].__of__(self)
@@ -164,8 +168,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():
@@ -174,9 +178,8 @@
security.declareProtected(ManagePortal , 'addPolicy')
def addPolicy( self, typ ):
- """
- Add a policy to this element for objects whose type
- object name is 'typ'.
+
+ """ Add a policy to this element for objects of a given type.
"""
if typ is None:
raise MetadataError, "Can't replace default policy."
@@ -188,9 +191,10 @@
security.declareProtected(ManagePortal, 'removePolicy')
def removePolicy( self, typ ):
- """
- Remove the policy from this element for objects whose type
- object name is 'typ' (*not* the default, however).
+
+ """ Remove the policy from this element for objects of a given type.
+
+ o Note that this method does *not* remove the default!
"""
if typ is None:
raise MetadataError, "Can't remove default policy."
@@ -201,6 +205,8 @@
class MetadataTool( UniqueObject, SimpleItem, ActionProviderBase ):
+ """ Hold, enable, and enforce site-wide metadata policies.
+ """
__implements__ = (IMetadataTool, ActionProviderBase.__implements__)
id = 'portal_metadata'
@@ -267,9 +273,8 @@
# TODO , validation_hook=None
, REQUEST=None
):
- """
- Form handler for "tool-wide" properties (including list of
- metadata elements).
+
+ """ Form handler for "tool-wide" properties .
"""
if publisher is not None:
self.publisher = publisher
@@ -297,8 +302,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
@@ -325,8 +330,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
@@ -351,12 +356,15 @@
, allowed_vocabulary
, REQUEST=None
):
- """
- Update a policy for one of our elements ('content_type'
- will be '<default>' when we edit the default).
+
+ """ Update a policy for one of our elements
+
+ o Note that 'content_type' will be passed as '<default>' when we
+ are editing the default.
"""
if content_type == '<default>':
content_type = None
+
spec = self.getElementSpec( element )
policy = spec.getPolicy( content_type )
policy.edit( is_required
@@ -378,9 +386,8 @@
#
security.declareProtected(ManagePortal, 'listElementSpecs')
def listElementSpecs( self ):
- """
- Return a list of ElementSpecs representing
- the elements managed by the tool.
+
+ """ Return a list of ElementSpecs describing our elements.
"""
res = []
for k, v in self.element_specs.items():
@@ -389,16 +396,15 @@
security.declareProtected(ManagePortal, 'getElementSpec')
def getElementSpec( self, element ):
- """
- Return an ElementSpec representing the tool's knowledge
- of 'element'.
+
+ """ Return an ElementSpec describing what we know about '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 ):
@@ -414,8 +420,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 ]
@@ -427,13 +433,16 @@
security.declareProtected(ManagePortal, 'listPolicies')
def listPolicies( self, typ=None ):
- """
- Show all policies for a given content type, or the default
- if None.
+
+ """ Show all policies for a given content type.
+
+ o If 'typ' is none, return the set of default policies.
"""
result = []
+
for element, spec in self.listElementSpecs():
result.append( ( element, spec.getPolicy( typ ) ) )
+
return result
#
@@ -441,28 +450,22 @@
#
security.declarePrivate( 'getFullName' )
def getFullName( self, userid ):
- """
- 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.
+ """ See 'portal_metadata' interface.
"""
return userid # TODO: do lookup here
security.declarePublic( 'getPublisher' )
def getPublisher( self ):
- """
- Return the "formal" name of the publisher of the
- portal.
+
+ """ See 'portal_metadata' interface.
"""
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.
+
+ """ See 'portal_metadata' interface.
"""
spec = self.getElementSpec( element )
if content_type is None and content:
@@ -471,41 +474,36 @@
security.declarePublic( 'listAllowedSubjects' )
def listAllowedSubjects( self, content=None, content_type=None ):
- """
- List allowed keywords for a given portal_type, or all
- possible keywords if none supplied.
+
+ """ See 'portal_metadata' interface.
"""
return self.listAllowedVocabulary( 'Subject', content, content_type )
security.declarePublic( 'listAllowedFormats' )
def listAllowedFormats( self, content=None, content_type=None ):
- """
- List the allowed 'Content-type' values for a particular
- portal_type, or all possible formats if none supplied.
+
+ """ See 'portal_metadata' interface.
"""
return self.listAllowedVocabulary( 'Format', content, content_type )
security.declarePublic( 'listAllowedLanguages' )
def listAllowedLanguages( self, content=None, content_type=None ):
- """
- List the allowed language values.
+
+ """ See 'portal_metadata' interface.
"""
return self.listAllowedVocabulary( 'Language', content, content_type )
security.declarePublic( 'listAllowedRights' )
def listAllowedRights( self, content=None, content_type=None ):
- """
- List the allowed values for a "Rights"
- selection list; this gets especially important where
- syndication is involved.
+
+ """ See 'portal_metadata' interface.
"""
return self.listAllowedVocabulary( 'Rights', content, content_type )
security.declareProtected(ModifyPortalContent, 'setInitialMetadata')
def setInitialMetadata( self, content ):
- """
- Set initial values for content metatdata, supplying
- any site-specific defaults.
+
+ """ See 'portal_metadata' interface.
"""
for element, policy in self.listPolicies(content.getPortalTypeName()):
@@ -523,11 +521,8 @@
security.declareProtected(View, 'validateMetadata')
def validateMetadata( self, content ):
- """
- 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.
+
+ """ See 'portal_metadata' interface.
"""
for element, policy in self.listPolicies(content.getPortalTypeName()):
@@ -546,4 +541,119 @@
# TODO: Call validation_hook, if present
+ security.declarePrivate( 'getContentMetadata' )
+ def getContentMetadata( self, content, element ):
+
+ """ See 'portal_metadata' interface.
+ """
+ dcmi = self._checkAndConvert( content )
+ return dcmi.getProperty( element )
+
+ security.declarePrivate( 'setContentMetadata' )
+ def setContentMetadata( self, content, element, value ):
+
+ """ See 'portal_metadata' interface.
+ """
+ dcmi = self._checkAndConvert( content )
+ dcmi._updateProperty( element, value )
+
+ #
+ # Helper methods
+ #
+ def _getDCMISheet( self, content ):
+
+ """ Return the DCMI propertysheet for content.
+
+ o Return 'None' if the sheet does not exist.
+ """
+ return content.propertysheets.get( DCMI_NAMESPACE )
+
+ def _checkAndConvert( self, content ):
+
+ """ Ensure that content has the DCMI propertysheet.
+
+ o Copy any legacy values from DCMI attributes to it, and remove
+ them.
+ """
+ sheets = content.propertysheets
+
+ sheet = sheets.get( DCMI_NAMESPACE )
+ if sheet is not None:
+ return sheet
+
+ md = { 'xmlns' : DCMI_NAMESPACE }
+ sheet = DCMISchema( 'dc', md )
+ marker = object()
+
+ for prop_name, attr in _DCMI_CONVERSIONS:
+
+ old = getattr( content, attr, marker )
+
+ if old is not marker:
+ setattr( sheet, prop_name, old )
+ try:
+ delattr( content, attr )
+ except ( AttributeError, KeyError ):
+ pass
+
+ sheets.addPropertySheet( sheet )
+ return sheet.__of__( sheets )
+
InitializeClass( MetadataTool )
+
+
+DCMI_NAMESPACE = 'http://purl.org/dc/elements/1.1/'
+
+class DCMISchema( PropertySheet ):
+
+ """ Fixed schema for DublinCore metadata.
+
+ o It gets its schema from a static map but has control over its
+ value storage.
+ """
+ def _propertyMap(self):
+ # Return a tuple of mappings, giving meta-data for properties.
+ result = []
+
+ for info in _DCMI_PROPERTY_MAP:
+ info = info.copy()
+ result.append( info )
+
+ return tuple( result )
+
+ def propertyMap(self):
+ return self._propertyMap()
+
+ def property_extensible_schema__(self):
+ return False
+
+InitializeClass( DCMISchema )
+
+_DCMI_PROPERTY_MAP = \
+( { 'id' : 'title', 'type' : 'string', 'mode' : 'w' }
+, { 'id' : 'description', 'type' : 'string', 'mode' : 'w' }
+, { 'id' : 'subject', 'type' : 'lines', 'mode' : 'w' }
+, { 'id' : 'contributors', 'type' : 'lines', 'mode' : 'w' }
+, { 'id' : 'created', 'type' : 'date', 'mode' : 'w' }
+, { 'id' : 'modified', 'type' : 'date', 'mode' : 'w' }
+, { 'id' : 'efffective', 'type' : 'date', 'mode' : 'w' }
+, { 'id' : 'expires', 'type' : 'date', 'mode' : 'w' }
+, { 'id' : 'format', 'type' : 'string', 'mode' : 'w' }
+, { 'id' : 'language', 'type' : 'string', 'mode' : 'w' }
+, { 'id' : 'rights', 'type' : 'string', 'mode' : 'w' }
+)
+
+# Map properties onto attributes
+_DCMI_CONVERSIONS = \
+( ( 'title', 'title' )
+, ( 'description', 'description' )
+, ( 'subject', 'subject' )
+, ( 'contributors', 'contributors' )
+, ( 'created', 'creation_data' )
+, ( 'modified', 'modification_date' )
+, ( 'efffective', 'effective_date' )
+, ( 'expires', 'expiration_date' )
+, ( 'format', 'format' )
+, ( 'language', 'language' )
+, ( 'rights', 'rights' )
+)
More information about the CMF-checkins
mailing list