====================
Uploading a new file
====================

There's a simple view for uploading a new file.  Let's try it:

  >>> from StringIO import StringIO

  >>> sio = StringIO("some text")
  >>> sio.filename = "plain.txt"
  >>> sio.headers = {"Content-Type": "text/plain; charset=utf-8",
  ...                "Content-Disposition": 'attachment; filename="plain.txt"'}

  >>> print http("""
  ... POST /@@+/zope.file.File HTTP/1.1
  ... Authorization: Basic mgr:mgrpw
  ... """, form={"form.data": sio,
  ...            "form.actions.add": "Add"}, handle_errors=False)
  HTTP/1.1 303 ...

Now, let's request the download view of the file object and check the
result:

  >>> print http("""
  ... GET /plain.txt/@@download HTTP/1.1
  ... Authorization: Basic mgr:mgrpw
  ... """, handle_errors=False)
  HTTP/1.1 200 Ok
  Content-Disposition: attachment; filename="plain.txt"
  Content-Length: 9
  Content-Type: text/plain;charset=utf-8
  <BLANKLINE>
  some text

We'll peek into the database to make sure the object implements the
expected MIME type interface:

  >>> from zope.mimetype import types
  >>> ob = getRootFolder()["plain.txt"]
  >>> types.IContentTypeTextPlain.providedBy(ob)
  True

We can upload new data into our file object as well:

  >>> sio = StringIO("new text")
  >>> sio.filename = "stuff.txt"
  >>> sio.headers = {"Content-Type": "text/plain; charset=utf-8",
  ...                "Content-Disposition": 'attachment; filename="stuff.txt"'}

  >>> print http("""
  ... POST /plain.txt/@@edit.html HTTP/1.1
  ... Authorization: Basic mgr:mgrpw
  ... """, form={"form.data": sio,
  ...            "form.actions.edit": "Edit"}, handle_errors=False)
  HTTP/1.1 200 ...

Now, let's request the download view of the file object and check the
result:

  >>> print http("""
  ... GET /plain.txt/@@download HTTP/1.1
  ... Authorization: Basic mgr:mgrpw
  ... """, handle_errors=False)
  HTTP/1.1 200 Ok
  Content-Disposition: attachment; filename="plain.txt"
  Content-Length: 8
  Content-Type: text/plain;charset=utf-8
  <BLANKLINE>
  new text

If we upload a file that has imprecise content type information (as we
expect from browsers generally, and MSIE most significantly), we can
see that the MIME type machinery will improve the information where
possible:

  >>> sio = StringIO("<?xml version='1.0' encoding='utf-8'?>\n"
  ...                "<html>...</html>\n")
  >>> sio.filename = "simple.html"
  >>> sio.headers = {
  ...     "Content-Type": "text/html; charset=utf-8",
  ...     "Content-Disposition": 'attachment; filename="simple.html"',
  ...     }

  >>> print http("""
  ... POST /@@+/zope.file.File HTTP/1.1
  ... Authorization: Basic mgr:mgrpw
  ... """, form={"form.data": sio,
  ...            "form.actions.add": "Add"}, handle_errors=False)
  HTTP/1.1 303 ...

Again, we'll request the download view of the file object and check
the result:

  >>> print http("""
  ... GET /simple.html/@@download HTTP/1.1
  ... Authorization: Basic mgr:mgrpw
  ... """, handle_errors=False)
  HTTP/1.1 200 Ok
  Content-Disposition: attachment; filename="simple.html"
  Content-Length: 56
  Content-Type: application/xhtml+xml;charset=utf-8
  <BLANKLINE>
  <?xml version='1.0' encoding='utf-8'?>
  <html>...</html>
  <BLANKLINE>

Further, if a browser is bad and sends a full path as the file name (as
sometimes happens in many browsers, apparently), the name is correctly
truncated and changed.

  >>> sio = StringIO("<?xml version='1.0' encoding='utf-8'?>\n"
  ...                "<html>...</html>\n")
  >>> sio.filename = r"C:\Documents and Settings\Joe\naughty name.html"
  >>> sio.headers = {
  ...     "Content-Type": "text/html; charset=utf-8",
  ...     "Content-Disposition": 'attachment; filename=%s' % sio.filename,
  ...     }

  >>> print http("""
  ... POST /@@+/zope.file.File HTTP/1.1
  ... Authorization: Basic mgr:mgrpw
  ... """, form={"form.data": sio,
  ...            "form.actions.add": "Add"}, handle_errors=False)
  HTTP/1.1 303 ...

Again, we'll request the download view of the file object and check
the result:

  >>> print http("""
  ... GET /naughty%20name.html/@@download HTTP/1.1
  ... Authorization: Basic mgr:mgrpw
  ... """, handle_errors=False)
  HTTP/1.1 200 Ok
  Content-Disposition: attachment; filename="naughty name.html"
  Content-Length: 56
  Content-Type: application/xhtml+xml;charset=utf-8
  <BLANKLINE>
  <?xml version='1.0' encoding='utf-8'?>
  <html>...</html>
  <BLANKLINE>

In zope.file <= 0.5.0, a redundant ObjectCreatedEvent was fired in the
Upload view.  We'll demonstrate that this is no longer the case.

  >>> import zope.component
  >>> from zope.file.interfaces import IFile
  >>> from zope.lifecycleevent import IObjectCreatedEvent

We'll register a subscriber for IObjectCreatedEvent that simply increments
a counter.

  >>> count = 0
  >>> def inc(*args):
  ...   global count; count += 1
  >>> zope.component.provideHandler(inc, (IFile, IObjectCreatedEvent))

  >>> print http("""
  ... POST /@@+/zope.file.File HTTP/1.1
  ... Authorization: Basic mgr:mgrpw
  ... """, form={"form.data": sio,
  ...            "form.actions.add": "Add"}, handle_errors=False)
  HTTP/1.1 303 ...

The subscriber was called only once.

  >>> print count
  1
