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.