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