# w.c.s. (asec) - w.c.s. extension for poll & survey service # Copyright (C) 2010-2011 Entr'ouvert # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 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 Affero General Public # License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import urllib import csv import re from sets import Set from quixote import get_publisher, get_request, get_response, get_session, redirect from quixote.directory import Directory from quixote.html import TemplateIO, htmltext from qommon.backoffice.menu import html_top from qommon.admin.menu import command_icon from qommon.form import * from qommon.tokens import Token from qommon import errors, get_cfg, get_logger from qommon.afterjobs import AfterJob import qommon.storage from qommon import emails from wcs.formdef import FormDef, FormField from wcs.roles import Role from wcs.users import User from qommon.ident.password import make_password from qommon.ident.password_accounts import PasswordAccount from wcs.workflows import Workflow from wcs.admin.fields import FieldWidget, FieldDefPage, FieldsDirectory from bodiffusion import DiffusionDirectory from boresults import FormResultDirectory import quota class CustomFieldDefPage(FieldDefPage): section = 'forms/' wsf_support = False def html_top(self, *args, **kwargs): html_top('forms/%s' % self.objectdef.id, *args, **kwargs) def duplicate(self): if not quota.may_add_a_new_field(self.objectdef): raise quota.QuotaExceeded() t = FieldDefPage.duplicate(self) if get_response().status_code == 302: return redirect('..') return t def delete(self): t = FieldDefPage.delete(self) if get_response().status_code == 302: return redirect('..') return t class FormDirectory(FieldsDirectory): _q_exports = ['', 'update_order', 'new', 'title', 'delete', 'options', 'clean', ('anonymity-on', 'anonymity_on'), ('anonymity-off', 'anonymity_off'), ('status-soonavailable', 'status_soonavailable'), ('status-running', 'status_running'), ('status-closed', 'status_closed'), ] section = 'forms' field_def_page_class = CustomFieldDefPage support_import = False blacklisted_types = ['file'] def html_top(self, *args, **kwargs): html_top('forms/%s' % self.objectdef.id, *args, **kwargs) def __init__(self, objectdef): self.objectdef = objectdef get_cfg('misc', {})['sitename'] = '%s - %s' % (get_cfg('misc', {}).get('sitename'), self.objectdef.name) get_response().filter['objectdef'] = self.objectdef if not hasattr(self.objectdef, 'asec_status'): self.objectdef.asec_status = 'running' def title(self): form = Form(enctype='multipart/form-data') form.add(StringWidget, 'name', title=_('Title'), size=40, required=True, value=self.objectdef.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(): try: return self.title_submit(form) except ValueError: pass get_response().breadcrumb.append( ('forms/', None) ) get_response().breadcrumb.append( ('new', _('New Questionnaire')) ) html_top('forms', _('Change Title')) return form.render() def title_submit(self, form): self.objectdef.name = form.get_widget('name').parse() self.objectdef.store() return redirect('.') def delete(self): form = Form(enctype='multipart/form-data') form.widgets.append(HtmlWidget('

%s

