This package provides two basic Zope 3 content components, File and Image, and their ZMI-compliant browser views.
You can add File objects from the common tasks menu in the ZMI.
>>> print http(r"""
... GET /@@contents.html HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """)
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: text/html;charset=utf-8
<BLANKLINE>
...
<title>Z3: </title>
...
<div class="box" id="commonTasks">
<h4>Add:</h4>
<div class="body">
...
<div class="content...">
<a href="http://localhost/@@+/action.html?type_name=zope.app.file.File"
class="">File</a>
</div>
...
Let's follow that link.
>>> print http(r""" ... GET /@@+/action.html?type_name=zope.app.file.File HTTP/1.1 ... Authorization: Basic mgr:mgrpw ... """, handle_errors=False) HTTP/1.1 303 See Other Content-Length: ... Location: http://localhost/+/zope.app.file.File= <BLANKLINE>
The file add form lets you specify the content type, the object name, and optionally upload the contents of the file.
>>> print http(r"""
... GET /+/zope.app.file.File= HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """)
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: text/html;charset=utf-8
<BLANKLINE>
...
<title>Z3: +</title>
...
...
<form action="http://localhost/+/zope.app.file.File%3D"
method="post" enctype="multipart/form-data">
<h3>Add a File</h3>
...<input class="textType" id="field.contentType"
name="field.contentType" size="20" type="text" value="" />...
...<input class="fileType" id="field.data" name="field.data" size="20"
type="file" />...
<div class="controls"><hr />
<input type="submit" value="Refresh" />
<input type="submit" value="Add"
name="UPDATE_SUBMIT" />
<b>Object Name</b>
<input type="text" name="add_input_name" value="" />
</div>
...
</form>
...
Let us upload a binary file.
>>> print http("""
... POST /+/zope.app.file.File%3D HTTP/1.1
... Authorization: Basic mgr:mgrpw
... Content-Type: multipart/form-data; boundary=---------------------------73793505419963331401738523176
...
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="field.contentType"
...
... application/octet-stream
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="field.data"; filename="hello.txt.gz"
... Content-Type: application/x-gzip
...
... \x1f\x8b\x08\x08\xcb\x48\xea\x42\x00\x03\x68\x65\x6c\x6c\x6f\x2e\
... \x74\x78\x74\x00\xcb\x48\xcd\xc9\xc9\xe7\x02\x00\x20\x30\x3a\x36\
... \x06\x00\x00\x00
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
...
... Add
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="add_input_name"
...
...
... -----------------------------73793505419963331401738523176--
... """)
HTTP/1.1 303 See Other
Content-Length: ...
Content-Type: text/html;charset=utf-8
Location: http://localhost/@@contents.html
<BLANKLINE>
...
Since we did not specify the object name in the form, Zope 3 will use the filename.
>>> response = http("""
... GET /hello.txt.gz HTTP/1.1
... """)
>>> print response
HTTP/1.1 200 OK
Content-Length: 36
Content-Type: application/octet-stream
<BLANKLINE>
...
Let's make sure the (binary) content of the file is correct
>>> response.getBody().encode('base64')
'H4sICMtI6kIAA2hlbGxvLnR4dADLSM3JyecCACAwOjYGAAAA\n'
Also, lets test a (bad) filename with full path that generates MS Internet Explorer, Zope should process it successfully and get the actual filename. Let's upload the same file with bad filename.
>>> print http("""
... POST /+/zope.app.file.File%3D HTTP/1.1
... Authorization: Basic mgr:mgrpw
... Content-Type: multipart/form-data; boundary=---------------------------73793505419963331401738523176
...
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="field.contentType"
...
... application/octet-stream
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="field.data"; filename="c:\\windows\\test.gz"
... Content-Type: application/x-gzip
...
... \x1f\x8b\x08\x08\xcb\x48\xea\x42\x00\x03\x68\x65\x6c\x6c\x6f\x2e\
... \x74\x78\x74\x00\xcb\x48\xcd\xc9\xc9\xe7\x02\x00\x20\x30\x3a\x36\
... \x06\x00\x00\x00
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
...
... Add
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="add_input_name"
...
...
... -----------------------------73793505419963331401738523176--
... """)
HTTP/1.1 303 See Other
Content-Length: ...
Content-Type: text/html;charset=utf-8
Location: http://localhost/@@contents.html
<BLANKLINE>
...
The file should be saved as "test.gz", let's check it name and contents.
>>> response = http(""" ... GET /test.gz HTTP/1.1 ... """) >>> print response HTTP/1.1 200 OK Content-Length: 36 Content-Type: application/octet-stream <BLANKLINE> ...>>> response.getBody().encode('base64') 'H4sICMtI6kIAA2hlbGxvLnR4dADLSM3JyecCACAwOjYGAAAA\n'
Let us now create a text file.
>>> print http(r""" ... POST /+/zope.app.file.File%3D HTTP/1.1 ... Authorization: Basic mgr:mgrpw ... Content-Type: multipart/form-data; boundary=---------------------------167769037320366690221542301033 ... ... -----------------------------167769037320366690221542301033 ... Content-Disposition: form-data; name="field.contentType" ... ... text/plain ... -----------------------------167769037320366690221542301033 ... Content-Disposition: form-data; name="field.data"; filename="" ... Content-Type: application/octet-stream ... ... ... -----------------------------167769037320366690221542301033 ... Content-Disposition: form-data; name="UPDATE_SUBMIT" ... ... Add ... -----------------------------167769037320366690221542301033 ... Content-Disposition: form-data; name="add_input_name" ... ... sample.txt ... -----------------------------167769037320366690221542301033-- ... """) HTTP/1.1 303 See Other Content-Length: ... Content-Type: text/html;charset=utf-8 Location: http://localhost/@@contents.html <BLANKLINE> ...
The file is initially empty, since we did not upload anything.
>>> print http("""
... GET /sample.txt HTTP/1.1
... """)
HTTP/1.1 200 OK
Content-Length: 0
Content-Type: text/plain
Last-Modified: ...
<BLANKLINE>
Since it is a text file, we can edit it directly in a web form.
>>> print http(r"""
... GET /sample.txt/edit.html HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: text/html;charset=utf-8
<BLANKLINE>
...
<title>Z3: sample.txt</title>
...
<form action="http://localhost/sample.txt/edit.html"
method="post" enctype="multipart/form-data">
<div>
<h3>Change a file</h3>
...<input class="textType" id="field.contentType" name="field.contentType"
size="20" type="text" value="text/plain" />...
...<textarea cols="60" id="field.data" name="field.data" rows="15" ></textarea>...
...
<div class="controls">
<input type="submit" value="Refresh" />
<input type="submit" name="UPDATE_SUBMIT"
value="Change" />
</div>
...
</form>
...
Files of type text/plain without any charset information can contain UTF-8 text. So you can use ASCII text.
>>> print http(r"""
... POST /sample.txt/edit.html HTTP/1.1
... Authorization: Basic mgr:mgrpw
... Content-Type: multipart/form-data; boundary=---------------------------165727764114325486311042046845
...
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="field.contentType"
...
... text/plain
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="field.data"
...
... This is a sample text file.
...
... It can contain US-ASCII characters.
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
...
... Change
... -----------------------------165727764114325486311042046845--
... """, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: text/html;charset=utf-8
<BLANKLINE>
...
<title>Z3: sample.txt</title>
...
<form action="http://localhost/sample.txt/edit.html"
method="post" enctype="multipart/form-data">
<div>
<h3>Change a file</h3>
<BLANKLINE>
<p>Updated on ...</p>
<BLANKLINE>
<div class="row">
...<input class="textType" id="field.contentType" name="field.contentType"
size="20" type="text" value="text/plain" />...
<div class="row">
...<textarea cols="60" id="field.data" name="field.data" rows="15"
>This is a sample text file.
<BLANKLINE>
It can contain US-ASCII characters.</textarea></div>
...
<div class="controls">
<input type="submit" value="Refresh" />
<input type="submit" name="UPDATE_SUBMIT"
value="Change" />
</div>
...
</form>
...
Here's the file
>>> print http(r""" ... GET /sample.txt HTTP/1.1 ... """) HTTP/1.1 200 OK Content-Length: ... Content-Type: text/plain Last-Modified: ... <BLANKLINE> This is a sample text file. <BLANKLINE> It can contain US-ASCII characters.
We can also use non-ASCII charactors in text file.
>>> print http("""
... POST /sample.txt/edit.html HTTP/1.1
... Authorization: Basic mgr:mgrpw
... Content-Type: multipart/form-data; boundary=---------------------------165727764114325486311042046845
...
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="field.contentType"
...
... text/plain
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="field.data"
...
... This is a sample text file.
...
... It can contain non-ASCII(UTF-8) characters, e.g. \xe2\x98\xbb (U+263B BLACK SMILING FACE).
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
...
... Change
... -----------------------------165727764114325486311042046845--
... """)
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: text/html;charset=utf-8
<BLANKLINE>
...
<title>Z3: sample.txt</title>
...
<form action="http://localhost/sample.txt/edit.html"
method="post" enctype="multipart/form-data">
<div>
<h3>Change a file</h3>
<BLANKLINE>
<p>Updated on ...</p>
<BLANKLINE>
<div class="row">
...<input class="textType" id="field.contentType" name="field.contentType"
size="20" type="text" value="text/plain" />...
<div class="row">
...<textarea cols="60" id="field.data" name="field.data" rows="15"
>This is a sample text file.
<BLANKLINE>
It can contain non-ASCII(UTF-8) characters, e.g. ... (U+263B BLACK SMILING FACE).</textarea></div>
...
<div class="controls">
<input type="submit" value="Refresh" />
<input type="submit" name="UPDATE_SUBMIT"
value="Change" />
</div>
...
</form>
...
Here's the file
>>> response = http(r""" ... GET /sample.txt HTTP/1.1 ... """) >>> print response HTTP/1.1 200 OK Content-Length: ... Content-Type: text/plain Last-Modified: ... <BLANKLINE> This is a sample text file. <BLANKLINE> It can contain non-ASCII(UTF-8) characters, e.g. ... (U+263B BLACK SMILING FACE).>>> u'\u263B' in response.getBody().decode('UTF-8') True
And you can explicitly specify the charset. Note that the browser form is always UTF-8.
>>> print http("""
... POST /sample.txt/edit.html HTTP/1.1
... Authorization: Basic mgr:mgrpw
... Content-Type: multipart/form-data; boundary=---------------------------165727764114325486311042046845
...
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="field.contentType"
...
... text/plain; charset=ISO-8859-1
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="field.data"
...
... This is a sample text file.
...
... It now contains Latin-1 characters, e.g. \xc2\xa7 (U+00A7 SECTION SIGN).
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
...
... Change
... -----------------------------165727764114325486311042046845--
... """)
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: text/html;charset=utf-8
<BLANKLINE>
...
<title>Z3: sample.txt</title>
...
<form action="http://localhost/sample.txt/edit.html"
method="post" enctype="multipart/form-data">
<div>
<h3>Change a file</h3>
<BLANKLINE>
<p>Updated on ...</p>
<BLANKLINE>
<div class="row">
...<input class="textType" id="field.contentType" name="field.contentType"
size="20" type="text" value="text/plain; charset=ISO-8859-1" />...
<div class="row">
...<textarea cols="60" id="field.data" name="field.data" rows="15"
>This is a sample text file.
<BLANKLINE>
It now contains Latin-1 characters, e.g. ... (U+00A7 SECTION SIGN).</textarea></div>
...
<div class="controls">
<input type="submit" value="Refresh" />
<input type="submit" name="UPDATE_SUBMIT"
value="Change" />
</div>
...
</form>
...
Here's the file
>>> response = http(r""" ... GET /sample.txt HTTP/1.1 ... """) >>> print response HTTP/1.1 200 OK Content-Length: ... Content-Type: text/plain; charset=ISO-8859-1 Last-Modified: ... <BLANKLINE> This is a sample text file. <BLANKLINE> It now contains Latin-1 characters, e.g. ... (U+00A7 SECTION SIGN).
Body is actually encoded in ISO-8859-1, and not UTF-8
>>> response.getBody().splitlines()[-1] 'It now contains Latin-1 characters, e.g. \xa7 (U+00A7 SECTION SIGN).'
The user is not allowed to specify a character set that cannot represent all the characters.
>>> print http("""
... POST /sample.txt/edit.html HTTP/1.1
... Authorization: Basic mgr:mgrpw
... Content-Type: multipart/form-data; boundary=---------------------------165727764114325486311042046845
...
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="field.contentType"
...
... text/plain; charset=US-ASCII
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="field.data"
...
... This is a slightly changed sample text file.
...
... It now contains Latin-1 characters, e.g. \xc2\xa7 (U+00A7 SECTION SIGN).
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
...
... Change
... -----------------------------165727764114325486311042046845--
... """, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: text/html;charset=utf-8
<BLANKLINE>
...
<title>Z3: sample.txt</title>
...
<form action="http://localhost/sample.txt/edit.html"
method="post" enctype="multipart/form-data">
<div>
<h3>Change a file</h3>
<BLANKLINE>
<p>The character set you specified (US-ASCII) cannot encode all characters in text.</p>
<BLANKLINE>
<div class="row">
...<input class="textType" id="field.contentType" name="field.contentType" size="20" type="text" value="text/plain; charset=US-ASCII" />...
<div class="row">
...<textarea cols="60" id="field.data" name="field.data" rows="15" >This is a slightly changed sample text file.
<BLANKLINE>
It now contains Latin-1 characters, e.g. ... (U+00A7 SECTION SIGN).</textarea></div>
...
<div class="controls">
<input type="submit" value="Refresh" />
<input type="submit" name="UPDATE_SUBMIT"
value="Change" />
</div>
...
</form>
...
Likewise, the user is not allowed to specify a character set that is not supported by Python.
>>> print http("""
... POST /sample.txt/edit.html HTTP/1.1
... Authorization: Basic mgr:mgrpw
... Content-Type: multipart/form-data; boundary=---------------------------165727764114325486311042046845
...
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="field.contentType"
...
... text/plain; charset=I-INVENT-MY-OWN
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="field.data"
...
... This is a slightly changed sample text file.
...
... It now contains just ASCII characters.
... -----------------------------165727764114325486311042046845
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
...
... Change
... -----------------------------165727764114325486311042046845--
... """, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: text/html;charset=utf-8
<BLANKLINE>
...
<title>Z3: sample.txt</title>
...
<form action="http://localhost/sample.txt/edit.html"
method="post" enctype="multipart/form-data">
<div>
<h3>Change a file</h3>
<BLANKLINE>
<p>The character set you specified (I-INVENT-MY-OWN) is not supported.</p>
<BLANKLINE>
<div class="row">
...<input class="textType" id="field.contentType" name="field.contentType" size="20" type="text" value="text/plain; charset=I-INVENT-MY-OWN" />...
<div class="row">
...<textarea cols="60" id="field.data" name="field.data" rows="15" >This is a slightly changed sample text file.
<BLANKLINE>
It now contains just ASCII characters.</textarea></div>
...
<div class="controls">
<input type="submit" value="Refresh" />
<input type="submit" name="UPDATE_SUBMIT"
value="Change" />
</div>
...
</form>
...
If you trick Zope and upload a file with a content type that does not match the file contents, you will not be able to access the edit view.
>>> print http(r""" ... GET /hello.txt.gz/@@edit.html HTTP/1.1 ... Authorization: Basic mgr:mgrpw ... """) HTTP/1.1 200 OK Content-Length: ... Content-Type: text/html;charset=utf-8 <BLANKLINE> ... <li>The character set specified in the content type (UTF-8) does not match file content.</li> ...
Filenames are not restricted to ASCII.
>>> print http("""
... POST /+/zope.app.file.File%3D HTTP/1.1
... Authorization: Basic mgr:mgrpw
... Content-Type: multipart/form-data; boundary=---------------------------73793505419963331401738523176
...
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="field.contentType"
...
... application/octet-stream
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="field.data"; filename="bj\xc3\xb6rn.txt.gz"
... Content-Type: application/x-gzip
...
... \x1f\x8b\x08\x08\xcb\x48\xea\x42\x00\x03\x68\x65\x6c\x6c\x6f\x2e\
... \x74\x78\x74\x00\xcb\x48\xcd\xc9\xc9\xe7\x02\x00\x20\x30\x3a\x36\
... \x06\x00\x00\x00
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
...
... Add
... -----------------------------73793505419963331401738523176
... Content-Disposition: form-data; name="add_input_name"
...
...
... -----------------------------73793505419963331401738523176--
... """)
HTTP/1.1 303 See Other
Content-Length: ...
Content-Type: text/html;charset=utf-8
Location: http://localhost/@@contents.html
<BLANKLINE>
...
Since we did not specify the object name in the form, Zope 3 will use the filename.
>>> response = http("""
... GET /bj%C3%B6rn.txt.gz HTTP/1.1
... """)
>>> print response
HTTP/1.1 200 OK
Content-Length: 36
Content-Type: application/octet-stream
<BLANKLINE>
...
When an HTML File page containing a head tag is visited, without a trailing slash, the base href isn't set. When visited with a slash, it is:
>>> print http(r""" ... POST /+/zope.app.file.File%3D HTTP/1.1 ... Authorization: Basic mgr:mgrpw ... Content-Length: 610 ... Content-Type: multipart/form-data; boundary=---------------------------32826232819858510771857533856 ... Referer: http://localhost:8081/+/zope.app.file.File= ... ... -----------------------------32826232819858510771857533856 ... Content-Disposition: form-data; name="field.contentType" ... ... text/html ... -----------------------------32826232819858510771857533856 ... Content-Disposition: form-data; name="field.data"; filename="" ... Content-Type: application/octet-stream ... ... ... -----------------------------32826232819858510771857533856 ... Content-Disposition: form-data; name="UPDATE_SUBMIT" ... ... Add ... -----------------------------32826232819858510771857533856 ... Content-Disposition: form-data; name="add_input_name" ... ... file.html ... -----------------------------32826232819858510771857533856-- ... """) HTTP/1.1 303 See Other ...>>> print http(r""" ... POST /file.html/edit.html HTTP/1.1 ... Authorization: Basic mgr:mgrpw ... Content-Length: 507 ... Content-Type: multipart/form-data; boundary=---------------------------10196264131256436092131136054 ... Referer: http://localhost:8081/file.html/edit.html ... ... -----------------------------10196264131256436092131136054 ... Content-Disposition: form-data; name="field.contentType" ... ... text/html ... -----------------------------10196264131256436092131136054 ... Content-Disposition: form-data; name="field.data" ... ... <html> ... <head></head> ... <body> ... <a href="eek.html">Eek</a> ... </body> ... </html> ... -----------------------------10196264131256436092131136054 ... Content-Disposition: form-data; name="UPDATE_SUBMIT" ... ... Change ... -----------------------------10196264131256436092131136054-- ... """) HTTP/1.1 200 OK ...>>> print http(r""" ... GET /file.html HTTP/1.1 ... Authorization: Basic mgr:mgrpw ... """) HTTP/1.1 200 OK ... <html> <head></head> <body> <a href="eek.html">Eek</a> </body> </html>>>> print http(r""" ... GET /file.html/ HTTP/1.1 ... Authorization: Basic mgr:mgrpw ... """) HTTP/1.1 200 OK ... <html> <head> <base href="http://localhost/file.html" /> </head> <body> <a href="eek.html">Eek</a> </body> </html>
Include information about which attributes changed in the IObjectModifiedEvent after upload.
This fixes https://bugs.launchpad.net/zope3/+bug/98483.