[Zope-CMF] Note on unique searchable ids

Raphael Ritz r.ritz@biologie.hu-berlin.de
Fri, 13 Sep 2002 10:51:54 +0200


Hi,

the issue of adding unique ids to CMF content object instances
came up again so I thought I just post 'our' solution. I decided to
extend
the 'DefaultDublinCoreImpl' since my primary motivation was to replace
the URL currently given as identifier with something that is (hopefully)

more persistent (the URL breaks as soon as an object gets moved
or something along the path gets renamed).

Raphael

(Sorry for posting the whole stuff this way and not providing a
howto on cmf.zope.org but the site seems to be down again;
the mapping is obviously site specific to us but you get the idea ;-)
-----------------------------------------------------------------------------------------

Providing portal content with a unique searchable Id

  Extended the 'DefaultDublinCoreImpl' class from the 'DublinCore.py'
modul
  in 'zopehome/Products/' to provide methods for setting, getting, and
  resetting an attribute called 'searchid' that is now available to all
  content types that inherit from 'DefaultDublinCoreImpl'. For
sourcecode see
  below.

  Wrote a Python script called 'show' (located in 'portal_skins/custom')

  to lookup individual searchids by searching the 'portal_catalog' and
to
  display the result (if nothing is found, redirects to
'context.absolute_url()'
  if exactly one is found, gets the objects view and if several are
found,
  lists them by redirecting to 'search' appropriately).

  Wrote another Python script called 'getURI' (located in
'portal_skins/custom')
  to generate a URL based on the searchid of the form
'portalhome/show/searchid'

  Added 'sid' as 'FieldIndex' to the 'portal_catalog'. If needed,
'SearchId'
  could be added as 'TextIndex' -> on 'sid' you can sort, whereas on
  'SearchId' you can use globbing.

  Where ever this id of an object should be shown, add a line like

  <p> The search Id of this document is <dtml-var SearchId missing="not
yet
   defined>.</p>

  to the corresponding 'view' or 'edit_form' methods.

  Similar for a URI

  <p> To reliably bookmark this page use <dtml-var getURI>.</p>


Source code

the additional class methods for the 'DefaultDublinCoreImpl'
-----------------------------------------------------------------------------

#
#  additional methods for the NI portal
#  Raphael Ritz, September 2002
#

    # Class variable default for an upgrade
    searchid = None

    security.declarePublic( 'SearchId' )
    def SearchId( self ):
        """
            used for Dublin Core element Identifier as URI
        """
        return self.sid()

    security.declarePublic( 'sid' )
    def sid( self ):
        """
            searchable id; generated if first needed
            used for the Dublin Core element identifier
            as URI.
        """
        searchid = self.searchid
        if searchid is None:
            # Upgrade.
            searchid = self._setSid()
            self.searchid = searchid
            try:
                self.reindexObject(idxs=['sid'])
            except:                            # for backwards
compatibility
                self.reindexObject()
        return searchid

    security.declareProtected( CMFCorePermissions.ManagePortal
                             , 'resetSid' )
    def resetSid( self ):
        """
            reset searchid to None (utility function for testing and
debugging)
        """
        self.searchid = None


    security.declarePrivate( '_setSid' )
    def _setSid( self ):
        metaTypeMapping = {'Database': 'DB',
                           'Portal External File': 'EF',
                           'Discussion Item': 'DI',
                           'Document': 'DO',
                           'ExternalFile': 'EF',
                           'Favorite': 'FA',
                           'UL File': 'UF',
                           'Portal Folder': 'PF',
                           'Portal Image': 'IM',
                           'Institute': 'IN',
                           'Journal': 'JO',
                           'Link': 'LI',
                           'CMF MailMessage': 'MM',
                           'NeuroEvent': 'NE',
                           'News Item': 'NI',
                           'Person': 'PE',
                           'Preprint': 'PR',
                           'Research Group': 'RG',
                           'Software': 'SW',
                           'Threaded Discussion': 'TD',
                           'Portal Topic': 'PT',
                           'WebResource': 'WR'
            }
        searchid = ''
        if self.meta_type in metaTypeMapping.keys():
            searchid = metaTypeMapping[self.meta_type]
        else:
            searchid = 'NS'

        searchid = searchid + self.CreationDate()
        # clean the string
        allowedChar = string.letters + string.digits
        tmp = ''
        for c in searchid:
            if c in allowedChar: tmp += c
        searchid = tmp
        # check whether this is unique using the portal_catalog
        counter = 0
        while self.portal_catalog.searchResults(sid = searchid) :
            counter += 1
            searchid = tmp + str(counter)
        return searchid

-------------------------------------------------------------------------------

The Python script 'show' in 'portal_skins/custom'

-------------------------------------------------------------------------------

## Script (Python) "show"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=lookup the trailing sid
##
# queries the portal_catalog for sids given as subpath if present
# redirects to homepage/the items view/search if 0/1/more items
# were found (maybe home should be replaced by something else?)

request = container.REQUEST
RESPONSE =  request.RESPONSE

target = traverse_subpath

if target:
    results = context.portal_catalog.searchResults(sid=target)
else:
    results = []

hits = len(results)

if not hits:
    context.REQUEST.RESPONSE.redirect('%s' % ( context.absolute_url() ))

elif hits == 1:
    context.REQUEST.RESPONSE.redirect('%s' % ( results[0].getURL() ))

else:
    searchpar = ''
    for item in target:
        searchpar = searchpar + '&sid=' + item
    searchstring = 'search?' + searchpar[1:]
    context.REQUEST.RESPONSE.redirect('%s/%s' % ( context.absolute_url()

                                                , searchstring ))

------------------------------------------------------------------------------

The Python script 'getURI' in 'portal_skins/custom'

------------------------------------------------------------------------------

## Script (Python) "getURI"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=based on SearchId
##
# generates an object URL based on the persistent
# searchable identifier SearchId

target = "%s/show/%s" % (container.absolute_url(), context.SearchId())
return target

--------------------------------------------------------------------------------