source: main/waeup.ikoba/trunk/src/waeup/ikoba/image/browser/tests/image.txt @ 12691

Last change on this file since 12691 was 11949, checked in by Henrik Bettermann, 10 years ago

Change of name.

File size: 12.1 KB
Line 
1Image Widgets
2=============
3
4This is an infrastructure to create an image file widget that behaves
5as much as possible like a normal text widget in formlib. Normally a
6file widget loses its file data when a form is re-presented for
7reasons of failing form validation. A ``hurry.file`` widget retains
8the file, for example by storing it in a session.
9
10In order to do this, we have a special way to store file data along with
11its filename:
12
13  >>> import os
14  >>> from waeup.ikoba.image import IkobaImageFile
15  >>> testimage = os.path.join(os.path.dirname(__file__), 'sample.jpg')
16  >>> testimage2 = os.path.join(os.path.dirname(__file__), 'sample2.jpg')
17  >>> some_file = IkobaImageFile('foo.jpg', open(testimage, 'rb').read())
18  >>> some_file.filename
19  'foo.jpg'
20  >>> 'Created with GIMP' in some_file.data
21  True
22
23We can provide a download widget. In this case, there's nothing
24to download:
25
26  >>> from hurry.file.browser import DownloadWidget
27  >>> from hurry.file.schema import File
28  >>> from waeup.ikoba.image.schema import ImageFile
29  >>> from zope.publisher.browser import TestRequest
30  >>> field = ImageFile(__name__='foo', title=u'Foo')
31  >>> field = field.bind(None)
32  >>> request = TestRequest()
33  >>> widget = DownloadWidget(field, request)
34  >>> widget()
35  u'<div>Download not available</div>'
36
37Even if there were data in the request, there'd be nothing to download:
38
39  >>> from zope.publisher.browser import FileUpload
40  >>> request = TestRequest(form={'field.foo': FileUpload(some_file)})
41  >>> widget = DownloadWidget(field, request)
42  >>> widget()
43  u'<div>Download not available</div>'
44
45Now set a value:
46
47  >>> widget.setRenderedValue(some_file)
48  >>> widget()
49  u'<a href="foo.jpg">foo.jpg</a>'
50
51Instead of downloading, we can also use a thumbnail widget:
52
53  >>> from waeup.ikoba.image.browser import ThumbnailWidget
54  >>> request = TestRequest(form={'field.foo': FileUpload(some_file)})
55  >>> widget = ThumbnailWidget(field, request)
56  >>> widget.setRenderedValue(some_file)
57  >>> widget()
58  u'<img src="foo.jpg" />'
59
60Now on to an edit widget. First the case in an add form with no
61data already available, and no data in request:
62
63  >>> from waeup.ikoba.image.browser import EncodingImageFileWidget
64  >>> field = ImageFile(__name__='foo', title=u'Foo', required=False)
65  >>> field = field.bind(None)
66  >>> request = TestRequest()
67  >>> widget = EncodingImageFileWidget(field, request)
68
69  >>> def normalize(s):
70  ...   return u'\n  '.join(filter(None, s.split(' ')))
71
72  >>> print normalize(widget())
73  <input
74    class="fileType"
75    id="field.foo"
76    name="field.foo"
77    size="20"
78    type="file"
79    />
80
81Now let's try a situation where data is available in the request, but
82it's an empty string for the file:
83
84  >>> request = TestRequest(form={'field.foo': u''})
85  >>> widget = EncodingImageFileWidget(field, request)
86
87  >>> def normalize(s):
88  ...   return '\n  '.join(filter(None, s.split(' ')))
89
90  >>> print normalize(widget())
91  <input
92    class="fileType"
93    id="field.foo"
94    name="field.foo"
95    size="20"
96    type="file"
97    />
98
99Now let's render again when there's already available data. What should show
100up is an extra, hidden field which contains the file_id:
101
102  >>> widget.setRenderedValue(some_file)
103  >>> print normalize(widget())
104  <img... />...
105  ...<input
106    class="fileType"
107    id="field.foo"
108    name="field.foo"
109    size="20"
110    type="file"
111    />
112    (foo.jpg)<input
113    class="hiddenType"
114    id="field.foo.file_id"
115    name="field.foo.file_id"
116    type="hidden"
117    value="Zm9vLmpw...="
118    />
119
120Now let's render again, this time with file data available in the request
121instead. The same should happen:
122
123  >>> request = TestRequest(form={'field.foo': FileUpload(some_file)})
124  >>> widget = EncodingImageFileWidget(field, request)
125  >>> print normalize(widget())
126  <img... />...
127  ...<input
128    class="fileType"
129    id="field.foo"
130    name="field.foo"
131    size="20"
132    type="file"
133    />
134    (foo.jpg)<input
135    class="hiddenType"
136    id="field.foo.file_id"
137    name="field.foo.file_id"
138    type="hidden"
139    value="Zm9vLmpwZ...="
140    />
141
142Now let's render again, this time not with file data available in the
143request, but an id. Again, we should see the same:
144
145  >>> request = TestRequest(form={'field.foo.file_id':
146  ...                             'Zm9vLnR4dAp0aGUgY29udGVudHM='})
147  >>> widget = EncodingImageFileWidget(field, request)
148  >>> print normalize(widget())
149  <img... />...
150  ...<input
151    class="fileType"
152    id="field.foo"
153    name="field.foo"
154    size="20"
155    type="file"
156    />
157    (foo.txt)<input
158    class="hiddenType"
159    id="field.foo.file_id"
160    name="field.foo.file_id"
161    type="hidden"
162    value="Zm9vLnR4dAp0aGUgY29udGVudHM="
163    />
164
165If there is both file data and an id, something else happens. First, let's
166prepare some new file:
167
168  >>> another_file = IkobaImageFile('bar.txt', 'bar contents')
169
170We happen to know, due to the implementation of
171EncodingImageFileWidget, that the file_id is going to be
172"Zm9vLnR4dAp0aGUgY29udGVudHM=". Let's make a request with the original
173id, but a new file upload:
174
175  >>> request = TestRequest(form={'field.foo': FileUpload(another_file),
176  ...                             'field.foo.file_id':
177  ...                             'Zm9vLnR4dAp0aGUgY29udGVudHM='})
178
179We expect the new file to be the one that's uploaded:
180
181  >>> widget = EncodingImageFileWidget(field, request)
182  >>> print normalize(widget())
183  <img... />...
184  ...<input
185    class="fileType"
186    id="field.foo"
187    name="field.foo"
188    size="20"
189    type="file"
190    />
191    (bar.txt)<input
192    class="hiddenType"
193    id="field.foo.file_id"
194    name="field.foo.file_id"
195    type="hidden"
196    value="YmFyLnR4dApiYXIgY29udGVudHM="
197    />
198
199Support for File Retrievals
200---------------------------
201
202As :class:`waeup.ikoba.image.IkobaImageFile` objects support storing
203image data by using external 'storages', also our widgets should do
204so.
205
206We create a simple IFileRetrieval utility and enable it:
207
208  >>> import hashlib
209  >>> from zope.component import provideUtility
210  >>> from hurry.file.interfaces import IFileRetrieval
211  >>> from StringIO import StringIO
212  >>> class MyFileRetrieval(object):
213  ...     storage = dict()
214  ...     def getFile(self, data):
215  ...         entry = self.storage.get(data, None)
216  ...         if entry is None:
217  ...             return None
218  ...         return StringIO(entry)
219  ...     def createFile(self, filename, f):
220  ...         contents = f.read()
221  ...         id_string = hashlib.md5(contents).hexdigest()
222  ...         result = IkobaImageFile(filename, id_string)
223  ...         self.storage[id_string] = contents
224  ...         return result
225
226  >>> retrieval = MyFileRetrieval()
227  >>> provideUtility(retrieval, IFileRetrieval)
228
229With this utility in place we can post requests. When no data was
230posted and the field also contains no data, we will get a simple
231input as appropriate for add forms:
232
233  >>> field = ImageFile(__name__='foo', title=u'Foo', required=False)
234  >>> field = field.bind(None)
235  >>> request = TestRequest()
236  >>> widget = EncodingImageFileWidget(field, request)
237  >>> print normalize(widget())
238  <input
239    class="fileType"
240    id="field.foo"
241    name="field.foo"
242    size="20"
243    type="file"
244    />
245
246If the request contains empty data but it is only an empty string, the
247result will be the same:
248
249  >>> request = TestRequest(form={'field.foo': u''})
250  >>> widget = EncodingImageFileWidget(field, request)
251  >>> print normalize(widget())
252  <input
253    class="fileType"
254    id="field.foo"
255    name="field.foo"
256    size="20"
257    type="file"
258    />
259
260We now want to simulate, that the field contains already data,
261identified by some `file_id`. To do so, we first store the data in our
262file retrieval and then create a IkobaImageFile object with that
263file_id stored:
264
265  >>> from waeup.ikoba.image import createIkobaImageFile
266  >>> image = createIkobaImageFile(
267  ...     'sample.jpg', open(testimage, 'rb'))
268  >>> file_id = image.data
269  >>> file_id    # MD5 sum of the file contents
270  '9feac4265077922000aa8b88748e25be'
271
272  >>> import hashlib
273  >>> hashlib.md5(open(testimage, 'rb').read()).hexdigest()
274  '9feac4265077922000aa8b88748e25be'
275
276The new file was stored by our utility, as createIkobaImageFile looks
277up IFileRetrieval utilities and uses them:
278
279  >>> retrieval.storage.keys()
280  ['9feac4265077922000aa8b88748e25be']
281
282We set this image as value of the widget:
283
284  >>> widget.setRenderedValue(image)
285  >>> print normalize(widget())
286  <img
287    src="sample.jpg"
288    /><br
289    /><input
290    class="fileType"
291    id="field.foo"
292    name="field.foo"
293    size="20"
294    type="file"
295    />
296    (sample.jpg)<input
297    class="hiddenType"
298    id="field.foo.file_id"
299    name="field.foo.file_id"
300    type="hidden"
301    value="c2FtcGxlLmpwZwo5ZmVhYzQyNjUwNzc5MjIwMDBhYThiODg3NDhlMjViZQ=="
302    />
303
304  >>> retrieval.storage.keys()
305  ['9feac4265077922000aa8b88748e25be']
306
307The stored hidden value contains the filename and our file_id:
308
309  >>> "c2FtcGxlLmpwZwo5ZmVhYzQyNjUwNzc5MjIwMDBhYThiODg3NDhlMjViZQ==".decode(
310  ...    'base64')
311  'sample.jpg\n9feac4265077922000aa8b88748e25be'
312
313Now, we want the the widget rendered again but this time with the data
314coming from the request. To do so, we have to create a FileUpload
315object that wraps the real file:
316
317  >>> class UploadedFile(object):
318  ...   headers = {}
319  ...   def __init__(self, filename, f):
320  ...     self.filename = filename
321  ...     self.file = f
322
323Now we can 'post' the request and will get the same result as above:
324
325  >>> upload = FileUpload(UploadedFile('sample.jpg', open(testimage, 'rb')))
326  >>> request = TestRequest(form={'field.foo': upload})
327  >>> widget = EncodingImageFileWidget(field, request)
328  >>> print normalize(widget())
329  <img... />...
330  ...<input
331    class="fileType"
332    id="field.foo"
333    name="field.foo"
334    size="20"
335    type="file"
336    />
337    (sample.jpg)<input
338    class="hiddenType"
339    id="field.foo.file_id"
340    name="field.foo.file_id"
341    type="hidden"
342    value="c2FtcGxlLmpwZwo5ZmVhYzQyNjUwNzc5MjIwMDBhYThiODg3NDhlMjViZQ=="
343    />
344
345Now let's render again, this time not with file data available in the
346request, but an id. Again, we should see the same:
347
348  >>> request = TestRequest(form={
349  ...   'field.foo.file_id':
350  ...   'c2FtcGxlLmpwZwo5ZmVhYzQyNjUwNzc5MjIwMDBhYThiODg3NDhlMjViZQ=='})
351  >>> widget = EncodingImageFileWidget(field, request)
352  >>> print normalize(widget())
353  <img... />...
354  ...<input
355    class="fileType"
356    id="field.foo"
357    name="field.foo"
358    size="20"
359    type="file"
360    />
361    (sample.jpg)<input
362    class="hiddenType"
363    id="field.foo.file_id"
364    name="field.foo.file_id"
365    type="hidden"
366    value="c2FtcGxlLmpwZwo5ZmVhYzQyNjUwNzc5MjIwMDBhYThiODg3NDhlMjViZQ=="
367    />
368
369If there is both file data and an id, something else happens. First, let's
370prepare some new file:
371
372  >>> upload = FileUpload(UploadedFile('sample2.jpg', open(testimage2, 'rb')))
373
374We happen to know, due to the implementation of
375EncodingImageFileWidget, that the file_id is going to be
376"Zm9vLnR4dAp0aGUgY29udGVudHM=". Let's make a request with the original
377id, but a new file upload:
378
379  >>> request = TestRequest(form={'field.foo': upload,
380  ...                             'field.foo.file_id':
381  ...                             'Z2FtcGxlLmpwZwr/2P/gAA=='})
382
383We expect the new file to be the one that's uploaded:
384
385  >>> field = ImageFile(__name__='foo', title=u'Foo', required=False)
386  >>> field = field.bind(None)
387  >>> widget = EncodingImageFileWidget(field, request)
388  >>> print normalize(widget())
389  <img... />...
390  ...<input
391    class="fileType"
392    id="field.foo"
393    name="field.foo"
394    size="20"
395    type="file"
396    />
397    (sample2.jpg)<input
398    class="hiddenType"
399    id="field.foo.file_id"
400    name="field.foo.file_id"
401    type="hidden"
402    value="c2FtcGxlMi5qcGcKZDk2MDkwZWRlMmRjODlkZDdkZWM5ZDU3MmFkNThjNzQ="
403    />
404
405The value displayed above again means the filename and md5 sum of the
406stored file:
407
408  >>> "c2FtcGxlMi5qcGcKZDk2MDkwZWRlMmRjODlkZDdkZWM5ZDU3MmFkNThjNzQ=".decode(
409  ...      'base64')
410  'sample2.jpg\nd96090ede2dc89dd7dec9d572ad58c74'
411
412where the md5 sum is in fact correct:
413
414  >>> import hashlib
415  >>> hashlib.md5(open(testimage2, 'rb').read()).hexdigest()
416  'd96090ede2dc89dd7dec9d572ad58c74'
417
418Our file retrieval utility now contains two files:
419
420  >>> retrieval.storage.keys()
421  ['9feac4265077922000aa8b88748e25be', 'd96090ede2dc89dd7dec9d572ad58c74']
Note: See TracBrowser for help on using the repository browser.