wcs/wcs/admin/forms.ptl

1088 lines
43 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/>.
try:
import elementtree.ElementTree as ET
except ImportError:
try:
import xml.etree.ElementTree as ET
except ImportError:
ET = None
try:
import M2Crypto
except ImportError:
M2Crypto = None
import tarfile
import time
from cStringIO import StringIO
from quixote import get_response, redirect
from quixote.directory import Directory
from qommon import misc
from qommon.errors import *
from qommon.form import *
from qommon.admin.menu import html_top, command_icon, error_page
from qommon import get_logger
from qommon import tokens
from qommon.afterjobs import AfterJob
from qommon import emails
from wcs.formdata import FormData, Evolution
from wcs.formdef import FormDef, FormField
from wcs.categories import Category
from wcs.roles import Role, logged_users_role, get_user_roles
from wcs.workflows import Workflow
from fields import FieldWidget, FieldDefPage, FieldsDirectory
def indent(elem, level=0):
# in-place prettyprint formatter
# http://effbot.org/zone/element-lib.htm#prettyprint
i = "\n" + level*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
for elem in elem:
indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def get_categories():
t = sorted([(misc.simplify(x.name), x.id, x.name) for x in Category.select()])
return [x[1:] for x in t]
def get_workflows(condition=lambda x: True):
t = sorted([(misc.simplify(x.name), x.id, x.name) for x in Workflow.select() if condition(x)])
return [x[1:] for x in t]
class FormDefUI:
def __init__(self, formdef):
self.formdef = formdef
def new_form_ui(self):
form = Form(enctype='multipart/form-data')
if self.formdef:
formdef = self.formdef
else:
formdef = FormDef()
form.add(StringWidget, 'name', title = _('Form Title'), required=True, size=40,
value = formdef.name)
categories = get_categories()
if categories:
form.add(SingleSelectWidget, 'category_id', title = _('Category'),
value = formdef.category_id,
options = [(None, '---')] + categories)
workflows = get_workflows()
if workflows:
form.add(SingleSelectWidget, 'workflow_id', title = _('Workflow'),
value = formdef.workflow_id,
options = [(None, _('Default Workflow'))] + workflows)
form.add(SingleSelectWidget, 'receiver_id', title = _('Recipient Role'), required = True,
value = formdef.receiver_id,
options = get_user_roles())
form.add(WidgetList, 'roles', title = _('Sender Roles'), element_type = SingleSelectWidget,
hint = _('Only show this form to the given roles.'),
value = formdef.roles,
add_element_label = _('Add Role'),
element_kwargs = {'render_br': False,
'options': [('', '---'),
(logged_users_role().id, logged_users_role().name)] + get_user_roles()})
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
def submit_form(self, form):
if self.formdef:
formdef = self.formdef
else:
formdef = FormDef()
name = form.get_widget('name').parse()
formdefs_name = [x.name for x in FormDef.select(ignore_errors=True) if x.id != formdef.id]
if name in formdefs_name:
form.get_widget('name').set_error(_('This name is already used'))
raise ValueError()
for f in ('name', 'confirmation', 'signing', 'acl_read',
'only_allow_one', 'receiver_id', 'category_id', 'disabled',
'allow_drafts', 'workflow_id', 'private_status_and_history',
'disabled_redirection', 'always_advertise',
'publication_date', 'expiration_date'):
widget = form.get_widget(f)
if widget:
setattr(formdef, f, widget.parse())
roles = form.get_widget('roles').parse()
if roles:
formdef.roles = [x for x in roles if x]
else:
formdef.roles = []
def lax_int(s):
try:
return int(s)
except ValueError:
return -1
if not formdef.fields:
formdef.fields = []
if form.get_widget('fields'):
new_fields = []
for field in form.get_widget('fields').parse():
if not field['label']:
if field['id']:
formdef.fields = [x for x in formdef.fields if x.id != field['id']]
continue
if field['id']:
form_field = [x for x in formdef.fields if x.id == field['id']][0]
else:
form_field = FormField()
if formdef.fields or new_fields:
form_field.id = str(max([
lax_int(x.id) for x in formdef.fields + new_fields]) + 1)
else:
form_field.id = '1'
form_field.label = field['label']
form_field.type = field['type']
new_fields.append(form_field)
formdef.fields = new_fields
formdef.store()
return formdef
class FieldDefPage(FieldDefPage):
section = 'forms'
wsf_support = True
class FieldsDirectory(FieldsDirectory):
section = 'forms'
field_def_page_class = FieldDefPage
def index_bottom [html] (self):
if hasattr(self.objectdef, str('disabled')) and self.objectdef.disabled:
'<p>'
_('This form is currently disabled.')
if hasattr(self.objectdef, str('disabled_redirection')) \
and self.objectdef.disabled_redirection:
' (<a href="%s">' % self.objectdef.disabled_redirection
_('redirection')
'</a>)'
' <a href="../enable?back=fields">%s</a>' % _('Enable')
'</p>'
class FormDefPage(Directory):
_q_exports = ['', 'fields', 'delete', 'duplicate', 'export',
'archive', 'invite', 'enable', 'workflow', 'category',
'recipient', ('workflow-options', 'workflow_options'),
('workflow-status-remapping', 'workflow_status_remapping'),
'roles', 'title', 'options', ('acl-read', 'acl_read')]
def __init__(self, component):
try:
self.formdef = FormDef.get(component)
except KeyError:
raise TraversalError()
self.formdefui = FormDefUI(self.formdef)
get_response().breadcrumb.append((component + '/', self.formdef.name))
self.fields = FieldsDirectory(self.formdef)
def _q_index [html] (self):
html_top('forms', title = self.formdef.name)
get_response().filter['sidebar'] = self.get_sidebar()
get_response().add_javascript(['jquery.js', 'widget_list.js'])
DateWidget.prepare_javascript()
'<h2>%s - ' % _('Form')
self.formdef.name
' <span class="change">(<a rel="popup" href="title">%s</a>)</span>' % _('change title')
'</h2>'
'<div class="splitcontent-left">'
'<div class="bo-block">'
'<h3>%s</h3>' % _('Access')
'<ul>'
categories = get_categories()
if categories:
'<li>%s ' % _('Category:')
if self.formdef.category:
self.formdef.category.name
else:
'-'
' '
'(<a href="category" rel="popup">%s</a>)' % _('change')
'</li>'
workflows = get_workflows()
if workflows:
'<li>%s ' % _('Workflow:')
if self.formdef.workflow:
self.formdef.workflow.name
else:
'-'
if self.formdef.workflow_id:
pristine_workflow = Workflow.get(self.formdef.workflow_id)
if pristine_workflow.has_options():
' (<a href="workflow-options">%s</a>)' % _('options')
' '
'(<a href="workflow" rel="popup">%s</a>)' % _('change')
'</li>'
'<li>%s ' % _('Recipient Role:')
if self.formdef.receiver:
'<a href="../../roles/%s/">%s</a>' % (
self.formdef.receiver.id, self.formdef.receiver.name)
else:
'-'
' '
'(<a href="recipient" rel="popup">%s</a>)' % _('change')
'</li>'
'<li>%s ' % _('Sender Roles:')
if self.formdef.roles:
roles = []
for x in self.formdef.roles:
if x == logged_users_role().id:
roles.append(logged_users_role().name)
else:
roles.append('<a href="../../roles/%s/">%s</a>' % (x, Role.get(x).name))
'%s' % ', '.join(roles)
if self.formdef.always_advertise:
' (%s)' % _('Always advertise')
else:
'-'
' '
'(<a href="roles" rel="popup">%s</a>)' % _('change')
'</li>'
'<li>%s ' % _('Read Access:')
'%s' % {'none': _('None'),
'owner': _('Owner'),
'roles': _('Roles'),
'all': _('Everybody')}.get(self.formdef.acl_read, 'none')
' '
'(<a href="acl-read" rel="popup">%s</a>)' % _('change')
'</ul>'
'</div>'
'</div>'
'<div class="splitcontent-right">'
'<div class="bo-block">'
'<h3>%s' % _('Options')
' <span class="change">(<a rel="popup" href="options">%s</a>)</span></h3>' % _('change')
'<ul>'
if self.formdef.confirmation:
'<li>%s</li>' % _('Include confirmation page')
if self.formdef.signing == 'optional':
'<li>%s</li>' % _('Optional Signing')
if self.formdef.signing == 'compulsory':
'<li>%s</li>' % _('Compulsary Signing')
if self.formdef.private_status_and_history:
'<li>%s</li>' % _('Keep workflow status and history private')
if self.formdef.only_allow_one:
'<li>%s</li>' % _('Only allow one form per user')
if self.formdef.allow_drafts:
'<li>%s</li>' % _('Allow user to keep drafts')
if self.formdef.disabled:
'<li>%s ' % _('Disabled')
if self.formdef.disabled_redirection:
'(<a href="%s">' % self.formdef.disabled_redirection
_('redirection')
'</a>) '
' (<a href="enable">%s</a>)</li>' % _('enable')
if self.formdef.publication_date:
'<li>%s</li>' % _('Publication Date: %s') % self.formdef.publication_date
if self.formdef.expiration_date:
'<li>%s</li>' % _('Expiration Date: %s') % self.formdef.expiration_date
'</ul>'
'</div>'
'</div>'
'<div class="bo-block clear">'
'<h3 class="clear">%s <span class="change">(<a href="fields/">%s</a>)</span></h3>' % (
_('Fields'), _('edit'))
self.get_preview()
'</div>'
def get_sidebar [html] (self):
'<ul>'
'<li><a href="delete" rel="popup">%s</a></li>' % _('Delete')
'<li><a href="duplicate">%s</a></li>' % _('Duplicate')
if ET:
'<li><a href="export">%s</a></li>' % _('Export')
'<li><a href="archive">%s</a></li>' % _('Archive')
if self.formdef.roles:
'<li><a href="invite">%s</a></li>' % _('Invites')
'</ul>'
def category [html] (self):
categories = get_categories()
form = Form(enctype='multipart/form-data')
form.add(SingleSelectWidget, 'category_id',
value=self.formdef.category_id,
options=[(None, '---')] + categories)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append( ('category', _('Category')) )
html_top('forms', title = self.formdef.name)
'<p>%s</p>' % _('Select a category for this form')
form.render()
else:
self.formdef.category_id = form.get_widget('category_id').parse()
self.formdef.store()
redirect('.')
def recipient [html] (self):
form = Form(enctype='multipart/form-data')
form.add(SingleSelectWidget, 'receiver_id',
value=self.formdef.receiver_id,
options = get_user_roles())
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append( ('recipient', _('Recipient Role')) )
html_top('forms', title=self.formdef.name)
'<p>%s</p>' % _('Select the role that will handle those forms.')
form.render()
else:
self.formdef.receiver_id = form.get_widget('receiver_id').parse()
self.formdef.store()
redirect('.')
def roles [html] (self):
form = Form(enctype='multipart/form-data')
form.add(WidgetList, 'roles', title=_('Sender Roles'), element_type=SingleSelectWidget,
value=self.formdef.roles,
add_element_label = _('Add Role'),
element_kwargs = {str('render_br'): False,
str('options'): [(None, str('---')),
(logged_users_role().id, logged_users_role().name)] + get_user_roles()})
form.add(CheckboxWidget, 'always_advertise', title=_('Always advertise'),
value=self.formdef.always_advertise)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append( ('recipient', _('Recipient Role')) )
html_top('forms', title=self.formdef.name)
'<p>%s</p>' % _('Select the roles that can access this form.')
form.render()
else:
self.formdef.always_advertise = form.get_widget('always_advertise').parse()
roles = form.get_widget('roles').parse()
if roles:
self.formdef.roles = [x for x in roles if x]
else:
self.formdef.roles = []
self.formdef.store()
redirect('.')
def title [html] (self):
form = Form(enctype='multipart/form-data')
form.add(StringWidget, 'name', title=_('Form Title'), required=True,
size=40, value=self.formdef.name)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if form.is_submitted() and not form.has_errors():
new_name = form.get_widget('name').parse()
formdefs_name = [x.name for x in FormDef.select(ignore_errors=True)
if x.id != self.formdef.id]
if new_name in formdefs_name:
form.get_widget('name').set_error(_('This name is already used'))
else:
self.formdef.name = new_name
self.formdef.store()
redirect('.')
get_response().breadcrumb.append( ('title', _('Title')) )
html_top('forms', title=self.formdef.name)
'<p>%s</p>' % _('Choose a title for this form')
form.render()
def acl_read [html] (self):
form = Form(enctype='multipart/form-data')
form.add(SingleSelectWidget, 'acl_read', title=_('Read Access'),
options=[
(str('none'), _('None')),
(str('owner'), _('Owner')),
(str('roles'), _('Roles')),
(str('all'), _('Everybody'))],
value=self.formdef.acl_read)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if form.is_submitted() and not form.has_errors():
self.formdef.acl_read = form.get_widget('acl_read').parse()
self.formdef.store()
redirect('.')
get_response().breadcrumb.append( ('acl-read', _('Read Access')) )
html_top('forms', title=self.formdef.name)
'<p>%s</p>' % _('Select who is granted a read access.')
form.render()
def options [html] (self):
form = Form(enctype='multipart/form-data')
form.add(CheckboxWidget, 'confirmation', title=_('Include confirmation page'),
value=self.formdef.confirmation)
if M2Crypto:
form.add(SingleSelectWidget, 'signing', title=_('Signature'),
value=self.formdef.signing,
options = [
(None, _('None')),
('optional', _('Optional')),
('compulsory', _('Compulsary'))])
form.add(CheckboxWidget, 'private_status_and_history',
title=_('Keep workflow status and history private'),
hint=_('Restrict the possibility to see status and history to the recipients'),
value=self.formdef.private_status_and_history)
form.add(CheckboxWidget, 'only_allow_one',
title=_('Only allow one form per user'),
value=self.formdef.only_allow_one)
form.add(CheckboxWidget, 'allow_drafts',
title=_('Allow user to keep drafts'),
value=self.formdef.allow_drafts)
form.add(CheckboxWidget, 'disabled',
title=_('Disable access to form'),
value=self.formdef.disabled)
form.add(StringWidget, 'disabled_redirection',
title=_('If disabled, redirect to this URL'), size=40,
hint=_('Redirection will only be performed if the form is disabled and a URL is given. '\
'Common substitution variables are available with the [variable] syntax.'),
value=self.formdef.disabled_redirection)
form.add(DateWidget, 'publication_date',
title=_('Publication Date'),
value=self.formdef.publication_date)
form.add(DateWidget, 'expiration_date',
title=_('Expiration Date'),
value=self.formdef.expiration_date)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if form.is_submitted() and not form.has_errors():
for f in ('confirmation', 'signing', 'only_allow_one', 'disabled',
'allow_drafts', 'private_status_and_history',
'disabled_redirection', 'publication_date', 'expiration_date'):
widget = form.get_widget(f)
if widget:
setattr(self.formdef, str(f), widget.parse())
self.formdef.store()
redirect('.')
get_response().breadcrumb.append( ('options', _('Options')) )
html_top('forms', title=self.formdef.name)
'<h2>%s</h2>' % _('Options')
form.render()
def workflow [html] (self):
form = Form(enctype='multipart/form-data')
workflows = get_workflows(condition=lambda x: x.possible_status)
form.add(SingleSelectWidget, 'workflow_id',
value=self.formdef.workflow_id,
options = [(None, _('Default Workflow'))] + workflows)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append( ('workflow', _('Workflow')) )
html_top('forms', title=self.formdef.name)
'<p>%s</p>' % _('Select the workflow that will handle those forms.')
form.render()
else:
workflow_id = form.get_widget('workflow_id').parse()
if self.formdef.data_class().keys():
# there are existing formdata, status will have to be mapped
if workflow_id is None:
workflow_id = '_default'
return redirect('workflow-status-remapping?new=%s' % workflow_id)
self.formdef.workflow_id = workflow_id
self.formdef.store()
redirect('.')
def workflow_status_remapping [html] (self):
new_workflow = Workflow.get(get_request().form.get('new'))
if get_request().get_method() == 'GET':
get_request().form = None # do not be considered submitted already
new_workflow_status = [(x.id, x.name) for x in new_workflow.get_waitpoint_status()]
form = Form(enctype='multipart/form-data')
for status in self.formdef.workflow.possible_status:
default = status.id
if not default in [x.id for x in new_workflow.possible_status]:
default = new_workflow_status[0]
form.add(SingleSelectWidget, 'mapping-%s' % status.id,
title=status.name,
value=default, options=new_workflow_status)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append( ('workflow-status-remapping', _('Workflow Status Remapping')) )
html_top('forms', title=self.formdef.name)
'<p>'
_('From %(here)s to %(there)s') % {'here': self.formdef.workflow.name,
'there': new_workflow.name}
'</p>'
form.render()
else:
get_logger().info('admin - form "%s", workflow is now "%s" (was "%s")' % (
self.formdef.name, new_workflow.name, self.formdef.workflow.name))
self.workflow_status_remapping_submit(form)
if new_workflow.id == '_default':
self.formdef.workflow_id = None
else:
self.formdef.workflow_id = new_workflow.id
self.formdef.store()
return redirect('.')
def workflow_status_remapping_submit(self, form):
status_mapping = {}
for status in self.formdef.workflow.possible_status:
status_mapping['wf-%s' % status.id] = 'wf-%s' % \
form.get_widget('mapping-%s' % status.id).parse()
for item in self.formdef.data_class().select():
item.status = status_mapping.get(item.status)
if item.evolution:
for evo in item.evolution:
evo.status = status_mapping.get(evo.status)
item.store()
def get_preview [html] (self):
form = Form()
on_page = 0
for i, field in enumerate(self.formdef.fields):
field.id = i
if hasattr(field, str('add_to_form')):
field.add_to_form(form)
else:
if field.key == 'page':
if on_page:
form.widgets.append(HtmlWidget('</fieldset>'))
form.widgets.append(HtmlWidget('<fieldset class="formpage">'))
on_page += 1
form.widgets.append(HtmlWidget(
'<legend>%s</legend>' % _('Page %s') % on_page))
elif field.key == 'title':
form.widgets.append(HtmlWidget('<h3>%s</h3>' % field.label))
elif field.key == 'subtitle':
form.widgets.append(HtmlWidget('<h4>%s</h4>' % field.label))
elif field.key == 'comment':
form.widgets.append(HtmlWidget('<p>%s</p>' % field.label))
if on_page:
form.widgets.append(HtmlWidget('</fieldset>'))
'<div class="form-preview">'
form.render()
'</div>'
def duplicate [html] (self):
self.formdefui.formdef.id = None
original_name = self.formdefui.formdef.name
self.formdefui.formdef.name = self.formdefui.formdef.name + _(' (copy)')
formdef_names = [x.name for x in FormDef.select()]
no = 2
while self.formdefui.formdef.name in formdef_names:
self.formdefui.formdef.name = _('%(name)s (copy %(no)d)') % {
'name': original_name, 'no': no}
no += 1
self.formdefui.formdef.url_name = None
self.formdefui.formdef.disabled = True
self.formdefui.formdef.store()
return redirect('../%s/' % self.formdefui.formdef.id)
def delete [html] (self):
form = Form(enctype='multipart/form-data')
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
'You are about to irrevocably delete this form.')))
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append(('delete', _('Delete')))
html_top('forms', title = _('Delete Form'))
'<h2>%s %s</h2>' % (_('Deleting Form:'), self.formdef.name)
form.render()
else:
# XXX: remove form data
#for formdata in FormData.select(FormData.q.formdefID == self.formdef.id):
# formdata.destroySelf()
self.formdef.remove_self()
return redirect('..')
def export(self):
x = self.formdef.export_to_xml()
indent(x)
response = get_response()
response.set_content_type('application/x-wcs-form')
response.set_header('content-disposition',
'attachment; filename=%s.wcs' % self.formdef.url_name)
return '<?xml version="1.0" encoding="iso-8859-15"?>\n' + ET.tostring(x)
def archive [html] (self):
if get_request().form.get('job'):
return self.archive_processing()
if get_request().form.get('download'):
return self.archive_download()
form = Form(enctype='multipart/form-data')
form.add(DateWidget, 'date', title = _('Archive forms handled before'))
form.add(CheckboxWidget, 'not-done', title = _('Include forms that have not been handled'),
value = False)
form.add(CheckboxWidget, 'keep', title = _('Do not remove forms'),
value = False)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append(('archive', _('Archive')))
html_top('forms', title = _('Archive Forms'))
'<h2>%s</h2>' % _('Archive Forms')
form.render()
else:
return self.archive_submit(form)
def archive_submit(self, form):
class Archiver:
def __init__(self, formdef):
self.formdef = formdef
def archive(self, job=None):
all_forms = self.formdef.data_class().select()
if form.get_widget('not-done').parse() is False:
not_endpoint_status = self.formdef.workflow.get_not_endpoint_status()
not_endpoint_status_ids = ['wf-%s' % x.id for x in not_endpoint_status]
all_forms = [x for x in all_forms if x.status not in not_endpoint_status_ids]
if form.get_widget('date').parse():
date = form.get_widget('date').parse()
date = time.strptime(date, misc.date_format())
all_forms = [x for x in all_forms if (
x.evolution and x.evolution[-1].time < date) or (
x.receipt_time < date)]
self.fd = StringIO()
t = tarfile.open('wcs.tar.gz', 'w:gz', fileobj=self.fd)
t.add(self.formdef.get_object_filename(), 'formdef')
for formdata in all_forms:
t.add(formdata.get_object_filename(), '%s/%s' % (self.formdef.url_name, str(formdata.id)))
t.close()
if form.get_widget('keep').parse() is False:
for f in all_forms:
f.remove_self()
if job:
job.file_content = self.fd.getvalue()
job.store()
count = self.formdef.data_class().count()
archiver = Archiver(self.formdef)
if count > 100: # Arbitrary threshold
job = get_response().add_after_job(
str(N_('Archiving forms')),
archiver.archive)
return redirect('archive?job=%s' % job.id)
else:
archiver.archive()
response = get_response()
response.set_content_type('application/x-wcs-archive')
response.set_header('content-disposition',
'attachment; filename=%s-archive.wcs' % self.formdef.url_name)
return archiver.fd.getvalue()
def archive_processing [html] (self):
try:
job = AfterJob.get(get_request().form.get('job'))
except KeyError:
return redirect('.')
html_top('forms', title=_('Archiving'))
get_session().display_message()
get_response().add_javascript(['jquery.js', 'interface.js', 'afterjob.js'])
'<dl class="job-status">'
'<dt>'
_(job.label)
'</dt>'
'<dd>'
'<span class="afterjob" id="%s">' % job.id
_(job.status)
'</span>'
'</dd>'
'</dl>'
'<div class="done">'
'<a href="archive?download=%s">%s</a>' % (job.id, _('Download Archive'))
'</div>'
def archive_download(self):
try:
job = AfterJob.get(get_request().form.get('download'))
except KeyError:
return redirect('.')
if not job.status == 'completed':
raise TraversalError()
response = get_response()
response.set_content_type('application/x-wcs-archive')
response.set_header('content-disposition',
'attachment; filename=%s-archive.wcs' % self.formdef.url_name)
return job.file_content
def invite [html] (self):
if not self.formdef.roles:
return template.error_page(
_('''Sending invites is limited to forms restricted
to a set of users.'''))
if get_request().form.get('job'):
return self.sending_invitations()
form = Form(enctype='multipart/form-data')
form.add(IntWidget, 'duration', title=_('Validity Duration (in days)'),
value=10, required=True)
form.add(StringWidget, 'invite_subject', title=_('Invite Subject'),
required=True, value=_('Invitation to %s') % self.formdef.name)
form.add(TextWidget, 'invite_body', title=_('Invite Text'),
cols=80, rows=10, required=True,
value=_('''Hello, connect to [url] and fill the form!'''))
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
users = [x for x in get_publisher().user_class.select() if x.email]
if logged_users_role().id in self.formdef.roles:
allowed_users = users
else:
allowed_users = []
for user in users:
for q in user.roles or []:
if q in self.formdef.roles:
allowed_users.append(user)
break
if self.formdef.only_allow_one:
# if the form is only authorised once per user, we have to remove
# users who have already submitted it from the list.
formdef_data_class = self.formdef.data_class()
real_allowed_users = []
for user in allowed_users:
if not formdef_data_class.get_with_indexed_value('user_id', user.id):
real_allowed_users.append(user)
allowed_users = real_allowed_users
if form.is_submitted() and not form.has_errors():
return self.invite_submit(form, allowed_users)
get_response().breadcrumb.append(('invite', _('Invite')))
html_top('forms', title = _('Invitation to Form'))
'<h2>%s</h2>' % _('Invitation to "%s" Form') % self.formdef.name
'<p>'
_('''This action allows you to send an invitation to all users
concerned by this form.''')
'</p>'
if len(allowed_users) == 0:
'<p>'
_('There is nobody to receive an invite.')
'</p>'
else:
'<p>'
_('The invitation will be sent to %d persons.') % len(allowed_users)
'</p>'
form.render()
def invite_submit(self, form, allowed_users):
class InvitationSender:
def __init__(self, formdef):
self.formdef = formdef
self.formdef_url = self.formdef.get_url()
def send(self, job=None):
duration_validity = form.get_widget('duration').parse()
subject = form.get_widget('invite_subject').parse()
body = form.get_widget('invite_body').parse()
for user in allowed_users:
token = tokens.Token(duration_validity * 86400)
token.type = 'form-invite'
token.formdef_id = self.formdef.id
token.user_id = user.id
token.store()
data = {
'url': self.formdef_url + 'tokens/%s/' % token.id,
'time': misc.localstrftime(time.localtime(token.expiration)),
}
emails.ezt_email(subject, body, data,
email_rcpt=user.email, fire_and_forget=False)
invitation_sender = InvitationSender(self.formdef)
job = get_response().add_after_job(
str(N_('Sending invitation emails')),
invitation_sender.send)
return redirect('invite?job=%s' % job.id)
def sending_invitations [html] (self):
try:
job = AfterJob.get(get_request().form.get('job'))
except KeyError:
return redirect('..')
html_top('forms', title = _('Invitation to Form'))
get_session().display_message()
get_response().add_javascript(['jquery.js', 'interface.js', 'afterjob.js'])
'<dl class="job-status">'
'<dt>'
_(job.label)
'</dt>'
'<dd>'
'<span class="afterjob" id="%s">' % job.id
_(job.status)
'</span>'
'</dd>'
'</dl>'
'<div class="done">'
'<a href="./">%s</a>' % _('Back')
'</div>'
def enable(self):
self.formdef.disabled = False
self.formdef.store()
if get_request().form.get('back') == 'fields':
return redirect('fields/')
return redirect('.')
def workflow_options [html] (self):
request = get_request()
if request.get_method() == 'GET' and request.form.get('file'):
value = self.formdef.workflow_options.get(request.form.get('file'))
if value:
return value.build_response()
html_top('forms', title = _('Workflow Options'))
'<h2>%s</h2>' % _('Workflow Options')
form = Form(enctype='multipart/form-data')
pristine_workflow = Workflow.get(self.formdef.workflow_id)
for status in self.formdef.workflow.possible_status:
had_options = False
for item in status.items:
prefix = '%s*%s*' % (status.id, item.id)
pristine_item = pristine_workflow.get_status(status.id).get_item(item.id)
parameters = [x for x in item.get_parameters() if not \
getattr(pristine_item, x)]
if not parameters:
continue
if not had_options:
form.widgets.append(HtmlWidget('<h3>%s</h3>' % status.name))
had_options = True
label = getattr(item, str('label'), None) or _(item.description)
form.widgets.append(HtmlWidget('<h4>%s</h4>' % label))
item.add_parameters_widgets(form, parameters, prefix=prefix, formdef=self.formdef)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
form.render()
if form.get_widget('cancel').parse():
return redirect('.')
if form.is_submitted() and not form.has_errors():
self.workflow_options_submit(form)
return redirect('.')
def workflow_options_submit(self, form):
self.formdef.workflow_options = {}
for widget in form.get_all_widgets():
if widget in form.get_submit_widgets():
continue
if widget.name.startswith('_'):
continue
self.formdef.workflow_options[widget.name] = widget.parse()
self.formdef.store()
class FormsDirectory(Directory):
_q_exports = ['', 'new', ('import', 'p_import')]
def _q_index [html] (self):
get_response().breadcrumb.append( ('forms/', _('Forms')) )
html_top('forms', title = _('Forms'))
get_response().add_javascript(['jquery.js', 'widget_list.js'])
if Role.count():
'<ul id="main-actions">'
' <li><a class="new-item" href="new" rel="popup">%s</a></li>' % _('New Form')
' <li><a href="import" rel="popup">%s</a></li>' % _('Import')
'</ul>'
else:
'<p>%s</p>' % _('You first have to define roles.')
cats = Category.select()
Category.sort_by_position(cats)
one = False
formdefs = FormDef.select(order_by='name', ignore_errors=True)
for c in cats:
l2 = [x for x in formdefs if x.category_id == c.id]
l2 = [x for x in l2 if not x.disabled or (x.disabled and x.disabled_redirection)] \
+ [x for x in l2 if x.disabled and not x.disabled_redirection]
if l2:
self.form_list(l2, title = c.name)
one = True
l2 = [x for x in formdefs if not x.category]
if l2:
if one:
title = _('Misc')
else:
title = None
l2 = [x for x in l2 if not x.disabled or (x.disabled and x.disabled_redirection)] \
+ [x for x in l2 if x.disabled and not x.disabled_redirection]
self.form_list(l2, title = title)
def form_list [html] (self, formdefs, title):
'<div class="bo-block">'
if title:
'<h2>%s</h2>' % title
'<ul class="biglist">'
for formdef in formdefs:
if formdef.disabled:
'<li class="disabled">'
else:
'<li>'
'<strong class="label"><a href="%s/">%s</a></strong>' % (formdef.id, formdef.name)
'<p class="details">'
if formdef.disabled and formdef.disabled_redirection:
'(<a href="%s">' % formdef.disabled_redirection
_('redirection')
'</a>) '
if formdef.receiver:
misc.ellipsize(formdef.receiver.name, 50)
'</p>'
'</li>'
'</ul>'
'</div>'
def new [html] (self):
get_response().breadcrumb.append( ('forms/', _('Forms')) )
get_response().breadcrumb.append( ('new', _('New')) )
if Role.count() == 0:
return error_page('forms', _('You first have to define roles.'))
formdefui = FormDefUI(None)
form = formdefui.new_form_ui()
if form.get_widget('cancel').parse():
return redirect('.')
redo = False
if form.get_widget('fields') and \
form.get_widget('fields').get_widget('add_element').parse():
form.clear_errors()
redo = True
if form.is_submitted() and not form.has_errors() and not redo:
try:
formdef = formdefui.submit_form(form)
formdef.disabled = True
formdef.store()
except ValueError:
pass
else:
return redirect(str(formdef.id) + '/fields')
html_top('forms', title = _('New Form'))
'<h2>%s</h2>' % _('New Form')
form.render()
def _q_lookup(self, component):
get_response().breadcrumb.append( ('forms/', _('Forms')) )
return FormDefPage(component)
def p_import [html] (self):
form = Form(enctype = 'multipart/form-data')
form.add(FileWidget, 'file', title = _('File'), required = True)
form.add_submit('submit', _('Import Form'))
form.add_submit('cancel', _('Cancel'))
if form.get_submit() == 'cancel':
return redirect('.')
if form.is_submitted() and not form.has_errors():
try:
return self.import_submit(form)
except ValueError:
form.get_widget('file').set_error(_('Invalid File'))
get_response().breadcrumb.append( ('import', _('Import')) )
html_top('forms', title = _('Import Form'))
'<h2>%s</h2>' % _('Import Form')
form.render()
def import_submit(self, form):
formdef = FormDef.import_from_xml(form.get_widget('file').parse().fp)
formdef.disabled = True
formdef.store()
return redirect('%s' % formdef.id)