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

folder.py

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

"""BibliographyFolder main class"""

import string, sys, re
from urllib import quote
from types import StringType
from DocumentTemplate import sequence
from AccessControl import ClassSecurityInfo
from Acquisition import Acquirer
from Products.CMFCore.permissions import View,\
     ModifyPortalContent, AddPortalContent, ManageProperties
from Products.CMFCore.utils import getToolByName

try:
    from Products.LinguaPlone.public import *
except ImportError:
    from Products.Archetypes.public import *
    
from Products.CMFBibliographyAT.config import REFERENCE_TYPES
from Products.CMFBibliographyAT.interfaces import IBibliographyExport
from Products.CMFBibliographyAT.marshall import BibtexMarshaller

from Products.CMFBibliographyAT.tool.parsers.base import EntryParseError

## a poor-mans approach to fixing unicode issues :-(
_encoding = 'utf-8'

def _encode(s):
    try:
        return s.encode(_encoding)
    except TypeError, UnicodeDecodeError:
        return s
    
def _decode(s):
    try:
        return unicode(s, _encoding)
    except TypeError, UnicodeDecodeError:
        return s

schema = BaseFolderSchema + Schema((
    TextField('intro',
        default = '',
        widget=RichWidget(label="Intro Text",
            description="Overriding the introductory text on top of "\
                        "the folder listing",
            label_msgid="label_bibfolder_intro",
            description_msgid="help_bibfolder_intro",      
            condition="python:object.portal_bibliography.allow_folder_intro",
            ),
        ),
    ))

00059 class BaseBibliographyFolder(Acquirer):
    """
    base class for containers for bibliographic references
    """

    security = ClassSecurityInfo()
    
    filter_content_types = 1
    allowed_content_types = REFERENCE_TYPES
    content_icon = 'bib_folder.png'

    author_urls = {}

    schema = schema

    _at_rename_after_creation = True
    __implements__ = (IBibliographyExport,
                     )

    actions = (
        {
        'id'           : 'import',
        'name'         : 'Import',
        'action'       : 'string:${object_url}/bibliography_importForm',
        'permissions'  : (AddPortalContent,),
        },
        {
        'id'           : 'defaults',
        'name'         : 'Defaults',
        'action'       : 'string:${object_url}/bibliography_defaultsForm',
        'permissions'  : (ModifyPortalContent,),
        },
        {
        'id'          : 'local_roles',
        'name'        : 'Sharing',
        'action'      : 'string:${object_url}/folder_localrole_form',
        'permissions' : (ManageProperties,),
        },
        )

    # enable import
    security.declareProtected(ModifyPortalContent, 'buildReportLine')
00101     def buildReportLine(self, import_status, entry, url=None, relations=None):
        """ format a line to be added in the import report """

        if entry.has_key('authors'):
            authors = entry.get('authors')
            authors_list = []
            for author in authors:
                firstname = author.get('firstname')
                middlename = author.get('middlename', '')
                lastname = author.get('lastname')
                authors_list.append('%s %s %s' %(firstname,
                                                 middlename,
                                                 lastname))
            ref_authors = ', '.join(authors_list)
        else:
            ref_authors = 'Anonymous'

        ref_authors = _decode(ref_authors)
        ref_title = _decode(entry.get('title'))
        for car in ['\n','\r','\t','  ','  ']:
            ref_title = ref_title.replace(car,'')
        line = u'%s - %s' % (ref_authors, ref_title)

        if entry.has_key('publication_year'):
            line = u'%s (%s)' %(line, entry.get('publication_year'))

        if import_status == 'ok':
            line = u'Successfully Imported: %s' % line
        if import_status == 'ok' and relations:
            relations = _decode(relations)
            line = u'%s (Inferred author references: %s)' \
                   % (line, relations)
        if import_status == 'ok' and url:
            line = u'%s [<a href="%s">view</a>], ' \
                   u'[<a href="%s">edit</a>]' \
                   % (line, url, url+'/edit')
        else:
            line = u'%s: %s' % (import_status, line)
        return _encode(line + '.\n')

    security.declareProtected(AddPortalContent, 'processSingleImport')
00142     def processSingleImport(self, entry, infer_references=True):
        """ called for importing a single entry """
        url = None
        relations = None
        if isinstance(entry, EntryParseError):
            return ('Failed: %s' % entry.description, 'FAILED')
        obj = None
        try:
            newid = self.cookId(entry)
            if newid and newid != "nobody1000":
                type = entry.get('publication_type', 'ArticleReference')
                if type in self.allowed_content_types:
                    self.invokeFactory(type, newid)
                    obj = getattr(self, newid)
                    obj.edit(**entry)
                    url = obj.absolute_url()
                    if obj.usesCMFMember() and infer_references:
                        relations = obj.inferAuthorReferences()
            import_status = 'ok'
        except: # for debugging
            # Remove the \n from import_status so that it all appears
            # on one line in the import_report, which keeps the count
            # the same as that reported.
            import_status = "Error type: %s. Error value: %s" \
                          % (sys.exc_info()[0],
                             sys.exc_info()[1])

        report_line = self.buildReportLine(import_status,
                                           entry,
                                           url=url,
                                           relations=relations)
        return (report_line, import_status, obj)

    security.declareProtected(AddPortalContent, 'processImport')
