Logo Search packages:      
Sourcecode: zope-cmfbibliographyat version File versions  Download package

bibliography.py

##########################################################################
#                                                                        #
#           copyright (c) 2003, 2005 ITB, Humboldt-University Berlin     #
#           written by: Raphael Ritz, r.ritz@biologie.hu-berlin.de       #
#                                                                        #
##########################################################################

"""BibliographyTool main class"""

# Python stuff
import re

# Zope stuff
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo, ModuleSecurityInfo
from OFS.Folder import Folder

# CMF stuff
from Products.CMFCore.Expression import Expression
from Products.CMFCore.ActionInformation import ActionInformation
from Products.CMFCore.ActionProviderBase import ActionProviderBase
from Products.CMFCore.CMFCorePermissions import View, ManagePortal
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.utils import UniqueObject

# My stuff ;-)
from Products.CMFBibliographyAT.tool.parsers.base import ParserFolder
from Products.CMFBibliographyAT.tool.renderers.base import RendererFolder

from bibutils import BibUtils
from migrate import migrateAuthors

# citation patterns
citations = re.compile(r'\\?cite{([\w, ]*)}')
bibitems = re.compile(r'\\?bibitem{([\w]*)}')


00038 class ImportParseError(Exception):
    """An exception to replace the use of TypeError in
    skins/bibliography/import.py as this masked true TypeErrors and made
    debugging that much more difficult.
    """
    pass

# Need to be able to import this exception so that it can be caught TTW.
module_path = 'Products.CMFBibliographyAT.tool.bibliography'
security = ModuleSecurityInfo(module_path)
security.declarePublic('ImportParseError')


00051 class BibliographyTool(UniqueObject, Folder, ActionProviderBase, BibUtils):
    """Tool for managing import and export functionality
       as well as some resources of the BibliographyFolders
       and -Entries.
    """
    id = 'portal_bibliography'
    meta_type = 'Bibliography Tool'
    show_isbn_link = 0
    allow_folder_intro = 0
    support_member_references = False
    member_types = []
    sort_members_on = ''
    
    _actions = (
        ActionInformation(
            id='bibliography_view',
            title='Bibliography',
            description='Site wide; sorted by year and authors',
            action=Expression(text='string: ${portal_url}/bibliography_view'),
            permissions=(View,),
            category='portal_tabs',
            condition=None,
            visible=1,
            ),
        )

    security = ClassSecurityInfo()
    security.declareObjectProtected(View)

    manage_options = (
        (Folder.manage_options[0],)
        + ActionProviderBase.manage_options
        + Folder.manage_options[2:]
        )

    _properties = Folder._properties + (
        {'id':'allow_folder_intro',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'support_member_references',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'member_types',
         'type':'multiple selection',
         'select_variable':'getPortalTypeNames',
         'mode':'w',
         },
        {'id':'sort_members_on',
         'type':'selection',
         'select_variable':'getFieldIndexes',
         'mode':'w',
         },
        {'id':'show_isbn_link',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'preprint_servers',
         'type':'lines',
         'mode':'w',
         },
        )

    def __init__(self):
        self._setObject('Parsers', ParserFolder('Parsers', ''))
        self._setObject('Renderers', RendererFolder('Renderers', ''))


    security.declarePublic('getReferenceTypes')
00121     def getReferenceTypes(self):
        """
        returns a list with the names (meta types) of the
        currently allowed reference types of a BibliographyFolder
        """
        types_tool = getToolByName(self, 'portal_types')
        bibfolder_fti = getattr(types_tool, 'BibliographyFolder', None)
        if not bibfolder_fti: # intermediate bug fix
            bibfolder_fti = getattr(types_tool, 'Bibliography Folder', None)
        if not bibfolder_fti:
            raise TypeError, \
                  "BibliographyFolder not registered with the types tool."
        # we don't want to return the folders here
        # (they are allowed types for FTP/WebDAV support)
        allowed = bibfolder_fti.allowed_content_types
        blacklist = ['BibliographyFolder','LargeBibliographyFolder']
        return [pt for pt in allowed if pt not in blacklist]

    security.declarePublic('getImportFormatNames')
