==================
 Files of persons
==================

It is possible to upload files for persons and download them
afterwards.

Setup
=====

>>> from icemac.addressbook.testing import create_addressbook
>>> from z3c.etestbrowser.wsgi import ExtendedTestBrowser as Browser
>>> create_addressbook()
<AddressBook u'ab' (u'test address book')>
>>> browser = Browser()

To add a file, a person is needed, so create one as an editor:

>>> from icemac.addressbook.testing import get_messages
>>> browser.addHeader('Authorization', 'Basic editor:editor')
>>> browser.open('http://localhost/++skin++AddressBook/ab')
>>> browser.getLink('person').click()
>>> browser.getControl('last name').value = u'Tester'
>>> browser.getControl('Add').click()
>>> get_messages(browser)
['"Tester" added.']

Add a file
==========

Adding a file is possible on the person edit form:

>>> from StringIO import StringIO
>>> browser.getLink('Tester').click()
>>> browser.getLink('file').click()
>>> browser.getControl('file').add_file(
...     StringIO('File contents'), 'text/plain', 'test.txt')

Deciding to cancel adding redirects to the person edit form:

>>> browser.getControl('Cancel').click()
>>> get_messages(browser)
['Addition canceled.']
>>> browser.url
'http://localhost/++skin++AddressBook/ab/Person'
>>> browser.getControl('file')
Traceback (most recent call last):
LookupError: label 'file'

Uploading a file is required:

>>> browser.getLink('file').click()
>>> browser.getControl('Add').click()
>>> get_messages(browser)
[]
>>> browser.url
'http://localhost/++skin++AddressBook/ab/Person/@@addFile.html'
>>> print browser.contents
<!DOCTYPE ...
...file: <div class="error">Required input is missing.</div>...

Really adding the file shows the file on the person edit form:

>>> from icemac.addressbook.testing import write_temp_file
>>> browser.getLink('file').click()
>>> fd, filename = write_temp_file('File contents', '.txt')
>>> browser.getControl('file').add_file(fd, 'text/plain', filename)
>>> browser.getControl('Add').click()
>>> get_messages(browser) == ['"%s" added.' % filename]
True

The new file is shown there including a widget displaying the
normalized name and mime type:

>>> browser.getControl('file name').value == filename
True
>>> browser.getControl('Mime Type').value
'text/plain'
>>> browser.getControl('file', index=1).value is None
True

We need to keep the current URL because after downloading we want to
come back here, but browser.goBack() does a page reload causing the
file beeing added again:

>>> url = browser.url

There is also a download link for the file:

>>> browser.getLink('Download file').click()
>>> browser.isHtml
False
>>> browser.headers['content-type']
'text/plain'
>>> browser.headers['content-disposition'] == 'attachment; filename=' + filename
True
>>> browser.headers['content-length']
'13'
>>> browser.contents
'File contents'
>>> browser.open(url)

Edit a file
===========

It is possible to change the name and mime type of the file, the name
and mime type of the dowloaded file are changed accordingly. Spaces in
the file name are replaced by underscores:

>>> browser.getControl('name', index=2).value = 'my nice file.txt'
>>> browser.getControl('Mime Type').value = 'text/example'
>>> browser.getControl('Apply').click()
>>> get_messages(browser)
['Data successfully updated.']
>>> browser.url
'http://localhost/++skin++AddressBook/ab'
>>> browser.getLink('Tester').click()
>>> browser.getLink('Download file').click()
>>> browser.headers['content-type']
'text/example'
>>> browser.headers['content-disposition']
'attachment; filename=my_nice_file.txt'
>>> browser.goBack()

It is possible to upload a new file, this changes the name and the
mime type if necessary. When the browser does not know the mime type
and sends ``application/octet-stream``q, the mime type is guessed using
the file extension and file contents:

