1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """For uploading files from client to server."""
17
18 from warnings import warn
19
20 from muntjac.ui.abstract_component import AbstractComponent
21
22 from muntjac.ui.component import \
23 IFocusable, Event as ComponentEvent
24
25 from muntjac.terminal.stream_variable import \
26 IStreamVariable, IStreamingEvent
27
28 from muntjac.terminal.gwt.server.exceptions import \
29 NoInputStreamException, NoOutputStreamException
30
31 from muntjac.util import OrderedSet
32
33
35 """Receives the events when the upload starts.
36
37 @author: Vaadin Ltd.
38 @author: Richard Lincoln
39 @version: 1.1.2
40 """
41
43 """Upload has started.
44
45 @param event:
46 the Upload started event.
47 """
48 raise NotImplementedError
49
50
52 """Receives the events when the uploads are ready.
53
54 @author: Vaadin Ltd.
55 @author: Richard Lincoln
56 @version: 1.1.2
57 """
58
60 """Upload has finished.
61
62 @param event:
63 the Upload finished event.
64 """
65 raise NotImplementedError
66
67
69 """Receives events when the uploads are finished, but unsuccessful.
70
71 @author: Vaadin Ltd.
72 @author: Richard Lincoln
73 @version: 1.1.2
74 """
75
77 """Upload has finished unsuccessfully.
78
79 @param event:
80 the Upload failed event.
81 """
82 raise NotImplementedError
83
84
86 """Receives events when the uploads are successfully finished.
87
88 @author: Vaadin Ltd.
89 @author: Richard Lincoln
90 @version: 1.1.2
91 """
92
94 """Upload successfull..
95
96 @param event:
97 the Upload successful event.
98 """
99 raise NotImplementedError
100
101
103 """IProgressListener receives events to track progress of upload."""
104
106 """Updates progress to listener.
107
108 @param readBytes:
109 bytes transferred
110 @param contentLength:
111 total size of file currently being uploaded, -1 if unknown
112 """
113 raise NotImplementedError
114
115
116 _UPLOAD_FINISHED_METHOD = getattr(IFinishedListener, 'uploadFinished')
117 _UPLOAD_FAILED_METHOD = getattr(IFailedListener, 'uploadFailed')
118 _UPLOAD_STARTED_METHOD = getattr(IStartedListener, 'uploadStarted')
119 _UPLOAD_SUCCEEDED_METHOD = getattr(ISucceededListener, 'uploadSucceeded')
120
121
122 -class Upload(AbstractComponent, IFocusable):
123 """IComponent for uploading files from client to server.
124
125 The visible component consists of a file name input box and a browse
126 button and an upload submit button to start uploading.
127
128 The Upload component needs a StringIO to write the uploaded
129 data. You need to implement the upload.IReceiver interface and return the
130 output stream in the receiveUpload() method.
131
132 You can get an event regarding starting (StartedEvent), progress
133 (ProgressEvent), and finishing (FinishedEvent) of upload by implementing
134 IStartedListener, IProgressListener, and IFinishedListener, respectively.
135 The IFinishedListener is called for both failed and succeeded uploads. If
136 you wish to separate between these two cases, you can use
137 ISucceededListener (SucceededEvenet) and IFailedListener (FailedEvent).
138
139 The upload component does not itself show upload progress, but you can use
140 the ProgressIndicator for providing progress feedback by implementing
141 IProgressListener and updating the indicator in updateProgress().
142
143 Setting upload component immediate initiates the upload as soon as a file
144 is selected, instead of the common pattern of file selection field and
145 upload button.
146
147 Note! Because of browser dependent implementations of <input type="file">
148 element, setting size for Upload component is not supported. For some
149 browsers setting size may work to some extent.
150
151 @author: Vaadin Ltd.
152 @author: Richard Lincoln
153 @version: 1.1.2
154 """
155
156 CLIENT_WIDGET = None
157
158 - def __init__(self, caption=None, uploadReceiver=None):
159 """Creates a new instance of Upload.
160
161 The receiver must be set before performing an upload.
162 """
163 super(Upload, self).__init__()
164
165
166 self._focus = False
167
168
169 self._tabIndex = 0
170
171
172 self._receiver = None
173
174 self._isUploading = False
175
176 self._contentLength = -1
177
178 self._totalBytes = None
179
180 self._buttonCaption = 'Upload'
181
182
183
184 self._progressListeners = OrderedSet()
185
186 self._progressCallbacks = dict()
187
188 self._interrupted = False
189
190 self._notStarted = None
191
192 self._nextid = 0
193
194
195 self._forceSubmit = None
196
197 if caption:
198 self.setCaption(caption)
199
200 if uploadReceiver is not None:
201 self._receiver = uploadReceiver
202
203 self._streamVariable = None
204
205
207 """Invoked when the value of a variable has changed.
208
209 @see: L{AbstractComponent.changeVariables}
210 """
211 if 'pollForStart' in variables:
212 idd = variables.get('pollForStart')
213 if not self._isUploading and idd == self._nextid:
214 self._notStarted = True
215 self.requestRepaint()
216 else:
217 pass
218
219
220 - def paintContent(self, target):
221 """Paints the content of this component.
222
223 @param target:
224 Target to paint the content on.
225 @raise PaintException:
226 if the paint operation failed.
227 """
228 if self._notStarted:
229 target.addAttribute('notStarted', True)
230 self._notStarted = False
231 return
232
233 if self._forceSubmit:
234 target.addAttribute('forceSubmit', True)
235 self._forceSubmit = True
236 return
237
238
239 if self._focus:
240 target.addAttribute('focus', True)
241
242
243 if self._tabIndex >= 0:
244 target.addAttribute('tabindex', self._tabIndex)
245
246 target.addAttribute('state', self._isUploading)
247
248 if self._buttonCaption is not None:
249 target.addAttribute('buttoncaption', self._buttonCaption)
250
251 target.addAttribute('nextid', self._nextid)
252
253
254 target.addVariable(self, 'action', self.getStreamVariable())
255
256
258 """Adds an event listener.
259
260 @param listener:
261 the listener to be added.
262 """
263 if (isinstance(listener, IFailedListener) and
264 (iface is None or issubclass(iface, IFailedListener))):
265 self.registerListener(FailedEvent,
266 listener, _UPLOAD_FAILED_METHOD)
267
268 if (isinstance(listener, IFinishedListener) and
269 (iface is None or issubclass(iface, IFinishedListener))):
270 self.registerListener(FinishedEvent,
271 listener, _UPLOAD_FINISHED_METHOD)
272
273 if (isinstance(listener, IProgressListener) and
274 (iface is None or issubclass(iface, IProgressListener))):
275 self._progressListeners.add(listener)
276
277 if (isinstance(listener, IStartedListener) and
278 (iface is None or issubclass(iface, IStartedListener))):
279 self.registerListener(StartedEvent,
280 listener, _UPLOAD_STARTED_METHOD)
281
282 if (isinstance(listener, ISucceededListener) and
283 (iface is None or issubclass(iface, ISucceededListener))):
284 self.registerListener(SucceededEvent,
285 listener, _UPLOAD_SUCCEEDED_METHOD)
286
287 super(Upload, self).addListener(listener, iface)
288
289
290 - def addCallback(self, callback, eventType=None, *args):
291 if eventType is None:
292 eventType = callback._eventType
293
294 if issubclass(eventType, FailedEvent):
295 self.registerCallback(FailedEvent, callback, None, *args)
296
297 elif issubclass(eventType, FinishedEvent):
298 self.registerCallback(FinishedEvent, callback, None, *args)
299
300 elif issubclass(eventType, IProgressListener):
301 self._progressCallbacks[callback] = args
302
303 elif issubclass(eventType, StartedEvent):
304 self.registerCallback(StartedEvent, callback, None, *args)
305
306 elif issubclass(eventType, SucceededEvent):
307 self.registerCallback(SucceededEvent, callback, None, *args)
308
309 else:
310 super(Upload, self).addCallback(callback, eventType, *args)
311
312
314 """Removes an event listener.
315
316 @param listener:
317 the listener to be removed.
318 """
319 if (isinstance(listener, IFailedListener) and
320 (iface is None or issubclass(iface, IFailedListener))):
321 self.withdrawListener(FailedEvent,
322 listener, _UPLOAD_FAILED_METHOD)
323
324 if (isinstance(listener, IFinishedListener) and
325 (iface is None or issubclass(iface, IFinishedListener))):
326 self.withdrawListener(FinishedEvent,
327 listener, _UPLOAD_FINISHED_METHOD)
328
329 if (isinstance(listener, IProgressListener) and
330 (iface is None or issubclass(iface, IProgressListener))):
331 if listener in self._progressListeners:
332 self._progressListeners.remove(listener)
333
334 if (isinstance(listener, IStartedListener) and
335 (iface is None or issubclass(iface, IStartedListener))):
336 self.withdrawListener(StartedEvent,
337 listener, _UPLOAD_STARTED_METHOD)
338
339 if (isinstance(listener, ISucceededListener) and
340 (iface is None or issubclass(iface, ISucceededListener))):
341 self.withdrawListener(SucceededEvent,
342 listener, _UPLOAD_SUCCEEDED_METHOD)
343
344 super(Upload, self).removeListener(listener, iface)
345
346
348 if eventType is None:
349 eventType = callback._eventType
350
351 if issubclass(eventType, FailedEvent):
352 self.withdrawCallback(FailedEvent, callback)
353
354 elif issubclass(eventType, FinishedEvent):
355 self.withdrawCallback(FinishedEvent, callback)
356
357 elif issubclass(eventType, IProgressListener):
358 if callback in self._progressCallbacks:
359 del self._progressListeners[callback]
360
361 elif issubclass(eventType, StartedEvent):
362 self.withdrawCallback(StartedEvent, callback)
363
364 elif issubclass(eventType, SucceededEvent):
365 self.withdrawCallback(SucceededEvent, callback)
366
367 else:
368 super(Upload, self).removeCallback(callback, eventType)
369
370
372 """Emit upload received event.
373 """
374 evt = StartedEvent(self, filename, MIMEType, self._contentLength)
375 self.fireEvent(evt)
376
377
379 """Emits the upload failed event.
380 """
381 if e is None:
382 evt = FailedEvent(self, filename, MIMEType, length)
383 else:
384 evt = FailedEvent(self, filename, MIMEType, length, e)
385
386 self.fireEvent(evt)
387
388
392
393
397
398
400 """Emits the upload success event.
401 """
402 evt = SucceededEvent(self, filename, MIMEType, length)
403 self.fireEvent(evt)
404
405
407 """Emits the progress event.
408
409 @param totalBytes:
410 bytes received so far
411 @param contentLength:
412 actual size of the file being uploaded, if known
413 """
414
415
416 for l in self._progressListeners:
417 l.updateProgress(totalBytes, contentLength)
418
419 for callback, args in self._progressCallbacks.iteritems():
420 callback(totalBytes, contentLength, *args)
421
422
424 """Returns the current receiver.
425
426 @return: the IStreamVariable.
427 """
428 return self._receiver
429
430
432 """Sets the receiver.
433
434 @param receiver:
435 the receiver to set.
436 """
437 self._receiver = receiver
438
439
442
443
445 """Gets the Tabulator index of this IFocusable component.
446
447 @see: L{IFocusable.getTabIndex}
448 """
449 return self._tabIndex
450
451
453 """Sets the Tabulator index of this IFocusable component.
454
455 @see: L{IFocusable.setTabIndex}
456 """
457 self._tabIndex = tabIndex
458
459
461 """Go into upload state. This is to prevent double uploading on same
462 component.
463
464 Warning: this is an internal method used by the framework and should
465 not be used by user of the Upload component. Using it results in the
466 Upload component going in wrong state and not working. It is currently
467 public because it is used by another class.
468 """
469 if self._isUploading:
470 raise ValueError, 'uploading already started'
471
472 self._isUploading = True
473 self._nextid += 1
474
475
477 """Interrupts the upload currently being received. The interruption
478 will be done by the receiving tread so this method will return
479 immediately and the actual interrupt will happen a bit later.
480 """
481 if self._isUploading:
482 self._interrupted = True
483
484
486 """Go into state where new uploading can begin.
487
488 Warning: this is an internal method used by the framework and should
489 not be used by user of the Upload component.
490 """
491 self._isUploading = False
492 self._contentLength = -1
493 self._interrupted = False
494 self.requestRepaint()
495
496
498 return self._isUploading
499
500
502 """Gets read bytes of the file currently being uploaded.
503
504 @return: bytes
505 """
506 return self._totalBytes
507
508
510 """Returns size of file currently being uploaded. Value sane only
511 during upload.
512
513 @return: size in bytes
514 """
515 return self._contentLength
516
517
519 """This method is deprecated, use addListener(IProgressListener)
520 instead.
521
522 @deprecated: Use addListener(IProgressListener) instead.
523 """
524 warn('use addListener() instead', DeprecationWarning)
525
526 self.addListener(progressListener, IProgressListener)
527
528
530 """This method is deprecated.
531
532 @deprecated: Replaced with addListener/removeListener
533 @return: listener
534 """
535 warn('replaced with addListener/removeListener', DeprecationWarning)
536
537 if len(self._progressListeners) == 0:
538 return None
539 else:
540 return iter(self._progressListeners).next()
541
542
546
547
570
571
573 """Forces the upload the send selected file to the server.
574
575 In case developer wants to use this feature, he/she will most probably
576 want to hide the uploads internal submit button by setting its caption
577 to null with L{setButtonCaption} method.
578
579 Note, that the upload runs asynchronous. Developer should use normal
580 upload listeners to trac the process of upload. If the field is empty
581 uploaded the file name will be empty string and file length 0 in the
582 upload finished event.
583
584 Also note, that the developer should not remove or modify the upload
585 in the same user transaction where the upload submit is requested. The
586 upload may safely be hidden or removed once the upload started event
587 is fired.
588 """
589 self.requestRepaint()
590 self._forceSubmit = True
591
592
596
597
599
600
601 if self._streamVariable is None:
602 self._streamVariable = InnerStreamVariable(self)
603
604 return self._streamVariable
605
606
612
613
619
620
622
624 self._upload = upload
625 self._lastStartedEvent = None
626
627
629 return (self._upload.progressListeners is not None
630 and len(self._upload.progressListeners) > 0)
631
632
636
637
639 return self._upload.interrupted
640
641
648
649
656
657
663
664
679
680
682 """Interface that must be implemented by the upload receivers to provide
683 the Upload component an output stream to write the uploaded data.
684
685 @author: Vaadin Ltd.
686 @author: Richard Lincoln
687 @version: 1.1.2
688 """
689
691 """Invoked when a new upload arrives.
692
693 @param filename:
694 the desired filename of the upload, usually as specified
695 by the client.
696 @param mimeType:
697 the MIME type of the uploaded file.
698 @return: Stream to which the uploaded file should be written.
699 """
700 raise NotImplementedError
701
702
704 """Upload.FinishedEvent is sent when the upload receives a file,
705 regardless of whether the reception was successful or failed. If
706 you wish to distinguish between the two cases, use either SucceededEvent
707 or FailedEvent, which are both subclasses of the FinishedEvent.
708
709 @author: Vaadin Ltd.
710 @author: Richard Lincoln
711 @version: 1.1.2
712 """
713
714 - def __init__(self, source, filename, MIMEType, length):
715 """@param source:
716 the source of the file.
717 @param filename:
718 the received file name.
719 @param MIMEType:
720 the MIME type of the received file.
721 @param length:
722 the length of the received file.
723 """
724 super(FinishedEvent, self).__init__(source)
725
726
727 self._type = MIMEType
728
729
730 self._filename = filename
731
732
733 self._length = length
734
735
737 """Uploads where the event occurred.
738
739 @return: the source of the event.
740 """
741 return self.getSource()
742
743
745 """Gets the file name.
746
747 @return: the filename.
748 """
749 return self._filename
750
751
753 """Gets the MIME Type of the file.
754
755 @return: the MIME type.
756 """
757 return self._type
758
759
761 """Gets the length of the file.
762
763 @return: the length.
764 """
765 return self._length
766
767
769 """Upload.FailedEvent event is sent when the upload is received,
770 but the reception is interrupted for some reason.
771
772 @author: Vaadin Ltd.
773 @author: Richard Lincoln
774 @version: 1.1.2
775 """
776
777 - def __init__(self, source, filename, MIMEType, length, reason=None):
778 super(FailedEvent, self).__init__(source, filename, MIMEType, length)
779
780 self._reason = reason
781
782
784 """Gets the exception that caused the failure.
785
786 @return: the exception that caused the failure, null if n/a
787 """
788 return self._reason
789
790
792 """FailedEvent that indicates that an output stream could not be obtained.
793 """
794
795 - def __init__(self, source, filename, MIMEType, length):
798
799
807
808
810 """Upload.SucceededEvent event is sent when the upload is received
811 successfully.
812
813 @author: Vaadin Ltd.
814 @author: Richard Lincoln
815 @version: 1.1.2
816 """
817
818 - def __init__(self, source, filename, MIMEType, length):
820
821
823 """Upload.StartedEvent event is sent when the upload is started to
824 received.
825
826 @author: Vaadin Ltd.
827 @author: Richard Lincoln
828 @version: 1.1.2
829 """
830
831 - def __init__(self, source, filename, MIMEType, contentLength):
832 super(StartedEvent, self).__init__(source)
833
834 self._filename = filename
835
836 self._type = MIMEType
837
838
839 self._length = contentLength
840
841
843 """Uploads where the event occurred.
844
845 @return: the source of the event.
846 """
847 return self.getSource()
848
849
851 """Gets the file name.
852
853 @return: the filename.
854 """
855 return self._filename
856
857
859 """Gets the MIME Type of the file.
860
861 @return: the MIME type.
862 """
863 return self._type
864
865
867 """@return: the length of the file that is being uploaded"""
868 return self._length
869