Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/batching.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/batching.py	(revision 12249)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/batching.py	(revision 12249)
@@ -0,0 +1,111 @@
+## $Id$
+##
+## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+"""Batch processing components for document objects.
+
+Batch processors eat CSV files to add, update or remove large numbers
+of certain kinds of objects at once.
+"""
+import grok
+import unicodecsv as csv  # XXX: csv ops should move to dedicated module.
+from time import time
+from datetime import datetime
+from zope.i18n import translate
+from zope.interface import Interface
+from zope.schema import getFields
+from zope.component import queryUtility, getUtility, createObject
+from zope.event import notify
+from zope.catalog.interfaces import ICatalog
+from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
+from waeup.ikoba.interfaces import (
+    IBatchProcessor, FatalCSVError, IObjectConverter, IUserAccount,
+    IObjectHistory, IGNORE_MARKER)
+from waeup.ikoba.interfaces import IIkobaUtils
+from waeup.ikoba.interfaces import MessageFactory as _
+from waeup.ikoba.documents.interfaces import IPDFDocument, IHTMLDocument
+from waeup.ikoba.utils.batching import BatchProcessor
+
+
+class PDFDocumentProcessor(BatchProcessor):
+    """A batch processor for IPDFDocument objects.
+    """
+    grok.implements(IBatchProcessor)
+    grok.provides(IBatchProcessor)
+    grok.context(Interface)
+    util_name = 'pdfdocumentprocessor'
+    grok.name(util_name)
+
+    name = _('Public PDF Document Processor')
+    iface = IPDFDocument
+
+    location_fields = ['document_id',]
+    factory_name = 'waeup.PDFDocument'
+
+    mode = None
+
+    def parentsExist(self, row, site):
+        return 'documents' in site.keys()
+
+    def entryExists(self, row, site):
+        return row['document_id'] in site['documents'].keys()
+
+    def getParent(self, row, site):
+        return site['documents']
+
+    def getEntry(self, row, site):
+        if not self.entryExists(row, site):
+            return None
+        parent = self.getParent(row, site)
+        return parent.get(row['document_id'])
+
+    def addEntry(self, obj, row, site):
+        parent = self.getParent(row, site)
+        parent.addDocument(obj)
+        return
+
+    def delEntry(self, row, site):
+        document = self.getEntry(row, site)
+        parent = self.getParent(row, site)
+        if document is not None:
+            grok.getSite().logger.info(
+                '%s - Document removed' % document.document_id)
+            del parent[document.document_id]
+        return
+
+    def updateEntry(self, obj, row, site, filename):
+        """Update obj to the values given in row.
+        """
+        items_changed = super(DocumentProcessor, self).updateEntry(
+            obj, row, site, filename)
+        # Log actions...
+        location_field = self.location_fields[0]
+        grok.getSite().logger.info(
+            '%s - %s - %s - updated: %s'
+            % (self.name, filename, row[location_field], items_changed))
+        return
+
+
+class HTMLDocumentProcessor(PDFDocumentProcessor):
+    """A batch processor for IHTMLDocument objects.
+    """
+    util_name = 'htmldocumentprocessor'
+    grok.name(util_name)
+
+    name = _('Public HTML Document Processor')
+    iface = IHTMLDocument
+
+    factory_name = 'waeup.HTMLDocument'
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/export.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/export.py	(revision 12249)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/documents/export.py	(revision 12249)
@@ -0,0 +1,82 @@
+## $Id$
+##
+## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+"""Exporters for documents.
+"""
+import grok
+from zope.securitypolicy.interfaces import IPrincipalRoleMap
+from waeup.ikoba.interfaces import ICSVExporter
+from waeup.ikoba.interfaces import MessageFactory as _
+from waeup.ikoba.utils.batching import ExporterBase
+from waeup.ikoba.utils.helpers import iface_names
+from waeup.ikoba.documents.interfaces import IPDFDocument, IHTMLDocument
+
+
+class PDFDocumentExporter(grok.GlobalUtility, ExporterBase):
+    """Exporter for documents.
+    """
+    grok.implements(ICSVExporter)
+    grok.name('pdfdocuments')
+
+    #: Fieldnames considered by this exporter
+    fields = tuple(sorted(iface_names(IPDFDocument))) + (
+        'users_with_local_roles',)
+
+    #: The title under which this exporter will be displayed
+    title = _(u'Public PDF Documents')
+
+    def mangle_value(self, value, name, context=None):
+        """Hook for mangling values in derived classes
+        """
+        if name == 'users_with_local_roles':
+            value = []
+            role_map = IPrincipalRoleMap(context)
+            for local_role, user_name, setting in role_map.getPrincipalsAndRoles():
+                value.append({'user_name':user_name,'local_role':local_role})
+        return super(PDFDocumentExporter, self).mangle_value(
+            value, name, context)
+
+    def export(self, documents, filepath=None):
+        """Export `documents`, an iterable, as CSV file.
+
+        If `filepath` is ``None``, a raw string with CSV data is returned.
+        """
+        writer, outfile = self.get_csv_writer(filepath)
+        for document in documents:
+            self.write_item(document, writer)
+        return self.close_outfile(filepath, outfile)
+
+    def export_all(self, site, filepath=None):
+        """Export documents in documentscontainer into filepath as CSV data.
+
+        If `filepath` is ``None``, a raw string with CSV data is returned.
+        """
+        documents = site.get('documents', {})
+        return self.export(documents.values(), filepath)
+
+
+class HTMLDocumentExporter(PDFDocumentExporter):
+    """Exporter for documents.
+    """
+    grok.name('htmldocuments')
+
+    #: Fieldnames considered by this exporter
+    fields = tuple(sorted(iface_names(IHTMLDocument))) + (
+        'users_with_local_roles',)
+
+    #: The title under which this exporter will be displayed
+    title = _(u'Public HTML Documents')