>>> fd, filename = write_temp_file('special data, blah', '.js')
>>> browser.getControl('file', index=1).add_file(fd, 'application/octet-stream',
...     filename)
>>> browser.getControl('Apply').click()
>>> get_messages(browser)
['Data successfully updated.']
>>> browser.url
'http://localhost/++skin++AddressBook/ab'
>>> browser.getLink('Tester').click()
>>> browser.getControl('file name').value == filename
True
>>> browser.getControl('Mime Type').value
'application/javascript'

The downloaded file behaves accordingly:

>>> browser.getLink('Download file').click()
>>> browser.headers['content-type']
'application/javascript'
>>> browser.headers['content-disposition'] == 'attachment; filename=' + filename
True
>>> browser.contents
'special data, blah'
>>> browser.goBack()

The mime type is optional, if the browser of the client does not send
a content type and zope.mimetype can't determine the mime type from
the filename or file content, it is set to ``application/octet-stream``.

>>> fd, filename = write_temp_file('Ä, no content type, huh', '')
>>> browser.getControl('file', index=1).add_file(fd, '', filename)
>>> browser.getControl('Apply').click()
>>> get_messages(browser)
['Data successfully updated.']
>>> browser.url
'http://localhost/++skin++AddressBook/ab'
>>> browser.getLink('Tester').click()
>>> browser.getControl('Mime Type').value
'application/octet-stream'

If the user deletes the mime type in the form, the type at download is
set to ``application/octet-stream``.

>>> browser.getControl('Mime Type').value = ''
>>> browser.getControl('Apply').click()
>>> get_messages(browser)
['Data successfully updated.']
>>> browser.url
'http://localhost/++skin++AddressBook/ab'
>>> browser.getLink('Tester').click()
>>> browser.getControl('Mime Type').value
''
>>> browser.getLink('Download file').click()
>>> browser.headers['content-type']
'application/octet-stream'
>>> browser.goBack()

Upload another file
===================

It is possible to upload, change and download more than one file.

Upload
------

Python reads small files into a StringIO and does not store them on
hard disk, so let's put the data into a StringIO:

>>> browser.getLink('file').click()
>>> browser.getControl('file').add_file(
...     StringIO('2nd file'), 'text/plain', '2nd.txt')
>>> browser.getControl('Add').click()
>>> get_messages(browser)
['"2nd.txt" added.']
>>> browser.url
'http://localhost/++skin++AddressBook/ab/Person'
>>> browser.getControl('name', index=3).value == '2nd.txt'
True
>>> browser.getControl('Mime Type', index=1).value
'text/plain'
>>> browser.getControl('file', index=1).value is None
True

We need to keep the current URL because after downloading we want to
come back here, but browser.goBack() does a page reload causing the
file beeing added again:

>>> url = browser.url

Download
--------

>>> browser.getLink('Download file', index=1).click()
>>> browser.headers['content-type']
'text/plain'
>>> browser.headers['content-disposition'] == 'attachment; filename=2nd.txt'
True
>>> browser.headers['content-length']
'8'
>>> browser.contents
'2nd file'
>>> browser.open(url)

Change
------

>>> fd, filename3 = write_temp_file('3rd try', '.txt')
>>> browser.getControl('file', index=3).add_file(fd, 'text/enriched', filename3)
>>> browser.getControl('Apply').click()
>>> get_messages(browser)
['Data successfully updated.']
>>> browser.url
'http://localhost/++skin++AddressBook/ab'
>>> browser.getLink('Tester').click()
>>> browser.getControl('file name', index=1).value == filename3
True
>>> browser.getControl('Mime Type', index=1).value
'text/enriched'

Download changed file
---------------------

>>> browser.getLink('Download file', index=1).click()
>>> browser.headers['content-type']
'text/enriched'
>>> browser.headers['content-disposition'] == 'attachment; filename='+filename3
True
>>> browser.contents
'3rd try'
>>> browser.goBack()


Delete a file
=============

Files can be deleted using the ``Delete single entry`` button.

