417 lines
17 KiB
Python
417 lines
17 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# 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/>.
|
|
|
|
from quixote import redirect
|
|
from quixote.directory import Directory
|
|
from quixote.html import TemplateIO, htmltext, htmlescape
|
|
|
|
from wcs.qommon import _
|
|
from wcs.qommon.form import *
|
|
from wcs.qommon import errors, misc
|
|
from wcs.qommon.backoffice.menu import html_top
|
|
from wcs.qommon.admin.menu import command_icon
|
|
|
|
from wcs import fields
|
|
from wcs.formdef import FormDef
|
|
from wcs.fields import get_field_types
|
|
import copy
|
|
|
|
class FieldDefPage(Directory):
|
|
_q_exports = ['', 'delete', 'duplicate']
|
|
|
|
large = False
|
|
page_id = None
|
|
blacklisted_attributes = []
|
|
|
|
def __init__(self, objectdef, field_id):
|
|
self.objectdef = objectdef
|
|
try:
|
|
self.field = [x for x in self.objectdef.fields if x.id == field_id][0]
|
|
except IndexError:
|
|
raise errors.TraversalError()
|
|
if not self.field.label:
|
|
self.field.label = _('None')
|
|
label = misc.ellipsize(self.field.unhtmled_label, 40)
|
|
get_response().breadcrumb.append((field_id + '/', label))
|
|
|
|
def form(self):
|
|
form = Form(enctype='multipart/form-data',
|
|
advanced_label=_('Additional parameters'))
|
|
self.field.fill_admin_form(form)
|
|
form.widgets = [x for x in form.widgets
|
|
if getattr(x, 'name', None) not in self.blacklisted_attributes]
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
return form
|
|
|
|
def _q_index(self):
|
|
form = self.form()
|
|
redo = False
|
|
|
|
if form.get_submit() == 'cancel':
|
|
return redirect('../#itemId_%s' % self.field.id)
|
|
|
|
if form.get_widget('items') and form.get_widget('items').get_widget('add_element').parse():
|
|
form.clear_errors()
|
|
redo = True
|
|
|
|
try:
|
|
self.field.check_admin_form(form)
|
|
except AttributeError:
|
|
# informational fields don't have that method
|
|
pass
|
|
if form.has_errors():
|
|
redo = True
|
|
|
|
if redo or not form.get_submit() == 'submit':
|
|
self.html_top('%s - %s' % (_('Form'), self.objectdef.name))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % misc.ellipsize(self.field.unhtmled_label, 80)
|
|
r += form.render()
|
|
return r.getvalue()
|
|
else:
|
|
self.submit(form)
|
|
if form.get_widget('items') is None and self.field.type == 'item':
|
|
return redirect('.')
|
|
return redirect('../#itemId_%s' % self.field.id)
|
|
|
|
def submit(self, form):
|
|
for f in self.field.get_admin_attributes():
|
|
widget = form.get_widget(f)
|
|
if not widget:
|
|
continue
|
|
setattr(self.field, f.replace('-', '_'), widget.parse())
|
|
self.objectdef.store()
|
|
|
|
def get_deletion_extra_warning(self):
|
|
return _('Warning: this field data will be permanently deleted.')
|
|
|
|
def delete(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
ellipsized_field_label = misc.ellipsize(self.field.unhtmled_label, 60)
|
|
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
|
|
"You are about to remove the \"%s\" field.") % ellipsized_field_label))
|
|
if self.field.type not in ('page', 'subtitle', 'title', 'comment'):
|
|
warning = self.get_deletion_extra_warning()
|
|
if warning:
|
|
form.widgets.append(HtmlWidget('<div class="warningnotice">%s</div>' % warning))
|
|
form.add_submit('delete', _('Submit'))
|
|
form.add_submit("cancel", _("Cancel"))
|
|
if form.get_widget('cancel').parse():
|
|
if self.page_id:
|
|
return redirect('../#itemId_%s' % self.field.id)
|
|
else:
|
|
return redirect('../../fields/#itemId_%s' % self.field.id)
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_response().breadcrumb.append(('delete', _('Delete')))
|
|
self.html_top(title = _('Delete Field'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Deleting Field: %s') % ellipsized_field_label
|
|
r += form.render()
|
|
return r.getvalue()
|
|
else:
|
|
field_index = self.objectdef.fields.index(self.field)
|
|
del self.objectdef.fields[field_index]
|
|
self.objectdef.store()
|
|
# redirect to the field that was above this one
|
|
if self.objectdef.fields:
|
|
if field_index == 0:
|
|
above_field = self.objectdef.fields[0]
|
|
else:
|
|
above_field = self.objectdef.fields[field_index-1]
|
|
anchor = '#itemId_%s' % above_field.id
|
|
else:
|
|
anchor = ''
|
|
if self.page_id:
|
|
return redirect('../' + anchor)
|
|
else:
|
|
return redirect('../../fields/' + anchor)
|
|
|
|
def duplicate(self):
|
|
field_pos = self.objectdef.fields.index(self.field)
|
|
fields = self.objectdef.fields
|
|
new_field = copy.deepcopy(self.field)
|
|
# allocate a new id
|
|
new_field.id = self.objectdef.get_new_field_id()
|
|
fields.insert(field_pos+1, new_field)
|
|
self.objectdef.store()
|
|
anchor = '#itemId_%s' % new_field.id
|
|
if self.page_id:
|
|
return redirect('../' + anchor)
|
|
else:
|
|
return redirect('../../fields/' + anchor)
|
|
|
|
|
|
class FieldsPagesDirectory(Directory):
|
|
def __init__(self, parent):
|
|
self.parent = parent
|
|
|
|
def _q_lookup(self, component):
|
|
directory = FieldsDirectory(self.parent.objectdef)
|
|
directory.field_var_prefix = self.parent.field_var_prefix
|
|
directory.html_top = self.parent.html_top
|
|
try:
|
|
directory.page_id = str(component)
|
|
except ValueError:
|
|
raise errors.TraversalError()
|
|
return directory
|
|
|
|
|
|
class FieldsDirectory(Directory):
|
|
_q_exports = ['', 'update_order', 'new', 'pages']
|
|
field_def_page_class = FieldDefPage
|
|
blacklisted_types = []
|
|
page_id = None
|
|
field_var_prefix = ''
|
|
|
|
support_import = True
|
|
|
|
def html_top(self, title, *args, **kwargs):
|
|
html_top(self.section, title, *args, **kwargs)
|
|
|
|
def __init__(self, objectdef):
|
|
self.objectdef = objectdef
|
|
self.pages = FieldsPagesDirectory(self)
|
|
|
|
def _q_traverse(self, path):
|
|
get_response().breadcrumb.append(('fields/', _('Fields')))
|
|
return Directory._q_traverse(self, path)
|
|
|
|
def _q_lookup(self, component):
|
|
d = self.field_def_page_class(self.objectdef, component)
|
|
d.html_top = self.html_top
|
|
d.page_id = self.page_id
|
|
return d
|
|
|
|
def _q_index(self):
|
|
self.html_top('%s - %s' % (_('Form'), self.objectdef.name))
|
|
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js'])
|
|
|
|
r = TemplateIO(html=True)
|
|
|
|
r += self.index_top()
|
|
|
|
if self.objectdef.fields:
|
|
if len(self.objectdef.fields) > 500:
|
|
r += htmltext('<div class="errornotice">')
|
|
r += htmltext(_('This form contains more than 500 fields. '
|
|
'It is close to the database limits and no new fields should be added.'))
|
|
r += htmltext('</div>')
|
|
|
|
if [x for x in self.objectdef.fields if x.type == 'page']:
|
|
if self.objectdef.fields[0].type != 'page':
|
|
r += htmltext('<div class="errornotice">')
|
|
r += htmltext(_('In a multipage form, the first field should be of type "page".'))
|
|
r += htmltext('</div>')
|
|
|
|
r += htmltext('<p class="hint">%s</p>') % _('Use drag and drop with the handles to reorder items.')
|
|
if self.page_id is not None:
|
|
r += htmltext('<p>')
|
|
r += htmltext('<a href="../../">%s</a>') % _('Display all pages')
|
|
r += htmltext('</p>')
|
|
|
|
extra_classes = []
|
|
if [x for x in self.objectdef.fields if x.type == 'page']:
|
|
extra_classes.append('multipage')
|
|
|
|
r += htmltext('<ul id="fields-list" class="biglist sortable %s" data-page-no-label="%s">' % (
|
|
' '.join(extra_classes),
|
|
_('Page #%s:') % '***'))
|
|
current_page_no = 0
|
|
on_page = False
|
|
for i, field in enumerate(self.objectdef.fields):
|
|
if field.type == 'page':
|
|
current_page_no += 1
|
|
if str(field.id) == self.page_id:
|
|
on_page = True
|
|
else:
|
|
on_page = False
|
|
|
|
hidden = ''
|
|
if self.page_id and (not on_page or field.type == 'page'):
|
|
hidden = 'style="display:none;"'
|
|
|
|
r += htmltext('<li class="biglistitem type-%s" id="itemId_%s" %s>' % (
|
|
field.type, field.id, hidden))
|
|
try:
|
|
type_label = [x[1] for x in get_field_types() if x[0] == field.type][0]
|
|
except IndexError:
|
|
type_label = _('Unknown')
|
|
if field.type in ('subtitle', 'title', 'comment'):
|
|
label = misc.ellipsize(field.unhtmled_label, 60)
|
|
if field.type in ('subtitle', 'title'):
|
|
r += htmltext('<strong id="label%s">%s</strong>') % (field.id, label)
|
|
else:
|
|
r += htmltext('<span id="label%s">%s</span>') % (field.id, label)
|
|
r += htmltext('<p class="details">')
|
|
r += htmltext('<span class="type">%s</span>') % _(type_label)
|
|
if getattr(field, 'condition', None):
|
|
r += htmltext(' - <span class="condition">%s</span>') % _('depending on condition')
|
|
r += htmltext('</p>')
|
|
r += htmltext('<p class="commands">')
|
|
r += command_icon('%s/' % field.id, 'edit')
|
|
else:
|
|
r += htmltext('<strong class="label" id="label%s">' % field.id)
|
|
if field.type == 'page':
|
|
r += htmltext('<span class="page-no">%s</span> ') % _('Page #%s:') % current_page_no
|
|
r += htmltext('%s</strong>') % field.label
|
|
r += htmltext('<p class="details">')
|
|
if field.type != 'page':
|
|
r += htmltext('<span class="type">%s</span>') % _(type_label)
|
|
if hasattr(field, str('required')):
|
|
if field.required:
|
|
required = ''
|
|
else:
|
|
required = ' - ' + _('optional')
|
|
r += htmltext('<span class="optional">%s</span>') % required
|
|
if getattr(field, 'condition', None):
|
|
r += htmltext(' - <span class="condition">%s</span>') % _('depending on condition')
|
|
if getattr(field, 'varname', None):
|
|
r += htmltext(' - <span class="varname">{{%s%s}}</span>') % (
|
|
self.field_var_prefix, field.varname)
|
|
r += htmltext('</p>')
|
|
r += htmltext('<p class="commands">')
|
|
if field.type == 'page' and self.page_id is None:
|
|
r += command_icon('pages/%s/' % field.id, 'view',
|
|
label = _('Limit display to this page'))
|
|
r += command_icon('%s/' % field.id, 'edit')
|
|
r += command_icon('%s/duplicate' % field.id, 'duplicate')
|
|
r += command_icon('%s/delete' % field.id, 'remove', popup = True)
|
|
r += htmltext('</p></li>')
|
|
r += htmltext('</ul>')
|
|
|
|
get_response().filter['sidebar'] = str(self.get_new_field_form(self.page_id))
|
|
r += self.index_bottom()
|
|
return r.getvalue()
|
|
|
|
def get_new_field_form(self, page_id):
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<div id="new-field">')
|
|
r += htmltext('<h3>%s</h3>') % _('New Field')
|
|
get_request().form = None # ignore the eventual ?page=x
|
|
form = Form(enctype='multipart/form-data', action = 'new')
|
|
if page_id:
|
|
form.add_hidden('page_id', page_id)
|
|
form.add(StringWidget, 'label', title = _('Label'),
|
|
required = True, size = 50)
|
|
form.add(SingleSelectWidget, 'type', title = _('Type'),
|
|
required=True,
|
|
options = [(x, _(y)) for x,y in get_field_types() if x not in self.blacklisted_types])
|
|
form.add_submit('submit', _('Add'))
|
|
r += form.render()
|
|
if self.support_import:
|
|
form = Form(enctype='multipart/form-data', action='new')
|
|
if page_id:
|
|
form.add_hidden('page_id', page_id)
|
|
form.add(SingleSelectWidget, 'form', title = _('Or import fields from:'), required = True,
|
|
options = [(None, '----', None)] + [
|
|
(x.id, x.name, x.id) for x in FormDef.select(order_by='name', lightweight=True, ignore_errors=True)])
|
|
form.add_submit('submit', _('Submit'))
|
|
r += form.render()
|
|
r += htmltext('</div>')
|
|
return r.getvalue()
|
|
|
|
def index_top(self):
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s') % self.objectdef.name
|
|
if self.page_id:
|
|
current_page_no = 0
|
|
for field in self.objectdef.fields:
|
|
if field.type == 'page':
|
|
current_page_no += 1
|
|
if str(field.id) == self.page_id:
|
|
r += ' - '
|
|
r += _('page %d') % current_page_no
|
|
r += ' - '
|
|
r += field.label
|
|
r += htmltext('</h2>')
|
|
r += get_session().display_message()
|
|
if not self.objectdef.fields:
|
|
r += htmltext('<div class="infonotice">%s</div>') % _('There are not yet any fields for this form.')
|
|
return r.getvalue()
|
|
|
|
def index_bottom(self):
|
|
pass
|
|
|
|
def update_order(self):
|
|
request = get_request()
|
|
new_order = request.form['order'].strip(';').split(';')
|
|
new_fields = [ [x for x in self.objectdef.fields if x.id == y][0] for y in new_order]
|
|
self.objectdef.fields = new_fields
|
|
self.objectdef.store()
|
|
return 'ok'
|
|
|
|
def new(self):
|
|
form = Form(enctype='multipart/form-data', action = 'new')
|
|
form.add(StringWidget, 'page_id')
|
|
form.add(StringWidget, 'label', title = _('Label'), size = 50)
|
|
form.add(SingleSelectWidget, 'type', title = _('Type'),
|
|
options = [(x, _(y)) for x,y in get_field_types()])
|
|
if FormDef.count():
|
|
form.add(SingleSelectWidget, 'form', title = _('Or import fields from:'),
|
|
options = [(x.id, x.name, x.id) for x in FormDef.select(
|
|
order_by = 'name', ignore_errors=True)])
|
|
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_session().message = ('error', _('Submitted form was not filled properly.'))
|
|
return redirect('.')
|
|
|
|
try:
|
|
page_id = form.get_widget('page_id').parse()
|
|
except (TypeError, ValueError):
|
|
page_id = None
|
|
|
|
redirect_url = '.'
|
|
if page_id is not None:
|
|
redirect_url = './?page=%s' % page_id
|
|
on_page = False
|
|
for i, field in enumerate(self.objectdef.fields):
|
|
if field.type == 'page':
|
|
if on_page:
|
|
break
|
|
if str(field.id) == str(page_id):
|
|
on_page = True
|
|
else:
|
|
i += 1
|
|
insertion_point = i
|
|
else:
|
|
insertion_point = len(self.objectdef.fields)
|
|
|
|
field_type = form.get_widget('type').parse()
|
|
if form.get_widget('label').parse() and field_type:
|
|
label = form.get_widget('label').parse()
|
|
if field_type == 'comment' and not label.startswith('<'):
|
|
label = '<p>%s</p>' % htmlescape(label)
|
|
self.objectdef.fields.insert(insertion_point,
|
|
fields.get_field_class_by_type(field_type)(
|
|
label=label,
|
|
type=field_type,
|
|
id =self.objectdef.get_new_field_id()))
|
|
elif form.get_widget('form') and form.get_widget('form').parse():
|
|
formdef = FormDef.get(form.get_widget('form').parse())
|
|
for j, field in enumerate(formdef.fields):
|
|
field.id = self.objectdef.get_new_field_id()
|
|
self.objectdef.fields.insert(insertion_point+j, field)
|
|
else:
|
|
get_session().message = ('error', _('Submitted form was not filled properly.'))
|
|
return redirect(redirect_url)
|
|
|
|
self.objectdef.store()
|
|
|
|
return redirect(redirect_url)
|