Index: main/waeup.sirp/trunk/src/waeup/sirp/image/__init__.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/image/__init__.py	(revision 5693)
+++ main/waeup.sirp/trunk/src/waeup/sirp/image/__init__.py	(revision 5693)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: main/waeup.sirp/trunk/src/waeup/sirp/image/browser/__init__.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/image/browser/__init__.py	(revision 5693)
+++ main/waeup.sirp/trunk/src/waeup/sirp/image/browser/__init__.py	(revision 5693)
@@ -0,0 +1,10 @@
+# The public API part
+# Contains the widgets defined for image files.
+from waeup.sirp.image.browser.widget import (
+    EncodingImageFileWidget, ThumbnailWidget,
+    )
+
+__all__ = (
+    EncodingImageFileWidget,
+    ThumbnailWidget,
+    )
Index: main/waeup.sirp/trunk/src/waeup/sirp/image/browser/image.txt
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/image/browser/image.txt	(revision 5693)
+++ main/waeup.sirp/trunk/src/waeup/sirp/image/browser/image.txt	(revision 5693)
@@ -0,0 +1,194 @@
+Image Widgets
+=============
+
+This is an infrastructure to create an image file widget that behaves
+as much as possible like a normal text widget in formlib. Normally a
+file widget loses its file data when a form is re-presented for
+reasons of failing form validation. A ``hurry.file`` widget retains
+the file, for example by storing it in a session.
+
+In order to do this, we have a special way to store file data along with
+its filename::
+
+  >>> import os
+  >>> from hurry.file import HurryFile
+  >>> testimage = os.path.join(os.path.dirname(__file__), 'sample.jpg')
+  >>> some_file = HurryFile('foo.jpg', open(testimage, 'rb').read())
+  >>> some_file.filename
+  'foo.jpg'
+  >>> 'Created with GIMP' in some_file.data
+  True
+
+We can provide a download widget. In this case, there's nothing
+to download::
+
+  >>> from hurry.file.browser import DownloadWidget
+  >>> from hurry.file.schema import File
+  >>> from waeup.sirp.image.schema import ImageFile
+  >>> from zope.publisher.browser import TestRequest
+  >>> field = ImageFile(__name__='foo', title=u'Foo')
+  >>> field = field.bind(None)
+  >>> request = TestRequest()
+  >>> widget = DownloadWidget(field, request)
+  >>> widget()
+  u'<div>Download not available</div>'
+ 
+Even if there were data in the request, there'd be nothing to download::
+
+  >>> from zope.publisher.browser import FileUpload
+  >>> request = TestRequest(form={'field.foo': FileUpload(some_file)})
+  >>> widget = DownloadWidget(field, request)
+  >>> widget()
+  u'<div>Download not available</div>'
+
+Now set a value::
+
+  >>> widget.setRenderedValue(some_file)
+  >>> widget()
+  u'<a href="foo.jpg">foo.jpg</a>'
+
+Instead of downloading, we can also use a thumbnail widget:
+
+  >>> from waeup.sirp.image.browser import ThumbnailWidget
+  >>> request = TestRequest(form={'field.foo': FileUpload(some_file)})
+  >>> widget = ThumbnailWidget(field, request)
+  >>> widget.setRenderedValue(some_file)
+  >>> widget()
+  u'<img src="foo.jpg" />'
+
+Now on to an edit widget. First the case in an add form with no
+data already available, and no data in request::
+
+  >>> #from hurry.file.browser import EncodingFileWidget
+  >>> from waeup.sirp.image.browser import EncodingImageFileWidget
+  >>> field = File(__name__='foo', title=u'Foo', required=False)
+  >>> field = field.bind(None)
+  >>> request = TestRequest()
+  >>> widget = EncodingImageFileWidget(field, request)
+ 
+  >>> def normalize(s):
+  ...   return u'\n  '.join(filter(None, s.split(' ')))
+
+  >>> print normalize(widget())
+  <input
+    class="fileType"
+    id="field.foo"
+    name="field.foo"
+    size="20"
+    type="file"
+    />
+
+Now let's try a situation where data is available in the request, but
+it's an empty string for the file::
+
+  >>> request = TestRequest(form={'field.foo': u''})
+  >>> widget = EncodingImageFileWidget(field, request)
+ 
+  >>> def normalize(s):
+  ...   return '\n  '.join(filter(None, s.split(' ')))
+
+  >>> print normalize(widget())
+  <input
+    class="fileType"
+    id="field.foo"
+    name="field.foo"
+    size="20"
+    type="file"
+    />
+
+Now let's render again when there's already available data. What should show
+up is an extra, hidden field which contains the file_id::
+
+  >>> widget.setRenderedValue(some_file)
+  >>> print normalize(widget())
+  <input
+    class="fileType"
+    id="field.foo"
+    name="field.foo"
+    size="20"
+    type="file"
+    />
+    (foo.jpg)<input
+    class="hiddenType"
+    id="field.foo.file_id"
+    name="field.foo.file_id"
+    type="hidden"
+    value="Zm9vLmpw...="
+    />
+
+Now let's render again, this time with file data available in the request
+instead. The same should happen::
+
+  >>> request = TestRequest(form={'field.foo': FileUpload(some_file)})
+  >>> widget = EncodingImageFileWidget(field, request)
+  >>> print normalize(widget())
+  <input
+    class="fileType"
+    id="field.foo"
+    name="field.foo"
+    size="20"
+    type="file"
+    />
+    (foo.jpg)<input
+    class="hiddenType"
+    id="field.foo.file_id"
+    name="field.foo.file_id"
+    type="hidden"
+    value="Zm9vLmpwZ...="
+    />
+
+Now let's render again, this time not with file data available in the
+request, but an id. Again, we should see the same::
+
+  >>> request = TestRequest(form={'field.foo.file_id': 
+  ...                             'Zm9vLnR4dAp0aGUgY29udGVudHM='})
+  >>> widget = EncodingImageFileWidget(field, request)
+  >>> print normalize(widget())
+  <input
+    class="fileType"
+    id="field.foo"
+    name="field.foo"
+    size="20"
+    type="file"
+    />
+    (foo.txt)<input
+    class="hiddenType"
+    id="field.foo.file_id"
+    name="field.foo.file_id"
+    type="hidden"
+    value="Zm9vLnR4dAp0aGUgY29udGVudHM="
+    />
+
+If there is both file data and an id, something else happens. First, let's
+prepare some new file::
+
+  >>> another_file = HurryFile('bar.txt', 'bar contents')
+
+We happen to know, due to the implementation of
+EncodingImageFileWidget, that the file_id is going to be
+"Zm9vLnR4dAp0aGUgY29udGVudHM=". Let's make a request with the original
+id, but a new file upload::
+
+  >>> request = TestRequest(form={'field.foo': FileUpload(another_file),
+  ...                             'field.foo.file_id': 
+  ...                             'Zm9vLnR4dAp0aGUgY29udGVudHM='})
+
+We expect the new file to be the one that's uploaded::
+
+  >>> widget = EncodingImageFileWidget(field, request)
+  >>> print normalize(widget())
+  <input
+    class="fileType"
+    id="field.foo"
+    name="field.foo"
+    size="20"
+    type="file"
+    />
+    (bar.txt)<input
+    class="hiddenType"
+    id="field.foo.file_id"
+    name="field.foo.file_id"
+    type="hidden"
+    value="YmFyLnR4dApiYXIgY29udGVudHM="
+    />
+
Index: main/waeup.sirp/trunk/src/waeup/sirp/image/browser/tests.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/image/browser/tests.py	(revision 5693)
+++ main/waeup.sirp/trunk/src/waeup/sirp/image/browser/tests.py	(revision 5693)
@@ -0,0 +1,48 @@
+##
+## tests.py
+## Login : <uli@pu.smp.net>
+## Started on  Mon Sep 13 10:38:33 2010 Uli Fouquet
+## $Id$
+## 
+## Copyright (C) 2010 Uli Fouquet
+## 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
+##
+"""Tests for image files.
+"""
+import doctest
+import unittest
+
+from hurry.file.file import IdFileRetrieval
+from hurry.file.interfaces import IFileRetrieval
+from zope import component
+from zope.app.testing import placelesssetup
+
+
+def imageSetUp(doctest):
+    placelesssetup.setUp()
+    component.provideUtility(IdFileRetrieval(), IFileRetrieval)
+
+def test_suite():
+    return unittest.TestSuite(
+        (
+            doctest.DocFileSuite(
+                'image.txt',
+                setUp=imageSetUp,
+                tearDown=placelesssetup.tearDown,
+                optionflags=doctest.ELLIPSIS,
+                ),
+            )
+        )
+
Index: main/waeup.sirp/trunk/src/waeup/sirp/image/browser/views.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/image/browser/views.py	(revision 5693)
+++ main/waeup.sirp/trunk/src/waeup/sirp/image/browser/views.py	(revision 5693)
@@ -0,0 +1,36 @@
+##
+## views.py
+## Login : <uli@pu.smp.net>
+## Started on  Sat Sep 18 14:16:06 2010 Uli Fouquet
+## $Id$
+## 
+## Copyright (C) 2010 Uli Fouquet
+## 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
+##
+"""Views for images and the like.
+"""
+import grok
+from hurry.file.interfaces import IHurryFile
+
+class HurryFileView(grok.View):
+
+    grok.name('index.html')
+    grok.context(IHurryFile)
+
+    def render(self):
+        self.response.setHeader('Content-Type', 'image/jpeg')
+        print dir(self.context)
+        print self.context.headers
+        return self.context.data
Index: main/waeup.sirp/trunk/src/waeup/sirp/image/browser/widget.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/image/browser/widget.py	(revision 5693)
+++ main/waeup.sirp/trunk/src/waeup/sirp/image/browser/widget.py	(revision 5693)
@@ -0,0 +1,81 @@
+##
+## widget.py
+## Login : <uli@pu.smp.net>
+## Started on  Tue Sep 14 11:00:16 2010 Uli Fouquet
+## $Id$
+## 
+## Copyright (C) 2010 Uli Fouquet
+## 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
+##
+"""Image file widgets.
+"""
+from hurry.file import HurryFile
+from hurry.file.browser.widget import (
+    FileWidgetBase, EncodingFileWidget, DownloadWidget,
+    )
+from zope.app.form.browser.textwidgets import escape
+from zope.app.form.browser.widget import renderElement
+
+class EncodingImageFileWidget(EncodingFileWidget):
+    """A hurry.file widget that stores images.
+    """
+    #: We can set a basename for the image to be rendered.
+    #: This will result in <img> links like <img src='.../<basename>.jpg' />
+    image_basename = None
+    
+    def __call__(self):
+        # The base widget renders the actual image as filename. We
+        # want the image itself displayed as well.
+        value = self._getFormValue()
+        result = u''
+        if value:
+            filename = value.filename
+            if self.image_basename is not None:
+                filename = self.image_basename + os.path.splitext(filename)[-1]
+            result = renderElement(
+                'img',
+                src=filename,
+                )
+            result += renderElement('br')
+        # Append the rendering as delivered from the base widget.
+        result += super(EncodingImageFileWidget, self).__call__()
+        #import pdb; pdb.set_trace()
+        return result
+
+    def _storeFile(self, file_upload):
+        # filenames are normally in unicode encoding, while the contents
+        # are byte streams. We turn the filename into a bytestream.
+        data = '%s\n%s' % (str(file_upload.filename), file_upload.read())
+        return data.encode('base64')[:-1]
+
+class ThumbnailWidget(DownloadWidget):
+    """An image file widget that displays the data as thumbnail.
+    """
+
+    def __call__(self):
+        if self._renderedValueSet():
+            value = self._data
+        else:
+            value = self.context.default
+        if value == self.context.missing_value:
+            return renderElement(
+                u'div',
+                contents=u'Download not available')
+        filename = escape(value.filename)
+        return renderElement(
+            u'img',
+            src=filename,
+            contents=None)
+
Index: main/waeup.sirp/trunk/src/waeup/sirp/image/interfaces.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/image/interfaces.py	(revision 5693)
+++ main/waeup.sirp/trunk/src/waeup/sirp/image/interfaces.py	(revision 5693)
@@ -0,0 +1,33 @@
+##
+## interfaces.py
+## Login : <uli@pu.smp.net>
+## Started on  Mon Sep 13 10:28:05 2010 Uli Fouquet
+## $Id$
+## 
+## Copyright (C) 2010 Uli Fouquet
+## 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
+##
+"""Interfaces for waeup.sirp.image
+"""
+from zope.app.file.interfaces import IImage
+from hurry.file.interfaces import IFile, IFileRetrieval
+
+class IImageFile(IFile, IImage):
+    """Image file field.
+    """
+
+class IImageFileRetrieval(IFileRetrieval):
+    """A file retrieval for images.
+    """
Index: main/waeup.sirp/trunk/src/waeup/sirp/image/schema.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/image/schema.py	(revision 5693)
+++ main/waeup.sirp/trunk/src/waeup/sirp/image/schema.py	(revision 5693)
@@ -0,0 +1,31 @@
+##
+## schema.py
+## Login : <uli@pu.smp.net>
+## Started on  Mon Sep 13 10:31:43 2010 Uli Fouquet
+## $Id$
+## 
+## Copyright (C) 2010 Uli Fouquet
+## 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
+##
+"""Image file schemas.
+"""
+from zope.interface import implements
+from hurry.file.schema import File
+from waeup.sirp.image.interfaces import IImageFile
+
+class ImageFile(File):
+    """An image file.
+    """
+    implements(IImageFile)