>>> browser.getControl('file name', index=1)
<Control name='IcemacAddressbookFileFileFile_1.widgets.IcemacAddressbookFileFileFile_1.name' type='text'>
>>> person_edit_form_url = browser.url
>>> browser.getControl('Delete single entry').click()
>>> browser.url
'http://localhost/++skin++AddressBook/ab/Person/@@delete_entry.html'
>>> entries = browser.getControl('Entries').displayOptions
>>> entries == ['postal address -- Germany',
...             'phone number -- none',
...             'e-mail address -- none',
...             'home page address -- none',
...             'file -- ' + filename,
...             'file -- ' + filename3]
True

If the user changed his mind, he can use the `cancel` button and gets
redirected to the person edit page:

>>> browser.getControl('Cancel').click()
>>> get_messages(browser)
['No changes were applied.']
>>> browser.url == person_edit_form_url
True

To go back to the delete form, the user has to click the apporiate
delete button again and choose an entry:

>>> browser.getControl('Delete single entry').click()
>>> entries == browser.getControl('Entries').displayOptions
True
>>> browser.getControl(filename).click()
>>> browser.getControl('Delete entry').click()

Before the entry gets deleted, an `are you sure` formular is presented
where the user can choose to cancel. In this case he is sent back to
the person edit form:

>>> browser.getControl('No, cancel').click()
>>> get_messages(browser)
['Deletion canceled.']
>>> browser.url == person_edit_form_url
True

To really delete the file we do the procedure again and click
`delete` which really deletes the file and sends the user back to
the person edit form:

>>> browser.getControl('Delete single entry').click()
>>> entries == browser.getControl('Entries').displayOptions
True
>>> browser.getControl(filename).click()
>>> browser.getControl('Delete entry').click()
>>> browser.getControl('Yes, delete').click()
>>> get_messages(browser) == ['"%s" deleted.' % filename]
True
>>> browser.url == person_edit_form_url
True

The deleted file is no longer displayed (there is now only one file):

>>> browser.getControl('file name', index=1)
Traceback (most recent call last):
IndexError: list index out of range

Visitor
=======

Visitors can only download files, they cannot create them, edit them
nor delete them.

>>> visitor = Browser()
>>> visitor.addHeader('Authorization', 'Basic visitor:visitor')
>>> visitor.open('http://localhost/++skin++AddressBook/ab')
>>> visitor.getLink('Tester').click()

Create
------

A visitor can't create a file (the only file link points to download
not to ``add file``), directly accessing the URL is also forbidden:

>>> visitor.getLink('file')
<Link text='Download file'
      url='http://localhost/++skin++AddressBook/ab/Person/File-2/download.html'>
>>> visitor.open(visitor.url + '/@@addFile.html')
Traceback (most recent call last):
HTTPError: HTTP Error 403: Forbidden


Edit or delete
--------------

A visitor can't edit or delete a file. To demonstrate this let's
create a new file as editor first:

>>> browser.getLink('file').click()
>>> fd, filename = write_temp_file('for visitor visible', '.txt')
>>> browser.getControl('file').add_file(fd, 'text/plain', filename)
>>> browser.getControl('Add').click()
>>> get_messages(browser) == ['"%s" added.' % filename]
True

There are neither any input widgets nor a delete button:

>>> from icemac.addressbook.testing import get_all_control_names
>>> visitor.open('http://localhost/++skin++AddressBook/ab')
>>> visitor.getLink('Tester').click()
>>> get_all_control_names(visitor)
['form.buttons.apply', 'form.buttons.cancel']

Even trying to directly access the url fails:

>>> url = visitor.url
>>> visitor.open(url + '/@@delete_entry.html')
Traceback (most recent call last):
HTTPError: HTTP Error 403: Forbidden
>>> visitor.open(url + '/File/@@delete.html')
Traceback (most recent call last):
HTTPError: HTTP Error 403: Forbidden


Download
--------

A visitor can download any file:

>>> visitor.open('http://localhost/++skin++AddressBook/ab')
>>> visitor.getLink('Tester').click()
>>> visitor.getLink('Download file').click()
>>> visitor.headers['content-type']
'text/plain'
>>> visitor.headers['content-disposition'] == 'attachment; filename=' + filename
True
>>> visitor.contents
'for visitor visible'
>>> visitor.goBack()
