[Zope-CMF] add Metadata element to existing types

Tim Hoffman timhoffman@cams.wa.gov.au
Wed, 27 Feb 2002 09:02:13 +0800


This is a multi-part message in MIME format.
--------------040403030808000300090105
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Siegfried Stepke wrote:

>Hello,
>
>although I found lots of information regarding adding new CMF-Types with 
>their own properties I didn't find anything about adding a totally new 
>element in the metadata to show up in the full_metadata_edit_form ...
>
>I succeeded in creating a new element in the portal_metadata tool; it 
>shows up there and I can set its default or content-type-specific 
>settings. But I don't know what to do to let it show up in the documents 
>meta-data edit forms...
>
>Is this possible? (or are only the standard metadata elements supported?)
>Is this possible for the default portal-types?
>Do I have to create a new "custom/full_metadata_edit_form"?
>  Which code would have to be integrated there for a subject-like element?
>
You need to patch defaultDublinCoreImpl with  new setter and getter methods
and you need to modify the create and bind new __init__ and _editMetadata,
and manage_editMetadata methods

plus make a new full_metadata_edit_form

I think one thing you need to consider is that these new elements that you
are adding will be added to all CMF content types that have metadata.

I have been doing this to extend the metadata to include AGLS elements,
plus some additional ones of our own, which get coalesced into Subject.

>
>The goal is to integrate more metadata fields like target_audience_level, 
>client_category, etc. to be able to create nice topics specific to that 
>data and even more...
>


>
>I really searched a lot in the docs and archives and found a lot similar 
>questions but all answers just pointed to new types with properties - not 
>metadata.
>
I have attached the monkey patch we use. basically it is an init method 
in a product directory.

You will note there are some other odd bit's in the code, like the 
generation of
a unique id for every piece of content that has metadata, which get's 
regenerated
if the object is cloned.


Regards

Tim

>
>thanks for any help!
>
>regards,
>siegfried
>
>
>
>
>
>_______________________________________________
>Zope-CMF maillist  -  Zope-CMF@zope.org
>http://lists.zope.org/mailman/listinfo/zope-cmf
>
>See http://www.zope.org/Products/PTK/Tracker for bug reports and feature requests
>



--------------040403030808000300090105
Content-Type: text/plain;
 name="__init__.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="__init__.py"


#"""Extend CMF DublinCore to include AGLS and PFA MetaData"""
import string
from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl
from Products.CMFCore.PortalContent import PortalContent
from Products.CMFDefault.utils import tuplize, semi_split,_dtmldir
from AccessControl import ClassSecurityInfo
from Products.CMFCore import CMFCorePermissions
from Globals import InitializeClass, DTMLFile
from DateTime.DateTime import DateTime
from Products.CMFCore.WorkflowCore import WorkflowAction
from Products.CMFCore.PortalContent import PortalContent
from Products.CMFCore.CMFCorePermissions import AccessContentsInformation
from Globals import InitializeClass
from OFS.SimpleItem import SimpleItem
from Acquisition import aq_base
import re

from zLOG import LOG, INFO, ERROR, TRACE, BLATHER




security = ClassSecurityInfo()

#Creator      -----------------------------------------------------
security.declarePublic( 'Creator' )
def Creator( self ):
    '''DC element - Creator'''
    try:
        return self.creator
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setCreator' )
def setCreator( self, creator ):
    '''DC element - Creator'''
    if creator:
        self.creator = creator
    if not creator and not self.creator:
        owner = self.getOwner()
        if hasattr( owner, 'getUserName' ):
            self.creator = owner.getUserName()

#Jurisdiction -----------------------------------------------------
security.declarePublic( 'Jurisdiction' )
def Jurisdiction( self ):
    '''AGLS element - Jurisdiction'''
    try:
        return self.jurisdiction
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setJurisdiction' )
def setJurisdiction( self, jurisdiction ):
    '''AGLS element - Jurisdiction'''
    self.jurisdiction = jurisdiction


#Location -----------------------------------------------------
security.declarePublic( 'Location' )
def Location( self ):
    '''AGLS element - Location'''
    try:
        return self.location
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setLocation' )
def setLocation( self, location ):
    '''AGLS element - Location'''
    self.location = location

#Relation -----------------------------------------------------
security.declarePublic( 'Relation' )
def Relation( self ):
    '''AGLS element - Relation'''
    try:
        return self.relation
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setRelation' )
def setRelation( self, relation ):
    "AGLS element - Relation"
    self.relation = relation

#Source -----------------------------------------------------
security.declarePublic( 'Source' )
def Source( self ):
    '''AGLS element - Source'''
    try:
        return self.source
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setSource' )
def setSource( self, source ):
    '''AGLS element - Source'''
    self.source = source