00176     def processImport(self, source, filename, format=None, return_obs=False):
        """ main routine to be called for importing entire files """
        # XXX This method does not seem to be called! skins/bibliography_import.cpy
        # is used instead, so far as I can tell.  We should probably remove
        # this method, or clarify things to some extent.

        # get parsed entries from the Bibliography Tool
        bibtool = getToolByName(self, 'portal_bibliography')
        entries = bibtool.getEntries(source, format, filename)

        # start building the report
        mtool  = getToolByName(self, 'portal_membership')
        member = mtool.getAuthenticatedMember()
        member_id = member.getId()
        fullname = member.getProperty('fullname', None)
        if not fullname:
            fullname = 'unknown fullname'
        import_date = self.ZopeTime()
        tmp_report = '[%s] Imported by %s (%s) from file %s:\n\n' \
                     %(import_date, member_id, fullname, filename)

        # process import for each entry
        obs = []
        for entry in entries:
            if entry.get('title'):
                if return_obs:
                    upload = self.processSingleImport(entry, return_ob=True)
                    obs.append(upload[2])
                else:
                    upload = self.processSingleImport(entry)
                tmp_report = tmp_report + upload[0]

        # finish building and write the report
        old_report = self.import_report
        report = tmp_report \
               + '='*30 + '\n' \
               + old_report
        self.manage_changeProperties(import_report=report)

        if return_obs:
            return obs
        return None

00219     def cookId(self, ref):
        """ Come up with a unique id for this folder"""
        newid = ''
        if type(ref) != type({}):
            return "nobody1000"
        if ref.has_key('pid') and ref['pid']:
            newid = ref['pid']
        elif ref.has_key('authors'):
            namepart = ref['authors'][0]['lastname']
        else:
            namepart="nobody"
        if ref.get('publication_year', None):
            yearpart = str(ref['publication_year'])
        else:
            yearpart = "1000"
        if not newid: newid = namepart + yearpart

        newid = self.clean(newid)
        while newid in self.contentIds():
            newid = self.nextId(newid)
        return newid

00241     def clean(self, text):
        """remove all charcaters not allowed in Zope ids"""
        return re.sub('[^a-zA-Z0-9-_~.]', '', text)

    def nextId(self, testid):
        letters = string.letters
        if testid[-1] in letters:
            last = letters[letters.find(testid[-1])+1]
            return testid[:-1] + last
        else:
            return testid + 'a'

    # enable management of an {author:authorurl} dictionary
    security.declareProtected(View, 'AuthorURLs')
00255     def AuthorURLs(self):
        """ accessor """
        return self.author_urls

    security.declareProtected(View, 'AuthorUrlList')
00260     def AuthorUrlList(self):
        """ list of dictionaries for editing """
        link_list = []
        keys = self.AuthorURLs().keys()
        keys.sort()
        for key in keys:
            entry = {'key':key,
                     'value':self.AuthorURLs()[key],
                     }
            link_list.append(entry)
        return link_list
    
    security.declareProtected(ModifyPortalContent, 'setAuthorURLs')
00273     def setAuthorURLs(self, dict):
        """ mutator (entire dictionary) """
        self.author_urls = dict
        
    security.declareProtected(ModifyPortalContent, 'addAuthorURLs')
00278     def addAuthorURL(self, key='dummy', value=None):
        """ new entry """
        self.author_urls[key] = value

    security.declareProtected(ModifyPortalContent, 'processAuthorUrlList')
00283     def processAuthorUrlList(self, link_list):
        """ creates the links dictionary from the list entries """

        link_dict = {}

        for link in link_list:
            link_dict[link['key']] = link['value']

        self.setAuthorURLs(link_dict)

    # enable look-up by authors
    security.declareProtected(View, 'getPublicationsByAuthors')
00295     def getPublicationsByAuthors(self, search_list, and_flag = 0):
        """
        returns a filtered list of content values matching
        the publications of the specified authors

        authors MUST be specified by first initial plus lastname
        like in 'J Foo' or ['J Foo', 'J Bar'] 
        """
        if type(search_list) == StringType:
            search_list = [search_list]

        result = []
        for value in self.contentValues():
            author_list = []
            for author in value.getAuthorList():
                entry = author.get('firstname', ' ')[0] \
                        + ' ' \
                        + author.get('lastname', '')
                author_list.append(entry.strip())

            for author in search_list:
                if author in author_list and value not in result:
                    result.append(value)

            if and_flag:
                if value in result:
                    for author in search_list:
                        if author not in author_list:
                            result.remove(value)
        return result

    # enable ranking of publications
    security.declareProtected(View, 'Top')