00140     def getImportFormatNames(self):
        """
        returns a list with the names of the supported import formats
        """
        return [parser.getFormatName() \
                for parser in self.Parsers.objectValues()]

    security.declarePublic('getImportFormatExtensions')
00148     def getImportFormatExtensions(self):
        """
        returns a list with the file name extensions
        of the supported import formats
        """
        return [parser.getFormatExtension() \
                for parser in self.Parsers.objectValues()]

    security.declarePublic('getExportFormatNames')
00157     def getExportFormatNames(self):
        """
        returns a list with the names of the supported export formats
        """
        return [renderer.getFormatName() \
                for renderer in self.Renderers.objectValues()]

    security.declarePublic('getExportFormatExtensions')
00165     def getExportFormatExtensions(self):
        """
        returns a list with the file name extensions
        of the supported export formats
        """
        return [renderer.getFormatExtension() \
                for renderer in self.Renderers.objectValues()]

    security.declarePublic('getExportFormats')
00174     def getExportFormats(self):
        """
        returns a list of (name, extension) tuples
        of the supported export formats
        """
        return zip(self.getExportFormatNames(),
                   self.getExportFormatExtensions())
    
    security.declareProtected(View, 'render')
00183     def render(self, entry, format):
        """
        renders a BibliographyEntry object in the specified format
        """
        renderer = self.getRenderer(format)

        if renderer:
            return renderer.render(entry)
        else:
            return None

    security.declareProtected(View, 'getRenderer')
00195     def getRenderer(self, format):
        """
        returns the renderer for the specified format
        first looks for a renderer with the 'format' name
        next looks for a renderer with the 'format' extension
        """
        for renderer in self.Renderers.objectValues():
            if format.lower() == renderer.getFormatName().lower():
                return renderer
            if format.lower() == renderer.getFormatExtension().lower():
                return renderer
        return None

    security.declareProtected(View, 'getEntries')
00209     def getEntries(self, source, format, file_name=None):
        """
        main routine to be called from BibliographyFolders
        returns a list with the parsed entries
        """
        source = self.checkEncoding(source)

        format = self.checkFormat(source, format, file_name)

        parser = self.getParser(format)

        if parser:
            return parser.getEntries(source)
        else:
            return "No parser for '%s' available." % format

    security.declareProtected(View, 'getParser')
00226     def getParser(self, format):
        """
        returns the parser for the specified format
        first looks for a parser with the 'format' name
        next looks for a parser with the 'format' extension
        """
        for parser in self.Parsers.objectValues():
            if format.lower() == parser.getFormatName().lower():
                return parser
            elif format.lower() == parser.getFormatExtension().lower():
                return parser
        return None ## rr: we probabliy should raise an error here 

00239     def checkEncoding(self, source):
        """
        Make sure we have utf encoded text
        """
        mimetypesTool = getToolByName(self, 'mimetypes_registry', None)
        encoding = mimetypesTool and mimetypesTool.guess_encoding(source) \
                   or 'utf-8'
        fallbackEncoding = mimetypesTool and \
                           hasattr (mimetypesTool, 'fallbackEncoding') and \
                           mimetypesTool.fallbackEncoding or 'latin1'
        try:
            source = unicode(source, encoding)
        except UnicodeDecodeError:
            source = unicode(source, fallbackEncoding)
        return source.encode('utf-8')

00255     def checkFormat(self, source, format, file_name):
        """
        plausibility test whether 'source' has the 'format' specified
        if not it tries to infer the format from the 'file_name'
        raises an error if both fail
        """
        ok = 0
        if format:
            parser = self.getParser(format)
            ok = parser.checkFormat(source)
        if not ok and file_name:
            format = self.guessFormat(file_name)
        if format:
            return format
        else:
            raise  ImportParseError, "%s Parser's 'checkFormat' and " \
                  "guessing the format from the file name '%s' failed." \
                  % (format, file_name)