#Function -----------------------------------------------------
security.declarePublic( 'Function' )
def Function( self ):
    '''AGLS element - Function'''
    try:
        return self.function
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setFunction' )
def setFunction( self, function ):
    '''AGLS element - Function'''
    self.function = function

#Audience -----------------------------------------------------
security.declarePublic( 'Audience' )
def Audience( self ):
    '''AGLS element - Audience'''
    try:
        return self.audience
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setAudience' )
def setAudience( self, audience ):
    '''AGLS element - Audience'''
    self.audience = audience

#Mandate -----------------------------------------------------
security.declarePublic( 'Mandate' )
def Mandate( self ):
    '''AGLS element - Mandate'''
    try:
        return self.mandate
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setMandate' )
def setMandate( self, mandate ):
    '''AGLS element - Audience'''
    self.mandate = mandate
   
#SubjectMedium -----------------------------------------------------
security.declarePublic( 'SubjectMedium' )
def SubjectMedium( self ):
    '''Designing Futures element - SubjectMedium'''
    try:
        return self.subject_medium
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setSubjectMedium' )
def setSubjectMedium( self, subject_medium ):
    '''Designing Futures element - SubjectMedium'''
    LOG('ETIMetadataExtensions.setSubjectMedium',INFO,  subject_medium)
    self.subject_medium = tuplize('subject_medium',subject_medium)

#SubjectColour -----------------------------------------------------
security.declarePublic( 'SubjectColour' )
def SubjectColour( self ):
    '''Designing Futures element - SubjectColour'''
    try:
        return self.subject_colour
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setSubjectColour' )
def setSubjectColour( self, subject_colour ):
    '''Designing Futures element - SubjectColour'''
    self.subject_colour = tuplize( 'subject_colour', subject_colour)


#SubjectMatter -----------------------------------------------------
security.declarePublic( 'SubjectMatter' )
def SubjectMatter( self ):
    '''Designing Futures element - SubjectMatter'''
    try:
        return self.subject_matter
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setSubjectMatter' )
def setSubjectMatter( self, subject_matter ):
    '''Designing Futures element - SubjectMatter'''
    self.subject_matter = tuplize( 'subject_matter', subject_matter)

#SubjectMedium -----------------------------------------------------
security.declarePublic( 'SubjectFocus' )
def SubjectFocus( self ):
    '''Designing Futures element - SubjectFocus'''
    try:
        return self.subject_focus
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setSubjectFocus' )
def setSubjectFocus( self, subject_focus ):
    '''Designing Futures element - SubjectFocus'''
    self.subject_focus = tuplize( 'subject_focus', subject_focus)

#SubjectDimensions -----------------------------------------------------
security.declarePublic( 'SubjectDimensions' )
def SubjectDimensions( self ):
    '''Designing Futures element - SubjectFocus'''
    try:
        return self.subject_dimensions
    except:
        return ""


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setSubjectDimensions' )
def setSubjectDimensions( self, subject_focus ):
    '''Designing Futures element - SubjectDimensions'''
    self.subject_dimensions = tuplize( 'subject_dimensions', subject_dimensions)

# UniqueId -----------------------------------------------------
security.declarePublic( 'UniqueId' )
def UniqueID( self ):
    '''Designing Futures element - UniqueID'''
    try:
        return self.unique_id
    except:
        return ""
    
#------------------------------------------------------------------

import md5, base64, time,  string

def ETIMakeUniqueID(id):
    ''' Generate a unique id '''
    oid=md5.new()
    oid.update(id)
    oid.update(str(time.time()))
    oid=oid.digest()
    oid=string.strip(base64.encodestring(oid))

    return 'DF_OID_'+oid.replace('/','$')

# Contributors -------------------------------------------------------


security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'setContributors' )

# Redefine setContributors so that it also builds searchableContributors so
# a textfield version of contributors is catalogued so that wild card searches
# can be performed

def Contributors(self):
    return self.contributors

def setContributors( self, contributors ):
    "Dublin Core element - additional contributors to resource"
    # XXX: fixme
    self.contributors = tuplize('contributors', contributors, semi_split)
    bad_chars = r'[^A-Za-z0-9_]'
    searchableContributors = []
    for a in self.contributors:
        searchableContributors.append(string.join(re.split(bad_chars,string.rstrip(re.sub(bad_chars,' ',a))),'_'))
    self.searchableContributors = string.join(searchableContributors,' ')
        
security.declarePublic( 'searchableContent' )
def searchableContributors( self ):
    try:
        return self.searchableContributors
    except:
        self.searchableContributors = ''
        return self.searchableContributors

