diff --git a/browser/concept_macros.pt b/browser/concept_macros.pt
index 2c1ee51..046f532 100644
--- a/browser/concept_macros.pt
+++ b/browser/concept_macros.pt
@@ -63,6 +63,11 @@
+
+ *****
+
+
+
>> import os
- >>> from loops.external.tests import dataDirectory
- >>> loader.resourceDirectory = os.path.join(dataDirectory, 'import')
-
>>> input = ("resource('doc04.txt', u'Document 4', 'textdocument')\n"
... "resourceRelation('myquery', 'doc04.txt', 'standard')")
>>> reader = PyReader()
>>> elements = reader.read(input)
+
+ >>> import os
+ >>> from loops.external.tests import dataDirectory
+ >>> loader = Loader(loopsRoot, os.path.join(dataDirectory, 'import'))
>>> loader.load(elements)
>>> sorted(resources)
diff --git a/external/base.py b/external/base.py
index 1541562..310f3ec 100644
--- a/external/base.py
+++ b/external/base.py
@@ -34,6 +34,7 @@ from zope.traversing.api import getName, getParent
from cybertools.composer.interfaces import IInstance
from cybertools.composer.schema.interfaces import ISchemaFactory
+from cybertools.external.base import BaseLoader
from cybertools.typology.interfaces import IType
from loops.common import adapted
from loops.external.interfaces import ILoader, IExtractor, ISubExtractor
@@ -70,25 +71,22 @@ class Base(object):
return self.concepts.getTypePredicate()
-class Loader(Base, SetupManager):
+class Loader(Base, BaseLoader, SetupManager):
implements(ILoader)
def __init__(self, context, resourceDirectory=None):
- super(Loader, self).__init__(context, resourceDirectory)
+ #super(Loader, self).__init__(context, resourceDirectory)
+ Base.__init__(self, context, resourceDirectory)
+ BaseLoader.__init__(self, context)
self.logger = StringIO()
#self.logger = sys.stdout
- def load(self, elements):
- for element in elements:
- element.execute(self)
- if element.subElements is not None:
- self.load(element.subElements)
-
# TODO: care for setting attributes via Instance (Editor)
# instead of using SetupManager methods:
# def addConcept(self, ...):
+
class Extractor(Base):
implements(IExtractor)
diff --git a/integrator/README.txt b/integrator/README.txt
index 861ba87..6e25c9e 100644
--- a/integrator/README.txt
+++ b/integrator/README.txt
@@ -136,6 +136,39 @@ But if one of the referenced objects is not found any more it will be deleted.
('fullpath', {'subdirectory': '...zope'}, 'zope3.txt')
+Mail Collections
+================
+
+ >>> tType = concepts['type']
+ >>> from loops.integrator.mail.interfaces import IMailCollection, IMailResource
+ >>> tMailCollection = addObject(concepts, Concept, 'mailcollection',
+ ... title=u'Mail Collection', conceptType=tType,
+ ... typeInterface=IMailCollection)
+ >>> tMailResource = addObject(concepts, Concept, 'email',
+ ... title=u'Mail Resource', conceptType=tType,
+ ... typeInterface=IMailResource)
+
+ >>> mailColl = addObject(concepts, Concept, 'mails.user1',
+ ... title=u'My Mails (User1)', conceptType=tMailCollection)
+
+ >>> from loops.integrator.mail.collection import MailCollectionAdapter
+ >>> aMailColl = MailCollectionAdapter(mailColl)
+
+An external collection carries a set of attributes that control the access
+to the external system:
+
+ >>> (aMailColl.providerName, aMailColl.baseAddress,
+ ... aMailColl.address, aMailColl.pattern)
+ (u'imap', None, None, None)
+
+ >>> from loops.integrator.mail import testing
+
+ >>> from loops.integrator.mail.imap import IMAPCollectionProvider
+ >>> component.provideUtility(IMAPCollectionProvider(), name='imap')
+ >>> aMailColl.update()
+ *** 1 blubb
+
+
Uploading Resources with HTTP PUT Requests
==========================================
diff --git a/integrator/configure.zcml b/integrator/configure.zcml
index 00c1eac..ad412db 100644
--- a/integrator/configure.zcml
+++ b/integrator/configure.zcml
@@ -3,8 +3,7 @@
+ i18n_domain="loops">
@@ -55,5 +54,6 @@
permission="zope.Public" />
+
diff --git a/integrator/mail/__init__.py b/integrator/mail/__init__.py
new file mode 100644
index 0000000..4bc90fb
--- /dev/null
+++ b/integrator/mail/__init__.py
@@ -0,0 +1,4 @@
+"""
+$Id$
+"""
+
diff --git a/integrator/mail/collection.py b/integrator/mail/collection.py
new file mode 100644
index 0000000..89ee170
--- /dev/null
+++ b/integrator/mail/collection.py
@@ -0,0 +1,56 @@
+#
+# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
+#
+# 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
+#
+
+"""
+Concept adapter(s) for external collections, e.g. a directory in the
+file system.
+
+$Id$
+"""
+
+from zope.cachedescriptors.property import Lazy
+from zope import component
+from zope.component import adapts
+from zope.interface import implements
+
+from loops.integrator.collection import ExternalCollectionAdapter
+from loops.integrator.mail.interfaces import IMailCollection
+from loops.type import TypeInterfaceSourceList
+
+
+TypeInterfaceSourceList.typeInterfaces += (IMailCollection,)
+
+
+class MailCollectionAdapter(ExternalCollectionAdapter):
+ """ A concept adapter for accessing a mail collection.
+ May delegate access to a named utility.
+ """
+
+ implements(IMailCollection)
+
+ _adapterAttributes = ExternalCollectionAdapter._adapterAttributes + (
+ 'providerName', '_collectedObjects')
+ _contextAttributes = list(IMailCollection)
+
+ _collectedObjects = None
+
+ def getProviderName(self):
+ return getattr(self.context, '_providerName', None) or u'imap'
+ def setProviderName(self, value):
+ self.context._providerName = value
+ providerName = property(getProviderName, setProviderName)
diff --git a/integrator/mail/configure.zcml b/integrator/mail/configure.zcml
new file mode 100644
index 0000000..70b230a
--- /dev/null
+++ b/integrator/mail/configure.zcml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integrator/mail/imap.py b/integrator/mail/imap.py
new file mode 100644
index 0000000..9b154fa
--- /dev/null
+++ b/integrator/mail/imap.py
@@ -0,0 +1,88 @@
+#
+# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
+#
+# 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
+#
+
+"""
+Concept adapter(s) for external collections, e.g. a directory in the
+file system.
+
+$Id$
+"""
+
+from datetime import datetime
+from logging import getLogger
+
+from zope.app.container.interfaces import INameChooser
+from zope.cachedescriptors.property import Lazy
+from zope import component
+from zope.component import adapts
+from zope.event import notify
+from zope.lifecycleevent import ObjectModifiedEvent
+from zope.interface import implements
+from zope.traversing.api import getName, getParent
+
+from loops.common import AdapterBase, adapted
+from loops.interfaces import IResource, IConcept
+from loops.integrator.interfaces import IExternalCollectionProvider
+from loops.integrator.mail.system import IMAP4
+from loops.resource import Resource
+from loops.setup import addAndConfigureObject
+
+
+class IMAPCollectionProvider(object):
+ """ A utility that provides access to an IMAP folder.
+ """
+
+ implements(IExternalCollectionProvider)
+
+ def collect(self, client):
+ client._collectedObjects = {}
+ imap = IMAP4(client.baseAddress)
+ imap.login(client.userName, client.password)
+ mailbox = 'INBOX'
+ addr = client.address
+ if addr:
+ mailbox = mailbox + '.' + addr.replace('/', '.')
+ imap.select(mailbox)
+ type, data = imap.search(None, 'ALL')
+ for num in data[0].split():
+ type, data = imap.fetch(num, '(RFC822)')
+ externalAddress = num
+ obj = data
+ mtime = datetime.today()
+ client._collectedObjects[externalAddress] = obj
+ yield externalAddress, mtime
+
+ def createExtFileObjects(self, client, addresses, extFileTypes=None):
+ loopsRoot = client.context.getLoopsRoot()
+ container = loopsRoot.getResourceManager()
+ contentType = 'text/plain'
+ resourceType = loopsRoot.getConceptManager()['email']
+ for addr in addresses:
+ print '***', addr, client._collectedObjects[addr]
+ return []
+
+ def _dummy(self):
+ name = self.generateName(container, addr)
+ title = self.generateTitle(addr)
+ obj = addAndConfigureObject(
+ container, Resource, name,
+ title=title,
+ resourceType=extFileType,
+ contentType=contentType,
+ )
+ yield obj
diff --git a/integrator/mail/interfaces.py b/integrator/mail/interfaces.py
new file mode 100644
index 0000000..387e2b0
--- /dev/null
+++ b/integrator/mail/interfaces.py
@@ -0,0 +1,75 @@
+#
+# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
+#
+# 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
+#
+
+"""
+Integrator interfaces for email representation.
+
+$Id$
+"""
+
+from zope.interface import Interface, Attribute
+from zope import interface, component, schema
+
+from loops.integrator.interfaces import IExternalCollection
+from loops.interfaces import ITextDocument
+from loops.util import _
+
+
+# mail collections
+
+class IMailCollection(IExternalCollection):
+ """ A concept representing a collection of emails that may be
+ actively retrieved from an external system using the parameters
+ given.
+ """
+
+ providerName = schema.TextLine(
+ title=_(u'Provider name'),
+ description=_(u'The name of a utility that provides the '
+ u'external objects; default is an IMAP '
+ u'collection provider.'),
+ default=u'imap',
+ required=False)
+ baseAddress = schema.TextLine(
+ title=_(u'Host name'),
+ description=_(u'The host name part for accessing the '
+ u'external mail system, i.e. the mail server.'),
+ required=True)
+ userName = schema.TextLine(
+ title=_(u'User name'),
+ description=_(u'The user name for logging in to the mail server.'),
+ required=False)
+ password = schema.Password(
+ title=_(u'Password'),
+ description=_(u'The password for logging in to the mail server.'),
+ required=False)
+
+
+# mail resources
+
+class IMailResource(ITextDocument):
+ """ A resource adapter representing an email.
+ """
+
+ externalAddress = schema.BytesLine(
+ title=_(u'External Address'),
+ description=_(u'The full address of the email in the external '
+ u'email system.'),
+ default='',
+ missing_value='',
+ required=False)
diff --git a/integrator/mail/resource.py b/integrator/mail/resource.py
new file mode 100644
index 0000000..6def733
--- /dev/null
+++ b/integrator/mail/resource.py
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
+#
+# 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
+#
+
+"""
+Adapter for mail resources.
+
+$Id$
+"""
+
+from zope.cachedescriptors.property import Lazy
+from zope import component
+from zope.component import adapts
+from zope.interface import implements
+
+from loops.integrator.mail.interfaces import IMailResource
+from loops.resource import TextDocumentAdapter
+from loops.type import TypeInterfaceSourceList
+
+
+TypeInterfaceSourceList.typeInterfaces += (IMailResource,)
+
+
+class MailResource(TextDocumentAdapter):
+ """ A concept adapter for accessing a mail collection.
+ May delegate access to a named utility.
+ """
+
+ implements(IMailResource)
+
+ _contextAttributes = list(IMailResource)
diff --git a/integrator/mail/system.py b/integrator/mail/system.py
new file mode 100644
index 0000000..5ed6eea
--- /dev/null
+++ b/integrator/mail/system.py
@@ -0,0 +1,25 @@
+#
+# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
+#
+# 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
+#
+
+"""
+Access to system libraries.
+
+$Id$
+"""
+
+from imaplib import IMAP4
diff --git a/integrator/mail/testing.py b/integrator/mail/testing.py
new file mode 100644
index 0000000..65d7940
--- /dev/null
+++ b/integrator/mail/testing.py
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
+#
+# 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
+#
+
+"""
+Fake access to system libraries for testing.
+
+$Id$
+"""
+
+class IMAP4(object):
+
+ def __init__(self, host=None):
+ pass
+
+ def login(self, user, password):
+ pass
+
+ def select(self, mailbox):
+ return 1
+
+ def search(self, charset, criterion):
+ return 'OK', ['1']
+
+ def fetch(self, idx, parts):
+ return 'OK', 'blubb'
+
+
+from loops.integrator.mail import system
+system.IMAP4 = IMAP4