' % _( 'You are about to irrevocably delete this questionnaire.'))) # XXX: add a checkbox to ask about the removal of submitted data 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 Questionnaire')) r = TemplateIO(html=True) r += htmltext('

%s %s

') % (_('Deleting Questionnaire:'), self.objectdef.name) r += form.render() return r.getvalue() else: # XXX: should also remove participants role, and participants to no # other forms self.objectdef.remove_self() return redirect('..') def _q_index(self): if self.objectdef.asec_status == 'soon-available': t = super(FormDirectory, self)._q_index() self.html_top(self.objectdef.name) return t return self._q_index_view() def _q_index_view(self): self.html_top(self.objectdef.name) get_response().add_javascript(['jquery.js', 'interface.js', 'biglist.js']) r = TemplateIO(html=True) r += self.index_top() form = self.get_preview_form() r += htmltext('
') r += form.render() r += htmltext('
') get_response().filter['sidebar'] = self.get_results_sidebar() return r.getvalue() def get_results_sidebar(self): r = TemplateIO(html=True) if self.objectdef.asec_status == 'running': r += htmltext('

%s

') % _('Visualisation & export of preliminary results') else: r += htmltext('

%s

') % _('Visualisation & export of results') r += htmltext('') return r.getvalue() def get_preview_form(self): form = Form() on_page = 0 for i, field in enumerate(self.objectdef.fields): field.id = i if hasattr(field, 'add_to_form'): field.add_to_form(form) else: if field.key == 'page': if on_page: form.widgets.append(HtmlWidget(htmltext(''))) form.widgets.append(HtmlWidget(htmltext('
'))) on_page += 1 form.widgets.append(HtmlWidget( htmltext('%s') % _('Page %s') % on_page)) elif field.key == 'title': form.widgets.append(HtmlWidget( htmltext('

%s

') % field.label)) elif field.key == 'subtitle': form.widgets.append(HtmlWidget( htmltext('

%s

') % field.label)) elif field.key == 'comment': form.widgets.append(HtmlWidget( htmltext('

%s') % field.label)) if on_page: form.widgets.append(HtmlWidget(htmltext('') if self.objectdef.asec_status == 'soon-available': r += htmltext('

  • %s
  • ') % _('Change Title') r += htmltext('
  • %s
  • ') % _('Delete') r += htmltext('') r += htmltext('

    %s

    ') % _('Design') r += get_session().display_message() r += htmltext('') if not self.objectdef.fields: r += htmltext('

    ') r += _('There are not yet any fields for this questionnaire.') r += htmltext('

    ') r += htmltext('

    ') r += _('You should use the controls at the right of the page to add fields.') r += htmltext('

    ') r += htmltext('

    %s

    ') % _('Contents of your questionnaire') return r.getvalue() def get_new_field_form(self, page_no): r = TemplateIO(html=True) if not self.objectdef.disabled: if not hasattr(self.objectdef, str('asec_status')): self.objectdef.asec_status = 'running' if self.objectdef.asec_status == 'soon-available': if self.objectdef.data_class().keys(): r += htmltext('

    ') r += htmltext('%s ') % _('Warning:') r += _('This form already contains submitted items.') r += ' ' r += htmltext('%s') % _('Remove Them') r += htmltext('

    ') if quota.may_add_a_new_field(self.objectdef): r += super(FormDirectory, self).get_new_field_form(page_no) else: r += htmltext('

    ') r += _('This questionnaire has reached its number of questions quota.') r += htmltext('

    ') return r.getvalue() def clean(self): form = Form(enctype='multipart/form-data') form.widgets.append(HtmlWidget('

    %s

    ' % _( 'You are about to irrevocably remove all submitted questionnaires.'))) form.add_submit('submit', _('Remove')) form.add_submit('cancel', _('Cancel')) if form.get_widget('cancel').parse(): return redirect('.') if form.is_submitted() and not form.has_errors(): try: return self.clean_submit() except ValueError: pass html_top('forms', _('Remove submitted questionnaires')) return form.render() def clean_submit(self): get_logger().info('form %s - wiped submitted form' % self.objectdef.id) self.objectdef.data_class().wipe() return redirect('.') def anonymity_on(self): if self.objectdef.workflow_id.startswith('asec-'): self.objectdef.workflow_id += '+anonymous' else: self.objectdef.workflow_id = 'asec-default+anonymous' self.objectdef.store() return redirect('.') def anonymity_off(self): self.objectdef.workflow_id = self.objectdef.workflow_id.replace('+anonymous', '') self.objectdef.store() return redirect('.') def _q_traverse(self, path): get_response().breadcrumb.append(('%s/' % self.objectdef.id, self.objectdef.name)) return Directory._q_traverse(self, path) def status_soonavailable(self): self.objectdef.asec_status = 'soon-available' self.objectdef.store() get_logger().info('form %s - set status to %s' % ( self.objectdef.id, self.objectdef.asec_status)) return redirect('.') def status_running(self): self.objectdef.asec_status = 'running' self.objectdef.store() get_logger().info('form %s - set status to %s' % ( self.objectdef.id, self.objectdef.asec_status)) return redirect('.') def status_closed(self): self.objectdef.asec_status = 'closed' self.objectdef.store() get_logger().info('form %s - set status to %s' % ( self.objectdef.id, self.objectdef.asec_status)) return redirect('.') def options(self): form = Form(enctype='multipart/form-data') if self.objectdef.roles: # list of participants -> access codes form.add(CheckboxWidget, 'access_as_password', title=_('Access code entry as password'), value=self.objectdef.access_as_password) 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(): try: return self.options_submit(form) except ValueError: pass get_response().breadcrumb.append( ('forms/', None) ) get_response().breadcrumb.append( ('options', _('Options')) ) html_top('forms', _('Options')) r = TemplateIO(html=True) r += htmltext('

    %s

    ') % _('Options') r += form.render() return r.getvalue() def options_submit(self, form): for widget in ('access_as_password',): if not form.get_widget(widget): continue setattr(self.objectdef, widget, form.get_widget(widget).parse()) self.objectdef.store() return redirect('.') def _q_lookup(self, component): if component == 'diffusion': get_response().breadcrumb.append( ('diffusion/', _('Diffusion')) ) return DiffusionDirectory(self.objectdef) if component == 'results': get_response().breadcrumb.append( ('results/', _('Results')) ) return FormResultDirectory(self.objectdef) return FieldsDirectory._q_lookup(self, component) class FormsDirectory(Directory): _q_exports = [''] def _q_index(self): return redirect('..') def _q_lookup(self, component): try: formdef = FormDef.get(component) except KeyError: raise errors.TraversalError() get_response().breadcrumb.append( ('forms/', None) ) return FormDirectory(formdef)