# --------------------------------------------------------------------
def manage_afterClone(self,obj):
    LOG('ETIMetadataExtensions._manage_afterClone',INFO, "object cloned [%s]" % obj.title_or_id())
    self.unique_id = ETIMakeUniqueID(obj.title_or_id())

# --------------------------------------------------------------------
# Replace init method

def __init__( self
            , title=''
            , subject=()
            , description=''
            , contributors=()
            , effective_date=None
            , expiration_date=None
            , format='text/html'
            , language='en'
            , rights=''
            , jurisdiction=''
            , location=''
            , relation=''
            , source=''
            , function=''
            , audience=''
            , mandate=''
            , subject_medium=()
            , subject_colour=()
            , subject_matter=()
            , subject_focus=()
            , subject_dimensions=''
            , creator=''
            ):
        LOG('ETIMetadataExtensions.__init__',INFO, "initialising DublinCore [%s]" % title)

        self.creation_date = DateTime()
        
        self.unique_id = ETIMakeUniqueID(title)

        self._editMetadata( title
                          , subject
                          , description
                          , contributors
                          , effective_date
                          , expiration_date
                          , format
                          , language
                          , rights
                          , jurisdiction
                          , location
                          , relation
                          , source
                          , function
                          , audience
                          , mandate
                          , subject_medium
                          , subject_colour
                          , subject_matter
                          , subject_focus
                          , subject_dimensions
                          , creator 
                            )



#
#  Management tab methods
#

security.declarePrivate( '_editMetadata' )
def _editMetadata( self
                , title=''
                , subject=()
                , description=''
                , contributors=()
                , effective_date=None
                , expiration_date=None
                , format='text/html'
                , language='en'
                , rights=''
                , jurisdiction=''
                , location=''
                , relation=''
                , source=''
                , function=''
                , audience=''
                , mandate=''
                , subject_medium=()
                , subject_colour=()
                , subject_matter=()
                , subject_focus=()
                , subject_dimensions=''
                , creator=''
                  ):
    """
        Update the editable metadata for this resource.
    """
    LOG('ETIMetadataExtensions._editMetadata',INFO,  title)
    self.setTitle( title )
    
    self.setDescription( description )
    self.setContributors( contributors )
    self.setEffectiveDate( effective_date )
    self.setExpirationDate( expiration_date )
    self.setFormat( format )
    self.setLanguage( language )
    self.setRights( rights )
    self.setJurisdiction( jurisdiction )
    self.setLocation( location )
    self.setRelation( relation )
    self.setSource( source )
    self.setFunction( function )
    self.setAudience( audience )
    self.setMandate( mandate )
    
    self.setSubjectMedium( subject_medium )
    
    self.setSubjectColour( subject_colour )
    self.setSubjectMatter( subject_matter )
    self.setSubjectFocus( subject_focus )
    self.setSubjectDimensions( subject_dimensions )
    self.setCreator( creator )

    # Aggregate Subject from the 4 subject components
    LOG('ETIMetadataExtensions._editMetadata',INFO,  subject)
    if (subject):
        self.setSubject(subject)
    else:
        self.setSubject(tuplize('subject_medium',self.SubjectMedium()) + tuplize('subject_colour',self.SubjectColour()) + tuplize('subject_matter',self.SubjectMatter()) + tuplize('subject_focus',self.SubjectFocus()))
    
    self.reindexObject()

security.declareProtected( CMFCorePermissions.ModifyPortalContent
                         , 'manage_editMetadata' )
def manage_editMetadata( self
                      , title
                      , subject
                      , description
                      , contributors
                      , effective_date
                      , expiration_date
                      , format
                      , language
                      , rights
                      , jurisdiction
                      , location
                      , relation
                      , source
                      , function
                      , audience
                      , mandate
                      , subject_medium
                      , subject_colour
                      , subject_matter
                      , subject_focus
                      , subject_dimensions
                      , creator
                      , REQUEST
                        ):
    """
        Update metadata from the ZMI.
    """
    LOG('ETIMetadataExtensions.editMetaData',INFO, "about to call _editMetadata [%s]" % subject)
    self._editMetadata(title
                      , subject
                      , description
                      , contributors
                      , effective_date
                      , expiration_date
                      , format
                      , language
                      , rights
                      , jurisdiction
                      , location
                      , relation
                      , source
                      , function
                      , audience
                      , mandate
                      , subject_medium
                      , subject_colour
                      , subject_matter
                      , subject_focus
                      , creator
                        )
    
                     
    REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
                            + '/manage_metadata'
                            + '?manage_tabs_message=Metadata+updated.' )