00274     def guessFormat(self, file_name):
        """
        Checks whether the file_name extension is
        among the supported ones.

        returns the respective format name if found 
        returns None otherwise
        """
        extension = file_name.split('.')[-1].lower()
        if extension in [ext.lower()
                         for ext in self.getImportFormatExtensions()]:
            for parser in self.Parsers.objectValues():
                if extension == parser.getFormatExtension().lower():
                    return parser.getFormatName()
        return None

    # support BibTeX style citations in text
    security.declarePublic('link_citations')
00292     def link_citations(self, text=""):
        """
        replace all citations with links to their references
        
        the pattern is 'cite{key}' or 'cite{key1,key2}
        If 'key' matches the id of a reference the pattern
        will be replaced with inline link(s) to this reference(s).
        Otherwise the pattern is replaced with the key.

        Using the pattern 'bibitem{key}' you can include the
        full reference like shown in other bibliographic listings
        (authors (year) title linked to entry, source) 
        """
        text = citations.sub(self._inline_links, text)
        return bibitems.sub(self._bibitem_links, text)

    def _inline_links(self, hit):
        keys = [k.strip() for k in hit.group(1).split(',')]
        results = []
        catalog = getToolByName(self, 'portal_catalog')
        encoding = self.getProperty('default_charset') or 'utf-8'
        for key in keys:
            brains = catalog(getId = key,
                             portal_type = self.getReferenceTypes()
                             )
            if brains:
                url = brains[0].getURL()
                label = self._encode(brains[0].citationLabel, encoding) \
                        or 'no label'
                link = '<a href="%s">%s</a>' % (url, label)
                results.append(link)
            else:
                results.append(key)
        return '; '.join(results)

    def _bibitem_links(self, hit):
        key = hit.group(1).strip()
        catalog = getToolByName(self, 'portal_catalog')
        brains = catalog(getId = key,
                         portal_type = self.getReferenceTypes()
                         )
        if brains:
            brain = brains[0]
            encoding = self.getProperty('default_charset') or 'utf-8'

            authors = self._encode(brain.Authors, encoding)
            year = self._encode(brain.publication_year, encoding)
            title = self._encode(brain.Title, encoding)
            source = self._encode(brain.Source, encoding)
            url = brain.getURL()

            link = '%s (%s) <a href="%s">%s</a>, %s' % \
                   (authors, year, url, title, source)
            return link
        else:
            return key

    def _encode(self, value, encoding='utf-8'):
        return isinstance(value, unicode) and \
               value.encode(encoding) or value

    # migrate data from old to new schema
00354     def updateAuthorSchema(self):
        """restore the old author data to be available to the
        new author schema"""
        ct = getToolByName(self, 'portal_catalog')
        brains = ct(portal_type=self.getReferenceTypes())
        for brain in brains:
            obj = brain.getObject()
            authors = getattr(obj, 'publication_authors', None)
            if authors is not None:
                migrateAuthors(obj)
                # obj.setAuthors(authors)
                # del obj.publication_authors

00367     def needsUpgrade(self):
        """Returns True if one of the first 5 bibitems found
        has the old 'publication_authors' attribute; called
        by the installer to figure out whether a schema update
        is needed."""
        ct = getToolByName(self, 'portal_catalog')
        brains = ct(portal_type=self.getReferenceTypes())
        if not brains:
            return False
        for brain in brains[:5]:
            if getattr(brain.getObject(), 'publication_authors', False):
                return True
        return False

    ## XXX just to have this in trusted code
00382     def getSortedMemberIds(self):
        """Return the ids like the membership tool but sorted (by id)"""
        mt = getToolByName(self, 'portal_membership')
        ids = mt.listMemberIds()
        ids.sort()
        return ids
    
    security.declareProtected(View, 'getFieldIndexes')
00390     def getFieldIndexes(self):
        """returns a list of all field index ids from teh catalog"""
        catalog = getToolByName(self, 'portal_catalog')
        indexes = catalog.indexes()
        field_indexes = [i for i in indexes \
                         if catalog.Indexes[i].meta_type == 'FieldIndex']
        field_indexes.sort()
        return field_indexes


InitializeClass(BibliographyTool)

Generated by  Doxygen 1.6.0   Back to index