00328     def Top(self, number=None, order=None, explicit=0):
        """
        Returns all ranked entries in order of their ranking.
        If number is set, returns the top 'number' reference objects
        (or all if 'number' is greater than the number of ranked entries).
        If 'explicit' is set, only returns the explicitly ranked
        entries, otherwise the default ordering is used
        """
        if number:
            number = int(number) 
        top = []
        entries = self.listFolderContents()
        for entry in entries:
            if getattr(entry, 'rank', None):
                top.append((entry.rank, entry))
        top.sort()
        result = [t[1] for t in top]

        if result and number:
            return result[:number]
        elif result:
            return result

        if not explicit:
            if number:
                return self.defaultOrder(order)[:number]
            else:
                return self.defaultOrder(order)

        return []

00359     def defaultOrder(self, order=None):
        """
        The entries in default ordering:
        If no orer is specified,
        (('publication_year', 'cmp', 'desc'),('Authors', 'nocase', 'asc'))
        is used.
        Order must be formated to match the
        'DocumentTemplate.sequence' syntax
        """
        raw_list = self.listFolderContents()

        if order is None:
            sort_on = (('publication_year', 'cmp', 'desc'),
                       ('Authors', 'nocase', 'asc'))
        else:
            sort_on = order

        return sequence.sort(raw_list, sort_on)

    security.declareProtected(ModifyPortalContent, 'setTop')
00379     def setTop(self, ids=[]):
        """ sets the publication ranking """
        rank = 0
        self._resetRanking()

        for id in ids:
            obj = getattr(self, id, None)
            if obj:
                rank += 1
                obj.rank = rank

00390     def _resetRanking(self):
        """ resets the ranking of reference objects """
        for obj in self.contentValues():
            obj.rank = None

    ## play nice with FTP and WebDAV

    security.declareProtected(View, 'manage_FTPget')
00398     def manage_FTPget(self):
        """render all references as one BibTeX file"""
        bibtool = getToolByName(self, 'portal_bibliography')
        return bibtool.render(self, 'bib')

    security.declareProtected(View, 'content_type')
00404     def content_type(self):
        """
        rely on our default rendering 'applicatio/x-bibtex'
        """
        return 'application/x-bibtex'

    security.declareProtected(View, 'get_size')
00411     def get_size(self):
        """ The number of content objects in this folder """
        return len(self.contentIds())

    security.declareProtected(ModifyPortalContent, 'PUT_factory')
00416     def PUT_factory(self, name, typ, body):
        """
        Handle HTTP and FTP PUT requests

        What we need to do here is to return something that later
        can be called 'PUT' upon as we are in a bibfolder already
        Also temporarily allow nesting of bibfodlers
        """
        # temporarily allow bibfolders within bibfolders
        types_tool = getToolByName(self, 'portal_types')
        for ptype in ['BibliographyFolder','LargeBibliographyFolder']:
            fti = types_tool[ptype]
            fti.filter_content_types = 0
        return BibliographyFolder(name)  
        

    security.declareProtected(ModifyPortalContent, 'PUT')
00433     def PUT(self, REQUEST, RESPONSE):
        """ Handle HTTP and FTP PUT requests """
        raw = REQUEST.get('BODY')
        id = self.getId()
        parent = self.aq_inner.aq_parent
        if IBibliographyExport in parent.__implements__:
            target_folder = parent
            try:
                target_folder.manage_delObjects([id])
            except AttributeError:
                pass
            # don't forget to impose our restriction again
            types_tool = getToolByName(self, 'portal_types')
            for ptype in ['BibliographyFolder','LargeBibliographyFolder']:
                fti = types_tool[ptype]
                fti.filter_content_types = 1
        else:
            target_folder = self
            target_folder.setTitle(id)
        target_folder.processImport(raw, id)
        RESPONSE.setStatus(204)
        target_folder.reindexObject()
        return RESPONSE

    security.declareProtected(AddPortalContent, 'logImportReport')
00458     def logImportReport(self, report):
        """Store the import report.
        """
        # finish building and write the report
        old_report = self.getProperty('import_report', '')
        report = report + '='*30 + '\n' + old_report
        self.manage_changeProperties(import_report=report)


00467 class BibliographyFolder(BaseBibliographyFolder, BaseFolder):
    """container for bibliographic references
    """
    archetype_name = "Bibliography Folder"

    __implements__ = BaseBibliographyFolder.__implements__ + \
                     BaseFolder.__implements__

    import_report = ''
    _properties= BaseFolder._properties + \
                 ({'id':'import_report', 'type': 'text', 'mode': 'w'},)



00481 class LargeBibliographyFolder(BaseBibliographyFolder, BaseBTreeFolder):
    """container for bibliographic references
    """
    archetype_name = "Large Bibliography Folder"
    global_allow = 0

    __implements__ = BaseBibliographyFolder.__implements__ + \
                     BaseBTreeFolder.__implements__
    import_report = ''
    _properties= BaseBTreeFolder._properties + \
                 ({'id':'import_report', 'type': 'text', 'mode': 'w'},)


registerType(BibliographyFolder)
registerType(LargeBibliographyFolder)

Generated by  Doxygen 1.6.0   Back to index