security.declareProtected( CMFCorePermissions.ModifyPortalContent
                             , 'editMetadata' )
editMetadata = WorkflowAction(_editMetadata)



security.declarePublic( 'getMetadataHeaders' )
def getMetadataHeaders( self ):
    """
        Return RFC-822-style headers.
    """
    hdrlist = []
    hdrlist.append( ( 'Creator', self.Creator() ) )
    hdrlist.append( ( 'Title', self.Title() ) )
    hdrlist.append( ( 'Subject', string.join( self.Subject() ) ) )
    hdrlist.append( ( 'Publisher', self.Publisher() ) )
    hdrlist.append( ( 'Description', self.Description() ) )
    hdrlist.append( ( 'Contributors', string.join( self.Contributors() ) ) )
    hdrlist.append( ( 'Effective_date', self.EffectiveDate() ) )
    hdrlist.append( ( 'Expiration_date', self.ExpirationDate() ) )
    hdrlist.append( ( 'Type', self.Type() ) )
    hdrlist.append( ( 'Format', self.Format() ) )
    hdrlist.append( ( 'Language', self.Language() ) )
    hdrlist.append( ( 'Rights', self.Rights() ) )
    hdrlist.append( ( 'Jurisdiction', self.Jurisdiction() ) )
    hdrlist.append( ( 'Location', self.Location() ) )
    hdrlist.append( ( 'Relation', self.Relation() ) )
    hdrlist.append( ( 'Source', self.Source() ) )
    hdrlist.append( ( 'Function', self.Function() ) )
    hdrlist.append( ( 'Audience', self.Audience() ) )
    hdrlist.append( ( 'Mandate', self.Mandate() ) )
                     
    return hdrlist

# New meta data elements

DefaultDublinCoreImpl.Jurisdiction = Jurisdiction
DefaultDublinCoreImpl.setJurisdiction = setJurisdiction
DefaultDublinCoreImpl.Location = Location
DefaultDublinCoreImpl.setLocation = setLocation
DefaultDublinCoreImpl.Relation = Relation
DefaultDublinCoreImpl.setRelation = setRelation
DefaultDublinCoreImpl.Source = Source
DefaultDublinCoreImpl.setSource = setSource
DefaultDublinCoreImpl.Function = Function
DefaultDublinCoreImpl.setFunction = setFunction
DefaultDublinCoreImpl.Audience = Audience
DefaultDublinCoreImpl.setAudience = setAudience
DefaultDublinCoreImpl.Mandate = Mandate
DefaultDublinCoreImpl.setMandate = setMandate
DefaultDublinCoreImpl.SubjectMedium = SubjectMedium
DefaultDublinCoreImpl.setSubjectMedium = setSubjectMedium
DefaultDublinCoreImpl.SubjectColour = SubjectColour
DefaultDublinCoreImpl.setSubjectColour = setSubjectColour
DefaultDublinCoreImpl.SubjectMatter = SubjectMatter
DefaultDublinCoreImpl.setSubjectMatter = setSubjectMatter
DefaultDublinCoreImpl.SubjectFocus = SubjectFocus
DefaultDublinCoreImpl.setSubjectFocus = setSubjectFocus
DefaultDublinCoreImpl.SubjectDimensions = SubjectDimensions
DefaultDublinCoreImpl.setSubjectDimensions = setSubjectDimensions

DefaultDublinCoreImpl.UniqueID = UniqueID

PortalContent.manage_afterClone = manage_afterClone


# rebind the methods
DefaultDublinCoreImpl.setContributors = setContributors
DefaultDublinCoreImpl.Contributors = Contributors
DefaultDublinCoreImpl.getMetadataHeaders = getMetadataHeaders
DefaultDublinCoreImpl.__init__ = __init__
DefaultDublinCoreImpl._editMetadata = _editMetadata
DefaultDublinCoreImpl.manage_editMetadata = manage_editMetadata
DefaultDublinCoreImpl.editMetadata = editMetadata
DefaultDublinCoreImpl.Creator = Creator
DefaultDublinCoreImpl.setCreator = setCreator

# Do we need this??
security.declareProtected(AccessContentsInformation, 'objectItems')
def objectItems(self):
    """
    since 'talkback' is the only opaque item on content
    right now, I will return that. Should be replaced with
    a list of tuples for every opaque item!
    """
    if hasattr( aq_base( self ), 'talkback' ):
        talkback = self.talkback
        if talkback is not None:
            return ((talkback.id, talkback),)
    else:
        return []

PortalContent.objectItems = objectItems

--------------040403030808000300090105--