wcs/wcs/forms/common.ptl

483 lines
17 KiB
Plaintext

# w.c.s. - web application for online forms
# Copyright (C) 2005-2010 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import base64
import mimetypes
import json
from quixote import get_publisher, get_request, get_response, get_session, redirect
from quixote.directory import Directory, AccessControlled
from wcs import formdata
from wcs.fields import WidgetField
from qommon import template
from qommon import get_logger
from qommon.form import *
from wcs.anonylink import AnonymityLink
from qommon.strftime import strftime
from qommon.admin.texts import TextsDirectory
from qommon import errors
class FormStatusPage(Directory):
_q_exports = ['', 'download']
_q_extra_exports = []
def html_top [html] (self, title = None):
template.html_top(title = title, default_org = _('Forms'))
def __init__(self, formdef, filled, register_workflow_subdirs=True):
get_publisher().substitutions.feed(filled)
self.formdef = formdef
self.filled = filled
for q in self._q_extra_exports:
if not q in self._q_exports:
self._q_exports.append(q)
if self.formdef.workflow and register_workflow_subdirs:
for name, directory in self.filled.get_workflow_subdirectories():
self._q_exports.append(name)
setattr(self, name, directory)
def check_auth(self):
session = get_session()
mine = False
user = get_request().user
if user:
if user.anonymous:
anonylink = AnonymityLink.select(
lambda x: x.name_identifier == session.name_identifier and
x.formdata_type == 'form' and
x.formdata_def_id == self.formdef.id)
if len(anonylink) == 1:
mine = True
elif self.filled.is_submitter(user):
mine = True
if not self.filled.formdef.is_user_allowed_read(user, self.filled):
if not user:
raise errors.AccessUnauthorizedError()
raise errors.AccessForbiddenError()
return mine
def _q_index [html] (self):
mine = self.check_auth()
if get_request().get_header(str('Accept')) == 'application/json':
return self.export_to_json()
get_logger().info('form %s - id: %s - view' % (self.formdef.name, self.filled.id))
if self.formdef.only_allow_one:
self.html_top(self.formdef.name)
else:
self.html_top('%s - %s' % (self.formdef.name, self.filled.id))
if self.filled.receipt_time is not None:
tm = misc.localstrftime(self.filled.receipt_time)
else:
tm = '???'
'<div id="receipt-intro">'
if self.formdef.only_allow_one:
TextsDirectory.get_html_text('form-recorded-allow-one', vars={'date': tm})
else:
TextsDirectory.get_html_text('form-recorded',
vars={'date': tm, 'number': self.filled.id})
if mine:
handling_role = self.filled.get_handling_role()
if handling_role and handling_role.details:
endpoint_status = self.formdef.workflow.get_endpoint_status()
'<p>'
if self.filled.status in endpoint_status:
_('Your case is handled by:')
else:
_('Your case has been handled by:')
'</p>'
'<p id="receiver">'
htmltext(handling_role.details.replace(str('\n'), str('<br />')))
'</p>'
'</div>'
self.receipt()
if self.formdef.workflow:
htmltext(self.filled.display_workflow_message())
self.history()
form = None
session = get_session()
user = get_request().user
form = self.filled.get_workflow_form(user)
if form:
form.add_captcha()
if form and form.is_submitted():
if not form.has_errors():
url = self.submit(form, comment_only = True)
if not form.has_errors():
if url is None:
url = get_request().get_url()
response = get_response()
response.set_status(303)
response.headers[str('location')] = url
response.content_type = 'text/plain'
return "Your browser should redirect you"
if form:
form.render()
self.form_status_buttons()
def export_to_json(self):
data = {}
if self.filled.receipt_time is not None:
data['receipt_time'] = strftime('%Y-%m-%dT%H:%M:%S', self.filled.receipt_time)
try:
user = get_publisher().user_class.get(self.filled.user_id)
except KeyError:
user = None
# this is custom code so it is possible to mark forms as anonyms, this
# is done through the VoteAnonymity field, this is very specific but
# isn't generalised yet into an useful extension mechanism, as it's not
# clear at the moment what could be useful.
for f in self.formdef.fields:
if f.key == 'vote-anonymity':
user = None
break
if user:
data['user'] = {'id': user.id, 'name': user.display_name}
if self.formdef.signing:
if self.filled.signature:
data['signature'] = {'status': 'valid',
'issuer': self.filled.signature['issuer'],
'subject': self.filled.signature['subject']}
else:
data['signature'] = {'status': 'missing'}
data['fields'] = {}
for f in self.formdef.fields:
if not f.varname:
continue
if not self.filled.data.has_key(f.id):
continue
if f.store_display_value and ('%s_display' % f.id) in self.filled.data:
value = self.filled.data['%s_display' % f.id]
else:
value = self.filled.data[f.id]
if value is None:
value = ''
if not hasattr(f, str('get_view_value')):
continue
data['fields'][f.varname] = unicode(value)
wf_status = self.filled.get_visible_status()
if wf_status:
data['workflow'] = {}
data['workflow']['status'] = {'id': wf_status.id, 'name': wf_status.name}
get_response().set_content_type('application/json')
return json.dumps(data)
def form_status_buttons [html] (self):
if not get_response().iframe_mode:
'<div class="back-home-button">'
'<a href="%s">%s</a>' % (get_publisher().get_root_url(), _('Back Home'))
'</div>'
def history [html] (self):
if not self.filled.evolution:
return
if not self.formdef.is_user_allowed_read_status_and_history(get_request().user, self.filled):
return
'<div class="bo-block">'
'<h2>%s</h2>' % _('Log')
'<dl id="evolutions">'
hidden = False
for evo in self.filled.evolution:
if evo.status:
wf_status = self.filled.get_status(evo.status)
if wf_status and not wf_status.is_visible(self.filled, get_request().user):
hidden = True
else:
hidden = False
if hidden:
continue
"<dt>%s" % misc.localstrftime(evo.time)
if evo.who:
evo_author = None
if evo.who == '_submitter':
evo_author = _('Original Submitter')
if self.filled.is_submitter(get_request().user):
evo_author = _('Yourself')
elif self.filled.user_id:
try:
evo_author = _('Original Submitter (%s)') % \
get_publisher().user_class.get(self.filled.user_id).display_name
except KeyError:
pass
else:
try:
evo_author = evo.get_author_name()
except KeyError:
pass
if evo_author:
' <span class="user">%s</span>' % evo_author
'</dt>'
'<dd>'
if evo.status:
'<span class="status">%s</span> ' % self.filled.get_status_label(evo.status)
if evo.comment:
if evo.comment.startswith(str('#pre')):
'<div class="comment"><pre>%s</pre></div>' % evo.comment[4:]
else:
'<div class="comment">'
'<p>'
htmltext('<p>') + htmltext('\n').join(
[(x or htmltext('</p><p>')) for x in evo.comment.splitlines()])
'</p>'
'</div>'
for t in evo.display_parts():
t
'</dd>'
'</dl>'
'</div>' # bo-block
def check_receiver(self):
session = get_session()
if not session or not session.user:
raise errors.AccessUnauthorizedError()
user = get_request().user
if self.filled.formdef is None:
raise errors.AccessForbiddenError()
if not self.filled.formdef.is_user_allowed_read(user, self.filled):
raise errors.AccessForbiddenError()
return user
def receipt [html] (self,
always_include_user = False,
show_signature = True,
show_status = True,
form_url = ''):
user = get_request().user
if not always_include_user and get_request().user and \
get_request().user.id == self.filled.user_id:
user = None
else:
try:
user = get_publisher().user_class.get(self.filled.user_id)
except KeyError:
user = None
# this is custom code so it is possible to mark forms as anonyms, this
# is done through the VoteAnonymity field, this is very specific but
# isn't generalised yet into an useful extension mechanism, as it's not
# clear at the moment what could be useful.
for f in self.formdef.fields:
if f.key == 'vote-anonymity':
user = None
break
'<div class="dataview">'
if self.formdef.signing and not self.filled.signature:
'<div class="errornotice">%s</div>' % \
_("Warning form not signed")
if user:
'<p><span class="label">%s</span>' % _('User name')
'<span class="value">%s</span></p>' % user.display_name
if self.filled.signature:
'<span class="label">%s</span>' % _("The form is electronically signed")
if show_signature:
'<div class="signok">'
'<span class="label">%s</span>' % _("Signature")
issuer = self.filled.signature["issuer"]
subject = self.filled.signature["subject"]
if issuer:
'<span class="value">%s %s</span>' % (_('Issuer:'), issuer)
if subject:
'<span class="value">%s %s</span>' % (_('Subject:'), subject)
'</div>'
on_page = False
on_disabled_page = False
for f in self.formdef.fields:
if f.type == 'page':
on_disabled_page = False
if not f.is_visible(self.filled.data, self.formdef):
on_disabled_page = True
form_field = False
for f1 in self.formdef.fields[self.formdef.fields.index(f)+1:]:
if f1.key == 'page':
break
if isinstance(f1, WidgetField):
form_field = True
break
if form_field is False:
on_disabled_page = True
if on_disabled_page:
continue
if f.type == 'page':
if on_page:
'</div>'
'<div class="page">'
'<h3>%s</h3>' % f.label
on_page = True
continue
if not self.filled.data.has_key(f.id):
continue
if f.store_display_value and ('%s_display' % f.id) in self.filled.data:
value = self.filled.data['%s_display' % f.id]
else:
value = self.filled.data[f.id]
if value is None or value == '':
continue
if not hasattr(f, str('get_view_value')):
continue
'<p><span class="label">%s</span> ' % f.label
'<span class="value">'
s = f.get_view_value(value)
s = s.replace(str('[download]'), str('%sdownload' % form_url))
s
'</span></p>'
if on_page:
'</div>'
if show_status and self.formdef.is_user_allowed_read_status_and_history(
get_request().user, self.filled):
wf_status = self.filled.get_visible_status()
if wf_status:
'<p><span class="label">%s</span> ' % _('Status')
'<span class="value">%s</span></p>' % wf_status.name
'</div>'
def status [html] (self):
user = self.check_receiver()
form = None
try:
form = self.filled.get_workflow_form(user)
except:
# XXX: probably because there are mixed forms, with and without
# workflows
form = Form()
if form and form.is_submitted():
url = self.submit(form)
if url is None:
url = get_request().get_url()
response = get_response()
response.set_status(303)
response.headers[str('location')] = url
response.content_type = 'text/plain'
return "Your browser should redirect you"
get_logger().info('form %s - id: %s - view status' % (self.formdef.name, self.filled.id))
self.html_top('%s - %s' % (self.formdef.name, self.filled.id))
if self.filled.receipt_time is not None:
tm = misc.localstrftime(self.filled.receipt_time)
else:
tm = '???'
"<p>"
_('The form has been recorded on %(date)s with the number %(number)s.') % {
'date': tm, 'number': self.filled.id}
"</p>"
self.receipt(always_include_user = True)
if self.formdef.workflow:
htmltext(self.filled.display_workflow_message())
self.history()
if form:
form.render()
'<a href="..">%s</a>' % _('Back to Listing')
def submit(self, form, comment_only = False):
status = None
current_status = self.filled.status
user = get_request().user
next_url = self.filled.handle_workflow_form(user, form)
if next_url:
return next_url
if form.has_errors():
return
if current_status != self.filled.status:
get_logger().info('form %s - id: %s - status -> %s' % (
self.formdef.name, self.filled.id, self.filled.status))
def download(self):
if not self.filled.formdef.is_user_allowed_read(get_request().user, self.filled):
self.check_receiver()
try:
fn = get_request().form['f']
f = self.filled.data[fn]
except (KeyError, ValueError):
raise errors.TraversalError()
file = self.filled.data[fn]
if not hasattr(file, 'content_type'):
raise errors.TraversalError()
response = get_response()
if file.content_type:
response.set_content_type(file.content_type)
else:
response.set_content_type('application/octet-stream')
if file.charset:
response.set_charset(file.charset)
if file.base_filename:
if file.content_type.startswith('image/'):
response.set_header(
'content-disposition', 'inline; filename="%s"' % file.base_filename)
else:
response.set_header(
'content-disposition', 'attachment; filename="%s"' % file.base_filename)
return file.get_file_pointer().read()
def _q_traverse(self, path):
get_response().breadcrumb.append(
(str(self.filled.id) + '/',str(self.filled.id)))
return super(FormStatusPage, self)._q_traverse(path)