Forget about the all storage thing, use sqlobject to store everything,

currently defaulting to SQLite.  Everything old is imported and new again.
This commit is contained in:
Frédéric Péters 2005-08-22 13:22:56 +00:00
parent 5e275d0bc6
commit e4a72346a7
26 changed files with 1510 additions and 1075 deletions

12
debian/changelog vendored
View File

@ -1,3 +1,15 @@
wcs (0.3.9cvs-0) unstable; urgency=low
* CVS snapshot; now using SQLObject.
-- Frederic Peters <fpeters@debian.org> Mon, 22 Aug 2005 15:20:12 +0200
wcs (0.1.9cvs-0) unstable; urgency=low
* CVS snapshot.
-- Frederic Peters <fpeters@debian.org> Thu, 4 Aug 2005 10:51:42 +0200
wcs (0.0.0-0) unstable; urgency=low
* Initial package.

2
debian/control vendored
View File

@ -7,7 +7,7 @@ Standards-Version: 3.6.5.0
Package: wcs
Architecture: all
Depends: python2.3, quixote (>= 2.0), libapache2-mod-scgi | libapache-mod-scgi, python2.3-scgi
Depends: python2.3, quixote (>= 2.0), libapache2-mod-scgi | libapache-mod-scgi, python2.3-scgi, python2.3-sqlobject, python2.3-sqlite
Description: w.c.s. (1st draft)
.

View File

@ -9,7 +9,8 @@ import os
import lasso
import __builtin__
import storage
from sqlobject import *
import gettext, locale
gettext.install('wcs')
@ -26,6 +27,22 @@ import misc
import errors
import sessions
import anonylink
import categories
import consultationdef
import formdata
import formdef
import roles
import users
sqlobject_classes = [anonylink.AnonymityLink, categories.Category,
roles.RoleEmail, roles.Role, users.NameIdentifier, users.User,
formdef.FormField, formdef.FormDef,
consultationdef.ConsultationField, consultationdef.Consultation,
formdata.FormDataEvolution, formdata.FormData,
sessions.SqlSession,
]
from root import RootDirectory
class WcsPublisher(Publisher):
@ -41,6 +58,16 @@ class WcsPublisher(Publisher):
self.config.display_exceptions = debug_cfg.get('display_exceptions')
self.config.form_tokens = True
def set_connection(self):
self.conn = connectionForURI('sqlite://%s/wcs.db' % self.app_dir)
for klass in sqlobject_classes:
klass.setConnection(self.conn)
if not self.conn.tableExists(klass._table):
klass.createTable()
if hasattr(klass, 'initTable'):
klass.initTable()
if hasattr(klass, 'migrateOldData'):
klass.migrateOldData()
class WcsVHostPublisher(WcsPublisher):
def try_publish(self, request):
@ -48,6 +75,7 @@ class WcsVHostPublisher(WcsPublisher):
if not os.path.exists(self.app_dir):
os.mkdir(self.app_dir)
self.set_config()
self.set_connection()
return WcsPublisher.try_publish(self, request)
@ -59,8 +87,9 @@ def create_publisher(publisher_class = WcsPublisher):
publisher.data_dir = DATA_DIR
if not os.path.exists(publisher.app_dir):
os.mkdir(publisher.app_dir)
publisher.set_session_manager(sessions.StorageSessionManager())
publisher.set_session_manager(sessions.SqlSessionManager())
publisher.set_config()
publisher.set_connection()
return publisher
def create_vhost_publisher():

View File

@ -14,24 +14,15 @@ class CategoryUI:
def form_new(self):
form = Form(enctype="multipart/form-data")
auto_id = misc.cfg.get('misc', {}).get('auto-id', False)
if not auto_id:
form.add(StringWidget, "id", title = _('Category Id'), required = True, size=30,
value = self.category.id)
form.add(StringWidget, "name", title = _('Category Name'), required = True, size=30,
value = self.category.name)
form.add(StringWidget, "name", title = _('Category Name'), required = True, size=30)
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def form_edit(self):
form = Form(enctype="multipart/form-data")
auto_id = misc.cfg.get('misc', {}).get('auto-id', False)
if auto_id:
form.add_hidden("id", value = self.category.id)
else:
form.add(StringWidget, "id", title = _('Category Id'), required = False, size=30,
value = self.category.id, readonly = 'readonly')
form.add(StringWidget, "id", title = _('Category Id'), required = False, size=30,
value = self.category.id, readonly = 'readonly')
form.add(StringWidget, "name", title = _('Category Name'), required = True, size=30,
value = self.category.name)
form.add_submit("submit", _("Submit"))
@ -39,20 +30,10 @@ class CategoryUI:
return form
def submit_form(self, form):
for f in ('name',):
setattr(self.category, f, form.get_widget(f).parse())
if not hasattr(self.category, 'id') or not self.category.id:
auto_id = misc.cfg.get('misc', {}).get('auto-id', False)
if auto_id:
new_id = base_id = misc.simplify(self.category.name)
existing_ids = storage.get_storage().keys('formdefs')
i = 1
while new_id in existing_ids:
new_id = '%s-%d' % (base_id, i)
i += 1
self.category.id = new_id
else:
self.category.id = form.get_widget('id').parse()
if self.category:
self.category.name = form.get_widget('name').parse()
else:
category = Category(name = form.get_widget('name').parse())
@ -60,7 +41,7 @@ class CategoryPage(Directory):
_q_exports = ["edit", "delete"]
def __init__(self, component):
self.category = storage.get_storage().retrieve('categories', component)
self.category = Category.get(component)
self.category_ui = CategoryUI(self.category)
def edit [html] (self):
@ -75,7 +56,6 @@ class CategoryPage(Directory):
html_foot()
else:
self.category_ui.submit_form(form)
storage.get_storage().store(self.category)
return redirect('..')
def delete [html] (self):
@ -92,7 +72,7 @@ class CategoryPage(Directory):
form.render()
html_foot()
else:
storage.get_storage().remove_id('categories', self.category.id)
self.category.destroySelf()
return redirect('..')
@ -113,20 +93,18 @@ class CategoriesDirectory(Directory):
</ul>""" % _('New Category')
'<div class="biglist">'
for k in storage.get_storage().keys('categories'):
category = storage.get_storage().retrieve('categories', k)
for category in Category.select(orderBy = Category.q.name):
"""<div class="biglist-item">"""
"<h3>%s</h3>" % category.name
"""<p><span class="cmds"> [ """
"""<a href="%s/edit">%s</a> - """ % (k, _('Edit'))
"""<a href="%s/delete">%s</a> """ % (k, _('Delete'))
"""<a href="%s/edit">%s</a> - """ % (category.id, _('Edit'))
"""<a href="%s/delete">%s</a> """ % (category.id, _('Delete'))
"]</span></p></div>"
'</div>'
html_foot()
def new [html] (self):
category = Category()
category_ui = CategoryUI(category)
category_ui = CategoryUI(None)
form = category_ui.form_new()
if form.get_widget('cancel').parse():
return redirect('.')
@ -138,7 +116,6 @@ class CategoriesDirectory(Directory):
html_foot()
else:
category_ui.submit_form(form)
storage.get_storage().store(category)
return redirect('.')
def _q_lookup(self, component):

View File

@ -4,31 +4,35 @@ from quixote.directory import Directory
from menu import html_top, html_foot, error_page
from wcs import misc
from wcs import storage
from wcs.errors import *
from wcs.form import *
from wcs.consultationdef import Consultation
from wcs.consultationdef import Consultation, ConsultationField
from wcs.formdata import FormData
from wcs.users import User
from wcs.categories import Category
from wcs.roles import Role
def ellipsize(s, length = 30):
if not s or len(s) < length:
return s
return s[:length-5] + ' (...)'
def get_user_roles():
l = []
for role in storage.get_storage().values('roles'):
l.append( (role.id, role.name) )
return l
def get_user_roles(include_system = False):
r = []
for x in Role.select():
if x.system:
if include_system:
r.append( (x.id, _(x.name)) )
else:
r.append( (x.id, x.name) )
return r
def get_categories():
l = []
for category in storage.get_storage().values('categories'):
l.append( (category.id, category.name) )
return l
return [(x.id, x.name) for x in Category.select()]
question_types = [ ('string', _('Text (line)')),
field_types = [ ('string', _('Text (line)')),
('text', _('Text (block)')),
('email', _('Email Address')),
('bool', _('Check Box')),
@ -41,29 +45,31 @@ question_types = [ ('string', _('Text (line)')),
]
class QuestionWidget(CompositeWidget):
def __init__(self, name, value=None, **kwargs):
def __init__(self, name, value=None, consultation = None, **kwargs):
CompositeWidget.__init__(self, name, value, **kwargs)
if not value:
value = {}
self.value = value
self.add(TextWidget, 'question', value.get('question', ''), render_br = False,
if self.value is None:
self.value = {}
self.add(HiddenWidget, 'id', self.value.get('id'))
self.add(TextWidget, 'label', self.value.get('label', ''), render_br = False,
cols = 70, rows = 3)
self.add(SingleSelectWidget, 'answer', value.get('answer', ''), required=True,
options = question_types, render_br = False)
self.add(SingleSelectWidget, 'type', self.value.get('type'), required=True,
options = field_types)
def render_content [html] (self):
question_widget = self.get_widget('question')
question_widget.render()
_("Answer")
answer_widget = self.get_widget('answer')
answer_widget.render()
id_widget = self.get_widget('id')
id_widget.render()
label_widget = self.get_widget('label')
label_widget.render()
_("Type")
type_widget = self.get_widget('type')
type_widget.render()
'<hr />'
def _parse(self, request):
for k in ('question', 'answer'):
self.value[k] = self.get(k)
if not self.value.has_key('required'):
self.value['required'] = True
self.value['id'] = self.get('id')
self.value['label'] = self.get('label')
self.value['type'] = self.get('type')
class ConsultationUI:
@ -72,80 +78,104 @@ class ConsultationUI:
def edit_form_ui(self):
form = Form(enctype="multipart/form-data")
auto_id = misc.cfg.get('misc', {}).get('auto-id', False)
if not auto_id:
form.add(StringWidget, "id", title = _('Consultation Id'), required = True, size=30,
value = self.consultation.id)
form.add(StringWidget, "name", title = _('Consultation Name'), required = True, size=30,
value = self.consultation.name)
value = self.consultation and self.consultation.name)
if self.consultation and self.consultation.fields:
fields_list = [{'label': x.label, 'type': x.type, 'id': x.id} \
for x in self.consultation.fields]
else:
fields_list = []
form.add(WidgetList, 'questions', title = _('Questions'), element_type = QuestionWidget,
value = self.consultation.questions, add_element_label = _('Add Question'),
element_kwargs = {'render_br': False})
value = fields_list, add_element_label = _('Add Field'),
element_kwargs = {'render_br': False, 'consultation': self.consultation})
form.add(SingleSelectWidget, 'receiver', title = _('Recipient'), required = True,
value = self.consultation.receiver, options = get_user_roles())
value = self.consultation and self.consultation.receiverID,
options = get_user_roles())
form.add(WidgetList, 'roles', title = _('Roles'), element_type = SingleSelectWidget,
hint = _('Only show this consultation to the given roles.'),
value = self.consultation.roles, add_element_label = _('Add Role'),
value = self.consultation and [x.id for x in self.consultation.roles],
add_element_label = _('Add Role'),
element_kwargs = {'render_br': False,
'options': [('', '---'), ('logged-users', _('Logged Users'))] + get_user_roles()})
'options': [('', '---')] + get_user_roles(include_system = True)})
form.add(SingleSelectWidget, 'category', title = _('Category'),
value = self.consultation.category,
value = self.consultation and self.consultation.categoryID,
options = [('', '---')] + get_categories())
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def submit_form(self, form):
for f in ('name', 'questions', 'receiver', 'category'):
setattr(self.consultation, f, form.get_widget(f).parse())
if self.consultation:
consultation = self.consultation
else:
consultation = Consultation(name = form.get_widget('name').parse())
if not hasattr(self.consultation, 'id') or not self.consultation.id:
auto_id = misc.cfg.get('misc', {}).get('auto-id', False)
if auto_id:
new_id = base_id = misc.simplify(self.consultation.name)
existing_ids = storage.get_storage().keys('consultations')
i = 1
while new_id in existing_ids:
new_id = '%s-%d' % (base_id, i)
i += 1
self.consultation.id = new_id
for f in ('name', ):
setattr(consultation, f, form.get_widget(f).parse())
receiver = form.get_widget('receiver').parse()
if receiver:
consultation.receiver = Role.get(receiver)
category = form.get_widget('category').parse()
if category:
consultation.category = Role.get(category)
for field in form.get_widget('fields').parse():
if not field['label']:
if field['id']:
FormField.delete(field['id'])
continue
if field['id']:
form_field = ConsultationField.get(field['id'])
form_field.set(label = field['label'], type = field['type'])
else:
self.consultation.id = form.get_widget('id').parse()
self.consultation.questions = [x for x in self.consultation.questions if x['question']] # remove empty questions
self.consultation.roles = [x for x in form.get_widget('roles').parse() if x]
form_field = ConsultationField(label = field['label'], type = field['type'],
consultation = self.consultation)
class ConsultationFieldPage(Directory):
new_roles = [x for x in form.get_widget('roles').parse() if x]
for role in consultation.roles or []:
if not role.id in new_roles:
consultation.removeRole(role)
for role_id in new_roles:
if not role_id in [x.id for x in consultation.roles]:
consultation.addRole(Role.get(role_id))
return consultation
class FieldDefPage(Directory):
_q_exports = ['', 'delete', 'down', 'up']
def __init__(self, consultation, question_no):
self.consultation = consultation
self.question_no = question_no
def __init__(self, field_id):
self.field = ConsultationField.get(field_id)
def form(self):
value = self.consultation.questions[self.question_no]
form = Form(enctype="multipart/form-data")
form.add(TextWidget, 'question', title = _('Question'), value = value.get('question', ''),
readonly = "readonly", cols = 70, rows = 3)
form.add(SingleSelectWidget, 'answer', title = _('Answer'),
value = value.get('answer', ''), required=True,
options = question_types)
form.add(TextWidget, 'label', title = _('Label'), value = self.field.label,
cols = 70, rows = 3, required = True)
form.add(SingleSelectWidget, 'type', title = _('Type'),
value = self.field.type, required=True,
options = field_types)
form.add(CheckboxWidget, 'required', title = _('Required'),
value = value.get('required', False))
value = self.field.required)
if value.get('answer') == 'item':
current_type = form.get_widget('type').parse()
if current_type == 'item':
form.add(WidgetList, 'items', title = _('Items'), element_type = StringWidget,
value = value.get('items', []), required = True,
value = self.field.extra.get('items', []), required = True,
element_kwargs = {'render_br': False})
form.add(CheckboxWidget, 'show-as-radio', title = _('Show as radio buttons'),
value = value.get('show-as-radio', False))
elif value.get('answer') == 'text':
value = self.field.extra.get('show-as-radio', False))
elif current_type == 'text':
form.add(StringWidget, 'cols', title = _('Line length'),
value = value.get('cols', ''))
value = self.field.extra.get('cols', ''))
form.add(StringWidget, 'rows', title = _('Number of rows'),
value = value.get('rows', ''))
elif value.get('answer') == 'string':
value = self.field.extra.get('rows', ''))
elif current_type == 'string':
form.add(StringWidget, 'size', title = _('Line length'),
value = value.get('size', ''))
value = self.field.extra.get('size', ''))
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
@ -153,39 +183,41 @@ class ConsultationFieldPage(Directory):
return form
def _q_index [html] (self):
value = self.consultation.questions[self.question_no]
form = self.form()
redo = False
if form.get_widget('cancel').parse():
return redirect('../questions')
return redirect('../../%s/fields' % self.field.consultationID)
if form.get_widget('items') and form.get_widget('items').get_widget('add_element').parse():
form.clear_errors()
redo = True
if form.get_widget('items') and form.get_widget('type').parse() != 'list':
form.clear_errors()
redo = True
if redo or not form.is_submitted() or form.has_errors():
html_top('consultations', '%s - %s' % (_('Consultation'), self.consultation.name))
'<h2>%s - %s</h2>' % (self.consultation.name, value['question'])
html_top('consultations', '%s - %s' % (
_('Consultation'), self.field.consultation.name))
'<h2>%s - %s</h2>' % (self.field.consultation.name, self.field.label)
form.render()
html_foot()
else:
self.submit(form)
if form.get_widget('items') is None and value['answer'] == 'item':
if form.get_widget('items') is None and self.field.type == 'item':
return redirect('.')
return redirect('../questions')
return redirect('../../%s/fields' % self.field.consultationID)
def submit(self, form):
value = self.consultation.questions[self.question_no]
for f in ('question', 'answer', 'required', 'items', 'show-as-radio',
'rows', 'cols', 'size'):
for f in ('label', 'type', 'required'):
setattr(self.field, f, form.get_widget(f).parse())
extra = {}
for f in ('items', 'show-as-radio', 'rows', 'cols', 'size'):
w = form.get_widget(f)
if not w:
if value.has_key(f):
del value[f]
continue
value[f] = w.parse()
storage.get_storage().store(self.consultation)
extra[f] = w.parse()
self.field.extra = extra
def delete [html] (self):
value = self.consultation.questions[self.question_no]
@ -202,114 +234,82 @@ class ConsultationFieldPage(Directory):
form.render()
html_foot()
else:
del self.consultation.questions[self.question_no]
storage.get_storage().store(self.consultation)
return redirect('../questions')
pos = self.field.position
consultation = self.field.consultation
self.field.destroySelf()
for field in consultation.fields:
if field.position > pos:
field.position = field.position - 1
return redirect('../../%s/fields' % self.field.consultationID)
def down(self):
t = self.consultation.questions[self.question_no]
self.consultation.questions[self.question_no] = self.consultation.questions[self.question_no+1]
self.consultation.questions[self.question_no+1] = t
storage.get_storage().store(self.consultation)
return redirect('../questions')
next = [x for x in self.field.consultation.fields if x.position == self.field.position+1][0]
self.field.position = self.field.position+1
next.position = next.position-1
return redirect('../../%s/fields' % self.field.consultationID)
def up(self):
t = self.consultation.questions[self.question_no]
self.consultation.questions[self.question_no] = self.consultation.questions[self.question_no-1]
self.consultation.questions[self.question_no-1] = t
storage.get_storage().store(self.consultation)
return redirect('../questions')
prev = [x for x in self.field.consultation.fields if x.position == self.field.position-1][0]
self.field.position = self.field.position-1
prev.position = prev.position+1
return redirect('../../%s/fields' % self.field.consultationID)
class ConsultationPage(Directory):
_q_exports = ["questions", "edit", "delete", "listing", "csv", "duplicate"]
_q_exports = ["fields", "edit", "delete", "duplicate"]
def __init__(self, component):
self.consultation = storage.get_storage().retrieve('consultations', component)
try:
self.consultation = Consultation.get(component)
except SQLObjectNotFound:
raise TraversalError()
self.consultationui = ConsultationUI(self.consultation)
def questions [html] (self):
def fields [html] (self):
html_top('consultations', '%s - %s' % (_('Consultation'), self.consultation.name))
'<h2>%s</h2>' % self.consultation.name
'<table>'
for i, question in enumerate(self.consultation.questions):
for i, field in enumerate(self.consultation.fields):
'<tr>'
if question['answer'] in ('subtitle', 'title', 'comment'):
label = question['question']
if field.type in ('subtitle', 'title', 'comment'):
label = field.label
if len(label) > 40:
label = label[:35] + ' (...)'
if question['answer'] in ('subtitle', 'title'):
if field.type in ('subtitle', 'title'):
'<td colspan="4"><strong>%s</strong></td>' % label
else:
'<td colspan="4">%s</td>' % label
else:
answer = [x[1] for x in question_types if x[0] == question['answer']][0]
if question.has_key('required') and question['required']:
type = [x[1] for x in field_types if x[0] == field.type][0]
if field.required:
required = ''
else:
required = _('optional')
'<td>%s</td>' % question['question']
'<td>%s</td>' % answer
'<td>%s</td>' % field.label
'<td>%s</td>' % type
'<td>%s</td>' % required
'<td><a href="question-%d">%s</a></td>' % (i, _('Edit'))
'<td><a href="../fields/%d/">%s</a></td>' % (field.id, _('Edit'))
'<td><a href="question-%d/delete">%s</a></td>' % (i, _('Delete'))
'<td><a href="../fields/%d/delete">%s</a></td>' % (field.id, _('Delete'))
'<td>'
if i != 0:
'<a class="arrow" href="question-%d/up">%s</a>' % (i, '&#8593;')
'<a class="arrow" href="../fields/%d/up">%s</a>' % (field.id, '&#8593;')
'</td><td>'
if i+1 != len(self.consultation.questions):
'<a class="arrow" href="question-%d/down">%s</a>' % (i, '&#8595;')
if i+1 != len(self.consultation.fields):
'<a class="arrow" href="../fields/%d/up">%s</a>' % (field.id, '&#8595;')
'</td>'
'</tr>'
'</table>'
html_foot()
def listing [html] (self):
html_top('consultations', '%s - %s' % (_('Consultation'), self.consultation.name))
names = 'consultation-' + self.consultation.id
'<h2>%s</h2>' % self.consultation.name
'<a href="csv">%s</a>' % _('Export to CSV format')
'<table id="listing"><thead><tr>'
for f in self.consultation.questions:
if f['answer'] in ('title', 'subtitle'):
continue
"<th>%s</th>" % f['question']
"</tr></thead>"
"<tbody>"
for k in storage.get_storage().keys(names):
filled = storage.get_storage().retrieve(names, k)
"<tr>"
for f in self.consultation.questions:
if f['answer'] in ('title', 'subtitle'):
continue
"<td>%s</td>" % filled.data.get(f['question'], '')
"</tr>"
"</tbody></table>"
html_foot()
def csv [plain] (self):
names = 'consultation-' + self.consultation.id
def fixcsv(s):
if isinstance(s, basestring):
return s.replace('\n', ' ').replace(';', ',')
return str(s)
for k in storage.get_storage().keys(names):
filled = storage.get_storage().retrieve(names, k)
';'.join([fixcsv(filled.data.get(x['question'])) for x in self.consultation.questions]) + '\r\n'
response = get_response()
response.set_content_type('text/csv')
def edit [html] (self, duplicate = False):
form = self.consultationui.edit_form_ui()
if form.get_widget('cancel').parse():
return redirect('.')
redo = False
if form.get_widget('questions').get_widget('add_element').parse():
if form.get_widget('fields').get_widget('add_element').parse():
form.clear_errors()
redo = True
@ -327,10 +327,9 @@ class ConsultationPage(Directory):
html_foot()
else:
self.consultationui.submit_form(form)
storage.get_storage().store(self.consultation)
return redirect('..')
def duplicate [html] (self):
def duplicate [html] (self): # XXX: FIXME for SQLObject
self.consultationui.consultation.id = ''
return self.edit(duplicate = True)
@ -348,30 +347,27 @@ class ConsultationPage(Directory):
form.render()
html_foot()
else:
storage.get_storage().remove_id('consultations', self.consultation.id)
storage.get_storage().remove_all('consultation-%s' % self.consultation.id)
for formdata in FormData.select(FormData.q.consultationID == self.consultation.id):
formdata.destroySelf()
self.consultation.destroySelf()
return redirect('..')
class FieldsDirectory(Directory):
def _q_lookup(self, component):
if not component.startswith('question-'):
raise TraversalError()
try:
question_no = int(component[9:])
except ValueError:
raise TraversalError()
return ConsultationFieldPage(self.consultation, question_no)
return FieldDefPage(component)
class ConsultationsDirectory(Directory):
_q_exports = ["", "new"]
_q_exports = ['', 'new', 'fields']
fields = FieldsDirectory()
def _q_index [html] (self):
misc.reload_cfg()
html_top('consultations', title = _('Consultations'))
if len(get_user_roles()) > 0:
if Role.select().count > 1:
"""<ul id="nav-consultations-admin">
<li><a href="new">%s</a></li>
</ul>""" % _('New Consultation')
@ -379,23 +375,21 @@ class ConsultationsDirectory(Directory):
"<p>%s</p>" % _('You first have to define roles.')
'<div class="biglist">'
for k in storage.get_storage().keys('consultations'):
consultation = storage.get_storage().retrieve('consultations', k)
for consultation in Consultation.select():
'<div class="biglist-item">'
'<h3>%s</h3>' % consultation.name
'<p><span class="data">%s</span>' % ellipsize(consultation.receiver, 50)
'<p><span class="data">%s</span>' % ellipsize(consultation.receiver.name, 50)
'<span class="cmds"> [ '
'<a href="%s/edit">%s</a> - ' % (k, _('Edit'))
'<a href="%s/questions">%s</a> - ' % (k, _('Questions'))
'<a href="%s/delete">%s</a> - ' % (k, _('Delete'))
'<a href="%s/duplicate">%s</a> - ' % (k, _('Duplicate'))
'<a href="%s/listing">%s</a> ' % (k, _('Listing'))
'<a href="%s/edit">%s</a> - ' % (consultation.id, _('Edit'))
'<a href="%s/fields">%s</a> - ' % (consultation.id, _('Fields'))
'<a href="%s/delete">%s</a> - ' % (consultation.id, _('Delete'))
'<a href="%s/duplicate">%s</a> - ' % (consultation.id, _('Duplicate'))
']</span></p></div>'
'</div>'
html_foot()
def new [html] (self):
if len(get_user_roles()) == 0:
if Role.select(Role.q.system == False).count() == 0:
return error_page('consultations', _("You first have to define roles."))
consultation = Consultation()
consultationui = ConsultationUI(consultation)
@ -403,7 +397,7 @@ class ConsultationsDirectory(Directory):
if form.get_widget('cancel').parse():
return redirect('.')
redo = False
if form.get_widget('questions').get_widget('add_element').parse():
if form.get_widget('fields').get_widget('add_element').parse():
form.clear_errors()
redo = True
@ -413,9 +407,8 @@ class ConsultationsDirectory(Directory):
form.render()
html_foot()
else:
consultationui.submit_form(form)
storage.get_storage().store(consultation)
return redirect(consultation.id + '/questions')
consultation = consultationui.submit_form(form)
return redirect(str(consultation.id) + '/fields')
def _q_lookup(self, component):
return ConsultationPage(component)

View File

@ -4,27 +4,31 @@ from quixote.directory import Directory
from menu import html_top, html_foot, error_page
from wcs import misc
from wcs import storage
from wcs.errors import *
from wcs.form import *
from wcs.formdef import FormDef
from wcs.formdata import FormData
from wcs.formdef import FormDef, FormField
from wcs.users import User
from wcs.categories import Category
from wcs.roles import Role
def ellipsize(s, length = 30):
if not s or len(s) < length:
return s
return s[:length-5] + ' (...)'
def get_user_roles():
l = []
for role in storage.get_storage().values('roles'):
l.append( (role.id, role.name) )
return l
def get_user_roles(include_system = False):
r = []
for x in Role.select():
if x.system:
if include_system:
r.append( (x.id, _(x.name)) )
else:
r.append( (x.id, x.name) )
return r
def get_categories():
l = []
for category in storage.get_storage().values('categories'):
l.append( (category.id, category.name) )
return l
return [(x.id, x.name) for x in Category.select()]
@ -40,26 +44,28 @@ field_types = [ ('string', _('Text (line)')),
]
class FieldWidget(CompositeWidget):
def __init__(self, name, value=None, **kwargs):
def __init__(self, name, value = None, formdef = None, **kwargs):
CompositeWidget.__init__(self, name, value, **kwargs)
if not value:
value = {}
self.value = value
self.add(StringWidget, 'name', value.get('name', ''))
self.add(SingleSelectWidget, 'type', value.get('type', ''), required=True,
if self.value is None:
self.value = {}
self.add(HiddenWidget, 'id', self.value.get('id'))
self.add(StringWidget, 'label', self.value.get('label'))
self.add(SingleSelectWidget, 'type', self.value.get('type'), required=True,
options = field_types)
def render_content [html] (self):
name_widget = self.get_widget('name')
name_widget.render()
id_widget = self.get_widget('id')
id_widget.render()
label_widget = self.get_widget('label')
label_widget.render()
type_widget = self.get_widget('type')
type_widget.render()
def _parse(self, request):
for k in ('name', 'type'):
self.value[k] = self.get(k)
if not self.value.has_key('required'):
self.value['required'] = True
self.value['id'] = self.get('id')
self.value['label'] = self.get('label')
self.value['type'] = self.get('type')
class FormDefUI:
@ -68,92 +74,113 @@ class FormDefUI:
def edit_form_ui(self):
form = Form(enctype="multipart/form-data")
auto_id = misc.cfg.get('misc', {}).get('auto-id', False)
if not auto_id:
form.add(StringWidget, "id", title = _('Form Id'), required = True, size=30,
value = self.formdef.id)
form.add(StringWidget, "name", title = _('Form Name'), required = True, size=30,
value = self.formdef.name)
value = self.formdef and self.formdef.name)
if self.formdef and self.formdef.fields:
fields_list = [{'label': x.label, 'type': x.type, 'id': x.id} \
for x in self.formdef.fields]
else:
fields_list = []
form.add(WidgetList, 'fields', title = _('Fields'), element_type = FieldWidget,
value = self.formdef.fields, add_element_label = _('Add Field'),
element_kwargs = {'render_br': False})
value = fields_list, add_element_label = _('Add Field'),
element_kwargs = {'formdef': self.formdef, 'render_br': False})
form.add(SingleSelectWidget, 'receiver', title = _('Recipient'), required = True,
value = self.formdef.receiver, options = get_user_roles())
value = self.formdef and self.formdef.receiverID,
options = get_user_roles())
form.add(WidgetList, 'roles', title = _('Roles'), element_type = SingleSelectWidget,
hint = _('Only show this form to the given roles.'),
value = self.formdef.roles, add_element_label = _('Add Role'),
value = self.formdef and [x.id for x in self.formdef.roles],
add_element_label = _('Add Role'),
element_kwargs = {'render_br': False,
'options': [('', '---'), ('logged-users', _('Logged Users'))] + get_user_roles()})
'options': [('', '---')] + get_user_roles(include_system = True)})
form.add(SingleSelectWidget, 'category', title = _('Category'),
value = self.formdef.category,
options = [('', '---')] + get_categories())
value = self.formdef and self.formdef.categoryID,
options = [(None, '---')] + get_categories())
form.add(CheckboxWidget, 'confirmation', title = _('Include confirmation page'),
value = self.formdef.confirmation)
value = (self.formdef and self.formdef.confirmation) or True)
form.add(CheckboxWidget, 'discussion', title = _('Allow discussion'),
value = self.formdef.discussion)
value = self.formdef and self.formdef.discussion)
form.add(CheckboxWidget, 'public', title = _('Public Access'),
hint = _('all forms viewable by anynone'),
value = self.formdef.public)
value = self.formdef and self.formdef.public)
form.add(CheckboxWidget, 'detailed_emails', title = _('Send detailed notification emails'),
value = self.formdef.detailed_emails)
value = self.formdef and self.formdef.detailed_emails)
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def submit_form(self, form):
for f in ('name', 'fields', 'receiver', 'category', 'confirmation',
'discussion', 'public', 'detailed_emails'):
setattr(self.formdef, f, form.get_widget(f).parse())
if self.formdef:
formdef = self.formdef
else:
formdef = FormDef(name = form.get_widget('name').parse())
if not hasattr(self.formdef, 'id') or not self.formdef.id:
auto_id = misc.cfg.get('misc', {}).get('auto-id', False)
if auto_id:
new_id = base_id = misc.simplify(self.formdef.name)
existing_ids = storage.get_storage().keys('formdefs')
i = 1
while new_id in existing_ids:
new_id = '%s-%d' % (base_id, i)
i += 1
self.formdef.id = new_id
for f in ('name', 'confirmation', 'discussion', 'public', 'detailed_emails'):
setattr(formdef, f, form.get_widget(f).parse())
receiver = form.get_widget('receiver').parse()
if receiver:
formdef.receiver = Role.get(receiver)
category = form.get_widget('category').parse()
if category:
formdef.category = Role.get(category)
for field in form.get_widget('fields').parse():
if not field['label']:
if field['id']:
FormField.delete(field['id'])
continue
if field['id']:
form_field = FormField.get(field['id'])
form_field.set(label = field['label'], type = field['type'])
else:
self.formdef.id = form.get_widget('id').parse()
self.formdef.fields = [x for x in self.formdef.fields if x['name']] # remove empty names
self.formdef.roles = [x for x in form.get_widget('roles').parse() if x]
form_field = FormField(label = field['label'], type = field['type'],
formdef = self.formdef)
class FormDefFieldPage(Directory):
new_roles = [x for x in form.get_widget('roles').parse() if x]
for role in formdef.roles or []:
if not role.id in new_roles:
formdef.removeRole(role)
for role_id in new_roles:
if not role_id in [x.id for x in formdef.roles]:
formdef.addRole(Role.get(role_id))
return formdef
class FieldDefPage(Directory):
_q_exports = ['', 'delete', 'down', 'up']
def __init__(self, formdef, field_no):
self.formdef = formdef
self.field_no = field_no
def __init__(self, field_id):
self.field = FormField.get(field_id)
def form(self):
value = self.formdef.fields[self.field_no]
form = Form(enctype="multipart/form-data")
form.add(StringWidget, 'name', title = _('Name'), value = value.get('name', ''),
readonly = "readonly")
form.add(StringWidget, 'label', title = _('Label'), value = self.field.label,
required = True)
form.add(SingleSelectWidget, 'type', title = _('Type'),
value = value.get('type', ''), required=True,
value = self.field.type, required=True,
options = field_types)
form.add(CheckboxWidget, 'required', title = _('Required'),
value = value.get('required', False))
value = self.field.required)
if value.get('type') == 'item':
current_type = form.get_widget('type').parse()
if current_type == 'item':
form.add(WidgetList, 'items', title = _('Items'), element_type = StringWidget,
value = value.get('items', []), required = True,
value = self.field.extra.get('items', []), required = True,
element_kwargs = {'render_br': False})
form.add(CheckboxWidget, 'show-as-radio', title = _('Show as radio buttons'),
value = value.get('show-as-radio', False))
elif value.get('type') == 'text':
value = self.field.extra.get('show-as-radio', False))
elif current_type == 'text':
form.add(StringWidget, 'cols', title = _('Line length'),
value = value.get('cols', ''))
value = self.field.extra.get('cols', ''))
form.add(StringWidget, 'rows', title = _('Number of rows'),
value = value.get('rows', ''))
value = self.field.extra.get('rows', ''))
form.add(CheckboxWidget, 'pre', title = _('Preformatted Text'),
value = value.get('pre', False))
elif value.get('type') == 'string':
value = self.field.extra.get('pre', False))
elif current_type == 'string':
form.add(StringWidget, 'size', title = _('Line length'),
value = value.get('size', ''))
value = self.field.extra.get('size', ''))
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
@ -161,42 +188,42 @@ class FormDefFieldPage(Directory):
return form
def _q_index [html] (self):
value = self.formdef.fields[self.field_no]
form = self.form()
redo = False
if form.get_widget('cancel').parse():
return redirect('../fields')
return redirect('../../%s/fields' % self.field.formdefID)
if form.get_widget('items') and form.get_widget('items').get_widget('add_element').parse():
form.clear_errors()
redo = True
if form.get_widget('items') and form.get_widget('type').parse() != 'list':
form.clear_errors()
redo = True
if redo or not form.is_submitted() or form.has_errors():
html_top('forms', '%s - %s' % (_('Form'), self.formdef.name))
'<h2>%s - %s</h2>' % (self.formdef.name, value['name'])
html_top('forms', '%s - %s' % (_('Form'), self.field.formdef.name))
'<h2>%s - %s</h2>' % (self.field.formdef.name, self.field.label)
form.render()
html_foot()
else:
self.submit(form)
if form.get_widget('items') is None and value['type'] == 'item':
if form.get_widget('items') is None and self.field.type == 'item':
return redirect('.')
return redirect('../fields')
return redirect('../../%s/fields' % self.field.formdefID)
def submit(self, form):
value = self.formdef.fields[self.field_no]
for f in ('name', 'type', 'required', 'items', 'show-as-radio',
'rows', 'cols', 'size', 'pre'):
for f in ('label', 'type', 'required'):
setattr(self.field, f, form.get_widget(f).parse())
extra = {}
for f in ('items', 'show-as-radio', 'rows', 'cols', 'size', 'pre'):
w = form.get_widget(f)
if not w:
if value.has_key(f):
del value[f]
continue
value[f] = w.parse()
storage.get_storage().store(self.formdef)
extra[f] = w.parse()
self.field.extra = extra
def delete [html] (self):
value = self.formdef.fields[self.field_no]
form = Form(enctype="multipart/form-data")
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
"You are about to remove a field.")))
@ -206,35 +233,40 @@ class FormDefFieldPage(Directory):
return redirect('../fields')
if not form.is_submitted() or form.has_errors():
html_top('forms', title = _('Delete Field'))
'<h2>%s %s</h2>' % (_('Deleting Field:'), value['name'])
'<h2>%s %s</h2>' % (_('Deleting Field:'), self.field.label)
form.render()
html_foot()
else:
del self.formdef.fields[self.field_no]
storage.get_storage().store(self.formdef)
return redirect('../fields')
pos = self.field.position
formdef = self.field.formdef
self.field.destroySelf()
for field in formdef.fields:
if field.position > pos:
field.position = field.position - 1
return redirect('../../%s/fields' % self.field.formdefID)
def down(self):
t = self.formdef.fields[self.field_no]
self.formdef.fields[self.field_no] = self.formdef.fields[self.field_no+1]
self.formdef.fields[self.field_no+1] = t
storage.get_storage().store(self.formdef)
return redirect('../fields')
next = [x for x in self.field.formdef.fields if x.position == self.field.position+1][0]
self.field.position = self.field.position+1
next.position = next.position-1
return redirect('../../%s/fields' % self.field.formdefID)
def up(self):
t = self.formdef.fields[self.field_no]
self.formdef.fields[self.field_no] = self.formdef.fields[self.field_no-1]
self.formdef.fields[self.field_no-1] = t
storage.get_storage().store(self.formdef)
return redirect('../fields')
prev = [x for x in self.field.formdef.fields if x.position == self.field.position-1][0]
self.field.position = self.field.position-1
prev.position = prev.position+1
return redirect('../../%s/fields' % self.field.formdefID)
class FormDefPage(Directory):
_q_exports = ["fields", "edit", "delete", "listing", "csv", "duplicate"]
_q_exports = ["fields", "edit", "delete", "duplicate"]
def __init__(self, component):
self.formdef = storage.get_storage().retrieve('formdefs', component)
try:
self.formdef = FormDef.get(component)
except SQLObjectNotFound:
raise TraversalError()
self.formdefui = FormDefUI(self.formdef)
def fields [html] (self):
@ -243,75 +275,38 @@ class FormDefPage(Directory):
'<table>'
for i, field in enumerate(self.formdef.fields):
'<tr>'
if field['type'] in ('subtitle', 'title', 'comment'):
label = field['name']
if field.type in ('subtitle', 'title', 'comment'):
label = field.label
if len(label) > 40:
label = label[:35] + ' (...)'
if field['type'] in ('subtitle', 'title'):
if field.type in ('subtitle', 'title'):
'<td colspan="4"><strong>%s</strong></td>' % label
else:
'<td colspan="4">%s</td>' % label
else:
type = [x[1] for x in field_types if x[0] == field['type']][0]
if field.has_key('required') and field['required']:
type = [x[1] for x in field_types if x[0] == field.type][0]
if field.required:
required = ''
else:
required = _('optional')
'<td>%s</td>' % field['name']
'<td>%s</td>' % field.label
'<td>%s</td>' % type
'<td>%s</td>' % required
'<td><a href="field-%d">%s</a></td>' % (i, _('Edit'))
'<td><a href="../fields/%d/">%s</a></td>' % (field.id, _('Edit'))
'<td><a href="field-%d/delete">%s</a></td>' % (i, _('Delete'))
'<td><a href="../fields/%d/delete">%s</a></td>' % (field.id, _('Delete'))
'<td>'
if i != 0:
'<a class="arrow" href="field-%d/up">%s</a>' % (i, '&#8593;')
'<a class="arrow" href="../fields/%d/up">%s</a>' % (field.id, '&#8593;')
'</td><td>'
if i+1 != len(self.formdef.fields):
'<a class="arrow" href="field-%d/down">%s</a>' % (i, '&#8595;')
'<a class="arrow" href="../fields/%d/down">%s</a>' % (field.id, '&#8595;')
'</td>'
'</tr>'
'</table>'
html_foot()
def listing [html] (self):
html_top('forms', '%s - %s' % (_('Form'), self.formdef.name))
names = 'form-' + self.formdef.id
'<h2>%s</h2>' % self.formdef.name
'<a href="csv">%s</a>' % _('Export to CSV format')
'<table id="listing"><thead><tr>'
for f in self.formdef.fields:
if f['type'] in ('title', 'subtitle'):
continue
"<th>%s</th>" % f['name']
"</tr></thead>"
"<tbody>"
for k in storage.get_storage().keys(names):
filled = storage.get_storage().retrieve(names, k)
"<tr>"
for f in self.formdef.fields:
if f['type'] in ('title', 'subtitle'):
continue
"<td>%s</td>" % filled.data.get(f['name'], '')
"</tr>"
"</tbody></table>"
html_foot()
def csv [plain] (self):
names = 'form-' + self.formdef.id
def fixcsv(s):
if isinstance(s, basestring):
return s.replace('\n', ' ').replace(';', ',')
return str(s)
for k in storage.get_storage().keys(names):
filled = storage.get_storage().retrieve(names, k)
';'.join([fixcsv(filled.data.get(x['name'])) for x in self.formdef.fields]) + '\r\n'
response = get_response()
response.set_content_type('text/csv')
def edit [html] (self, duplicate = False):
form = self.formdefui.edit_form_ui()
if form.get_widget('cancel').parse():
@ -335,10 +330,9 @@ class FormDefPage(Directory):
html_foot()
else:
self.formdefui.submit_form(form)
storage.get_storage().store(self.formdef)
return redirect('..')
def duplicate [html] (self):
def duplicate [html] (self): # XXX: FIXME for SQLObject
self.formdefui.formdef.id = ''
return self.edit(duplicate = True)
@ -356,30 +350,28 @@ class FormDefPage(Directory):
form.render()
html_foot()
else:
storage.get_storage().remove_id('formdefs', self.formdef.id)
storage.get_storage().remove_all('form-%s' % self.formdef.id)
for formdata in FormData.select(FormData.q.formdefID == self.formdef.id):
formdata.destroySelf()
self.formdef.destroySelf()
return redirect('..')
def _q_lookup(self, component):
if not component.startswith('field-'):
raise TraversalError()
try:
field_no = int(component[6:])
except ValueError:
raise TraversalError()
return FormDefFieldPage(self.formdef, field_no)
class FieldsDirectory(Directory):
def _q_lookup(self, component):
return FieldDefPage(component)
class FormsDirectory(Directory):
_q_exports = ["", "new"]
_q_exports = ['', 'new', 'fields']
fields = FieldsDirectory()
def _q_index [html] (self):
misc.reload_cfg()
html_top('forms', title = _('Forms'))
if len(get_user_roles()) > 0:
if Role.select(Role.q.system == False).count():
"""<ul id="nav-forms-admin">
<li><a href="new">%s</a></li>
</ul>""" % _('New Form')
@ -387,27 +379,23 @@ class FormsDirectory(Directory):
"<p>%s</p>" % _('You first have to define roles.')
'<div class="biglist">'
for k in storage.get_storage().keys('formdefs'):
formdef = storage.get_storage().retrieve('formdefs', k)
for formdef in FormDef.select():
'<div class="biglist-item">'
"<h3>%s</h3>" % formdef.name
'<p><span class="data">%s</span>' % ellipsize(
storage.get_storage().retrieve('roles', formdef.receiver).name, 50)
'<p><span class="data">%s</span>' % ellipsize(formdef.receiver.name, 50)
'<span class="cmds"> [ '
'<a href="%s/edit">%s</a> - ' % (k, _('Edit'))
'<a href="%s/fields">%s</a> - ' % (k, _('Fields'))
'<a href="%s/delete">%s</a> - ' % (k, _('Delete'))
'<a href="%s/duplicate">%s</a> - ' % (k, _('Duplicate'))
'<a href="%s/listing">%s</a> ' % (k, _('Listing'))
'<a href="%s/edit">%s</a> - ' % (formdef.id, _('Edit'))
'<a href="%s/fields">%s</a> - ' % (formdef.id, _('Fields'))
'<a href="%s/delete">%s</a> - ' % (formdef.id, _('Delete'))
'<a href="%s/duplicate">%s</a>' % (formdef.id, _('Duplicate'))
']</span></p></div>'
'</div>'
html_foot()
def new [html] (self):
if len(get_user_roles()) == 0:
if Role.select(Role.q.system == False).count() == 0:
return error_page('forms', _("You first have to define roles."))
formdef = FormDef()
formdefui = FormDefUI(formdef)
formdefui = FormDefUI(None)
form = formdefui.edit_form_ui()
if form.get_widget('cancel').parse():
return redirect('.')
@ -422,9 +410,8 @@ class FormsDirectory(Directory):
form.render()
html_foot()
else:
formdefui.submit_form(form)
storage.get_storage().store(formdef)
return redirect(formdef.id + '/fields')
formdef = formdefui.submit_form(form)
return redirect(str(formdef.id) + '/fields')
def _q_lookup(self, component):
return FormDefPage(component)

View File

@ -1,6 +1,7 @@
import quixote
from wcs import storage
from wcs import misc
from wcs.users import User
items = [
('forms', N_('Forms')),
@ -34,10 +35,7 @@ def user_info [html] ():
session = quixote.get_session()
if not session or not session.user:
return ''
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
return ''
user = User.get(session.user)
logout_url = quixote.get_request().environ['SCRIPT_NAME'] + '/logout'
"""<ul class="user-info">
<li class="ui-name">%s</li>

View File

@ -3,9 +3,10 @@ from quixote.directory import Directory
from menu import html_top, html_foot
from wcs import errors
from wcs import misc
from wcs import storage
from wcs.roles import Role
from wcs.roles import Role, RoleEmail
from wcs.form import *
class RoleUI:
@ -14,16 +15,11 @@ class RoleUI:
def form_new(self):
form = Form(enctype="multipart/form-data")
auto_id = misc.cfg.get('misc', {}).get('auto-id', False)
if not auto_id:
form.add(StringWidget, "id", title = _('Role Id'), required = True, size=30,
value = self.role.id)
form.add(StringWidget, "name", title = _('Role Name'), required = True, size=30,
value = self.role.name)
form.add(StringWidget, "name", title = _('Role Name'), required = True, size=30)
form.add(TextWidget, "details", title = _('Role Details'), required = False,
cols = 40, rows = 5, value = self.role.details)
cols = 40, rows = 5)
form.add(WidgetList, 'emails', title = _('Role Emails'), element_type = StringWidget,
value = self.role.emails, add_element_label = _('Add Email'),
add_element_label = _('Add Email'),
element_kwargs = {'render_br': False, 'size': 30})
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
@ -31,45 +27,41 @@ class RoleUI:
def form_edit(self):
form = Form(enctype="multipart/form-data")
auto_id = misc.cfg.get('misc', {}).get('auto-id', False)
if auto_id:
form.add_hidden("id", value = self.role.id)
else:
form.add(StringWidget, "id", title = _('Role Id'), required = False, size=30,
value = self.role.id, readonly = 'readonly')
form.add(StringWidget, "id", title = _('Role Id'), required = False, size=30,
value = self.role.id, readonly = 'readonly')
form.add(StringWidget, "name", title = _('Role Name'), required = True, size=30,
value = self.role.name)
form.add(TextWidget, "details", title = _('Role Details'), required = False,
cols = 40, rows = 5, value = self.role.details)
form.add(WidgetList, 'emails', title = _('Role Emails'), element_type = StringWidget,
value = self.role.emails, add_element_label = _('Add Email'),
value = [x.email for x in self.role.emails],
add_element_label = _('Add Email'),
element_kwargs = {'render_br': False, 'size': 30})
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def submit_form(self, form):
for f in ('name', 'details', 'emails'):
setattr(self.role, f, form.get_widget(f).parse())
if not hasattr(self.role, 'id') or not self.role.id:
auto_id = misc.cfg.get('misc', {}).get('auto-id', False)
if auto_id:
new_id = base_id = misc.simplify(self.role.name)
existing_ids = storage.get_storage().keys('roles')
i = 1
while new_id in existing_ids:
new_id = '%s-%d' % (base_id, i)
i += 1
self.role.id = new_id
else:
self.role.id = form.get_widget('id').parse()
if self.role:
role = self.role
else:
role = Role(name = form.get_widget('name').parse())
for f in ('name', 'details'):
setattr(role, f, form.get_widget(f).parse())
for email in role.emails:
email.destroySelf()
for email in form.get_widget('emails').parse() or []:
RoleEmail(email = email, role = role)
class RolePage(Directory):
_q_exports = ["edit", "delete"]
def __init__(self, component):
self.role = storage.get_storage().retrieve('roles', component)
self.role = Role.get(component)
if self.role.system:
raise errors.TraversalError()
self.role_ui = RoleUI(self.role)
def edit [html] (self):
@ -84,7 +76,6 @@ class RolePage(Directory):
html_foot()
else:
self.role_ui.submit_form(form)
storage.get_storage().store(self.role)
return redirect('..')
def delete [html] (self):
@ -101,7 +92,7 @@ class RolePage(Directory):
form.render()
html_foot()
else:
storage.get_storage().remove_id('roles', self.role.id)
self.role.destroySelf()
return redirect('..')
@ -124,32 +115,29 @@ class RolesDirectory(Directory):
</ul>""" % _('New Role')
'<div class="biglist">'
for k in storage.get_storage().keys('roles'):
role = storage.get_storage().retrieve('roles', k)
for role in Role.select(Role.q.system == False):
'<div class="biglist-item">'
"<h3>%s</h3>" % role.name
'<p><span class="cmds"> [ '
'<a href="%s/edit">%s</a> - ' % (k, _('Edit'))
'<a href="%s/delete">%s</a> ' % (k, _('Delete'))
'<a href="%s/edit">%s</a> - ' % (role.id, _('Edit'))
'<a href="%s/delete">%s</a> ' % (role.id, _('Delete'))
"]</span></p></div>"
'</div>'
html_foot()
def new [html] (self):
role = Role()
role_ui = RoleUI(role)
role_ui = RoleUI(None)
form = role_ui.form_new()
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
if not form.is_submitted() or form.has_errors() or form.get_submit() is True:
html_top('roles', title = _('New Role'))
'<h2>%s</h2>' % _('New Role')
form.render()
html_foot()
else:
role_ui.submit_form(form)
storage.get_storage().store(role)
return redirect('.')
def _q_lookup(self, component):

View File

@ -1,4 +1,4 @@
from quixote import get_session
from quixote import get_session, get_session_manager
from quixote.directory import Directory, AccessControlled
import settings
@ -42,20 +42,29 @@ class RootDirectory(AccessControlled, Directory):
def _q_access(self):
session = get_session()
user = None
if session.user == 'ultra-user':
return
if not session.user or session.user == 'ultra-user':
if len(storage.get_storage().keys('users')) == 0 or session.user == 'ultra-user':
user = None
if session.user and not str(session.user).startswith('anonymous-'):
try:
user = users.User.get(session.user)
except KeyError:
pass
except ValueError: # oldWorld session, kill it
get_session().user = None
if not user:
query = users.User.select()
if query.count() == 0:
session.set_user('ultra-user')
return # bootstrapping
raise errors.AccessUnauthorizedError()
if session.user:
raise errors.AccessForbiddenError()
else:
raise errors.AccessUnauthorizedError()
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
raise errors.AccessForbiddenError()
if not 'site-admin' in user.roles:
if not user.is_admin:
raise errors.AccessForbiddenError()

View File

@ -9,16 +9,11 @@ from wcs import emails
from wcs import errors
from wcs import misc
from wcs.users import User
from wcs.roles import Role
from wcs import storage
from wcs.form import *
def get_user_roles():
l = [('site-admin', _('Site Administrator'))]
for role in storage.get_storage().values('roles'):
l.append( (role.id, role.name) )
return l
class UserUI:
def __init__(self, user):
@ -26,16 +21,16 @@ class UserUI:
def form_new(self):
form = Form(enctype="multipart/form-data")
form.add(StringWidget, "id", title = _('User Id'), required = True, size=30,
value = self.user.id)
form.add(StringWidget, "name", title = _('User Name'), required = True, size=30,
value = self.user.name)
form.add(StringWidget, "email", title = _('Email'), required = False, size=30,
value = self.user.email)
form.add(WidgetList, 'roles', title = _('Roles'), element_type = SingleSelectWidget,
value = self.user.roles, add_element_label = _('Add Role'),
element_kwargs = {str('render_br'): False,
str('options'): [('', '---')] + get_user_roles()})
form.add(StringWidget, "name", title = _('User Name'), required = True, size=30)
form.add(StringWidget, "email", title = _('Email'), required = False, size=30)
form.add(CheckboxWidget, 'is_admin', title = _('Administrator Account'))
roles = Role.select()
if roles.count():
form.add(WidgetList, 'roles', title = _('Roles'), element_type = SingleSelectWidget,
add_element_label = _('Add Role'),
element_kwargs = {
'render_br': False,
'options': [(None, '---')] + [(x.id, x.name) for x in roles]})
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
@ -49,26 +44,42 @@ class UserUI:
value = self.user.name)
form.add(StringWidget, "email", title = _('Email'), required = False, size=30,
value = self.user.email)
form.add(WidgetList, 'roles', title = _('Roles'), element_type = SingleSelectWidget,
value = self.user.roles, add_element_label = _('Add Role'),
element_kwargs = {str('render_br'): False,
str('options'): [('', '---')] + get_user_roles()})
form.add(CheckboxWidget, 'is_admin', title = _('Administrator Account'),
value = self.user.is_admin)
roles = Role.select()
if roles.count():
form.add(WidgetList, 'roles', title = _('Roles'), element_type = SingleSelectWidget,
value = [x.id for x in self.user.roles],
add_element_label = _('Add Role'),
element_kwargs = {
'render_br': False,
'options': [(None, '---')] + [(x.id, x.name) for x in roles]})
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def submit_form(self, form):
for f in ('id', 'name', 'email'):
setattr(self.user, f, form.get_widget(f).parse())
self.user.roles = [x for x in form.get_widget('roles').parse() if x]
if self.user:
user = self.user
else:
user = User()
for f in ('name', 'email', 'is_admin'):
setattr(user, f, form.get_widget(f).parse())
for role in user.roles:
user.removeRole(role)
if form.get_widget('roles'):
for role_id in form.get_widget('roles').parse() or []:
if not role_id:
continue
user.addRole(Role.get(role_id))
class UserPage(Directory):
_q_exports = ["edit", "delete", "token", "debug"]
def __init__(self, component):
self.user = storage.get_storage().retrieve('users', component)
self.user = User.get(component)
self.user_ui = UserUI(self.user)
def debug [html] (self):
@ -91,7 +102,6 @@ class UserPage(Directory):
html_foot()
else:
self.user_ui.submit_form(form)
storage.get_storage().store(self.user)
return redirect('..')
def delete [html] (self):
@ -108,7 +118,7 @@ class UserPage(Directory):
form.render()
html_foot()
else:
storage.get_storage().remove_id('users', self.user.id)
self.user.destroySelf()
return redirect('..')
def token [html] (self):
@ -132,7 +142,6 @@ class UserPage(Directory):
html_top('users', title = _('Identification Token'))
token = '-'.join(['%04d' % random.randint(1, 9999) for x in range(4)])
self.user.identification_token = str(token)
storage.get_storage().store(self.user)
"<p>"
_('Identification Token for %s:') % self.user.name
@ -169,8 +178,7 @@ class UsersDirectory(Directory):
<li><a href="new">%s</a></li>
</ul>""" % _('New User')
users = storage.get_storage().values('users')
users.sort(lambda x,y: cmp(x.name, y.name))
users = User.select(orderBy = User.q.name)
'<div class="biglist">'
for user in users:
@ -178,7 +186,11 @@ class UsersDirectory(Directory):
"<h3>%s</h3>" % user.name
'<p><span class="cmds"> [ '
if not user.name_identifiers:
'<a href="%s/token">%s</a> - ' % (user.id, _('Identification Token'))
if not user.identification_token:
'<a href="%s/token">%s</a> - ' % (user.id, _('Identification Token'))
else:
'<a href="%s/token">%s</a> (%s) - ' % (user.id,
_('Identification Token'), user.identification_token)
'<a href="%s/edit">%s</a> - ' % (user.id, _('Edit'))
'<a href="%s/delete">%s</a> ' % (user.id, _('Delete'))
']</span></p></div>'
@ -189,8 +201,7 @@ class UsersDirectory(Directory):
if len(misc.cfg.get('idp', {}).items()) == 0:
return error_page('users',
_('Liberty support must be setup before creating users.'))
user = User()
user_ui = UserUI(user)
user_ui = UserUI(None)
form = user_ui.form_new()
if form.get_widget('cancel').parse():
return redirect('.')
@ -202,7 +213,6 @@ class UsersDirectory(Directory):
html_foot()
else:
user_ui.submit_form(form)
storage.get_storage().store(user)
return redirect('.')
def _q_lookup(self, component):

8
wcs/anonylink.py Normal file
View File

@ -0,0 +1,8 @@
from sqlobject import *
class AnonymityLink(SQLObject):
_cacheValue = False
formdata = ForeignKey('FormData')
key = StringCol(length = 100, default = None)
name_identifier = StringCol(default = None)

View File

@ -1,10 +1,53 @@
import storage
from sqlobject import *
class Category(storage.Storable):
class Category(SQLObject):
key = 'id'
names = 'categories'
_cacheValues = False
def __init__(self):
self.id = ''
self.name = ''
name = StringCol(length = 100)
def migrateOldData(cls):
import storage
from formdef import FormDef
from consultationdef import Consultation
init_method = cls.__init__
cls.__init__ = lambda x: x
categories = storage.get_storage().values('categories')
items = [x.__dict__ for x in categories]
cls.__init__ = init_method
new_ids_mapping = []
for item in items:
category = Category(name = item['name'])
new_ids_mapping.append( (item['id'], category.id) )
# fixing other tables to get the new ids
# formdefs
formdef_init_method = FormDef.__init__
FormDef.__init__ = lambda x: x
formdefs = storage.get_storage().values('formdefs')
for formdef in formdefs:
cat = formdef.__dict__.get('category')
if not cat:
formdef.__dict__['category'] = None
else:
formdef.__dict__['category'] = [x[1] for x in new_ids_mapping if x[0] == cat][0]
storage.get_storage().store(formdef)
FormDef.__init__ = formdef_init_method
# consultations
consultation_init_method = Consultation.__init__
Consultation.__init__ = lambda x: x
consultations = storage.get_storage().values('consultations')
for consultation in consultations:
cat = consultation.__dict__['category']
if not cat:
consultation.__dict__['category'] = None
else:
consultation.__dict__['category'] = [x[1] for x in new_ids_mapping if x[0] == cat][0]
storage.get_storage().store(consultation)
Consultation.__init__ = consultation_init_method
migrateOldData = classmethod(migrateOldData)

View File

@ -1,3 +1,6 @@
from sqlobject import *
from extracols import DictPickleCol
import emails
from quixote import get_request
@ -5,6 +8,10 @@ from quixote import get_request
import storage
from form import *
from misc import simplify
from formdata import FormData
from anonylink import AnonymityLink
widget_classes = {
'string': StringWidget,
@ -21,60 +28,70 @@ widget_extra_attributes = {
}
class Consultation(storage.Storable):
class ConsultationField(SQLObject):
label = StringCol(length = 300)
type = StringCol(length = 20)
required = BoolCol(default = True)
extra = DictPickleCol(default = None)
consultation = ForeignKey('Consultation')
position = IntCol(default = None)
class Consultation(SQLObject):
key = 'id'
names = 'consultations'
receiver = None
roles = None
category = None
def __init__(self):
self.id = ''
self.name = ''
self.questions = []
self.receiver = ''
self.category = ''
self.roles = []
name = StringCol(length = 200)
url_name = StringCol(default = None, length = 200, alternateID = True)
fields = MultipleJoin('ConsultationField', joinMethodName = 'fields',
joinColumn = 'consultation_id', orderBy = ConsultationField.q.position)
receiver = ForeignKey('Role', default = None)
roles = RelatedJoin('Role')
category = ForeignKey('Category', default = None)
def _set_name(self, value):
self._SO_set_name(value)
if not self.url_name or FormData.select(FormData.q.consultationID == self.id).count() == 0:
self.url_name = simplify(value)
def create_form(self, page_no, answers = {}):
form = Form(enctype = "multipart/form-data", use_tokens = False)
current_page = 0
for i, question in enumerate(self.questions):
if question['answer'] == 'newpage':
for field in self.fields:
if field.type == 'newpage':
current_page += 1
if current_page > page_no:
break
continue
if current_page != page_no:
continue
if question['answer'] == 'title':
form.widgets.append(HtmlWidget(htmltext("<h3>%s</h3>" % question['question'])))
if field.type == 'title':
form.widgets.append(HtmlWidget(htmltext("<h3>%s</h3>" % field.label)))
continue
if question['answer'] == 'subtitle':
form.widgets.append(HtmlWidget(htmltext("<h4>%s</h4>" % question['question'])))
if field.type == 'subtitle':
form.widgets.append(HtmlWidget(htmltext("<h4>%s</h4>" % field.label)))
continue
if question['answer'] == 'comment':
form.widgets.append(HtmlWidget(htmltext("<p>%s</p>" % question['question'])))
if field.type == 'comment':
form.widgets.append(HtmlWidget(htmltext("<p>%s</p>" % field.label)))
continue
widget_class = widget_classes[question['answer']]
widget_class = widget_classes[field.type]
kwargs = {'required': question.get('required', True)}
if question['answer'] == 'item':
kwargs['options'] = question.get('items', ['---'])
if question.get('show-as-radio'):
kwargs = {'required': field.required}
if field.type == 'item':
kwargs['options'] = field.extra.get('items', ['---'])
if field.extra.get('show-as-radio'):
widget_class = RadiobuttonsWidget
if len(kwargs['options']) > 2:
kwargs['delim'] = htmltext('<br />')
value = None
if answers.has_key(question['question']):
kwargs['value'] = answers[question['question']]
if widget_extra_attributes.has_key(question['answer']):
for k in widget_extra_attributes[question['answer']]:
v = question.get(k, None)
kwargs['value'] = answers.get(field.id)
if widget_extra_attributes.has_key(field.type):
for k in widget_extra_attributes[field.type]:
v = field.extra.get(k, None)
if v is not None:
kwargs[k] = v
form.add(widget_class, "f%d" % i, title = question['question'], **kwargs)
form.add(widget_class, "f%d" % field.id, title = field.label, **kwargs)
if page_no != 0:
form.add_submit("previous", _("Previous"))
@ -84,10 +101,68 @@ class Consultation(storage.Storable):
def get_data(self, form):
d = {}
for i, question in enumerate(self.questions):
if question['answer'] in ('title', 'subtitle', 'comment'):
continue
if form.get_widget('f%d' % i):
d[question['question']] = form.get_widget('f%d' % i).parse()
for field in self.fields:
widget = form.get_widget('f%s' % field.id)
if widget:
d[field.id] = widget.parse()
return d
def get_filled(self, session):
completed = FormData.select(AND(
FormData.q.consultationID == self.id,
FormData.q.userID == session.user))
if completed.count():
return completed[0]
completed = AnonymityLink.select(OR(
AnonymityLink.q.name_identifier == session.name_identifier,
AND(AnonymityLink.q.key != None,
AnonymityLink.q.key == session.get_anonymous_key(False))))
for c in completed:
if c.formdata.consultationID == self.id:
return c.formdata
raise SQLObjectNotFound
def migrateOldData(cls):
import storage
from roles import Role, get_logged_users_role
from categories import Category
init_method = cls.__init__
cls.__init__ = lambda x: x
consultations = storage.get_storage().values('consultations')
items = [x.__dict__ for x in consultations]
cls.__init__ = init_method
items.sort(lambda x,y: cmp(x['id'], y['id']))
logged_users = get_logged_users_role()
for item in items:
consultation = Consultation(name = item['name'])
consultation.url_name = item['id']
consultation.receiver = Role.get(item['receiver'])
if item.get('category'):
consultation.category = Category.get(item['category'])
for role in item.get('roles', []):
if role == 'logged-users':
consultation.addRole(logged_users)
continue
consultation.addRole(Role.get(role))
for i, field in enumerate(item['questions']):
fieldObject = ConsultationField(label = field['question'], type = field['answer'],
consultation = consultation)
fieldObject.required = field.get('required', False)
fieldObject.position = i
d = field.copy()
del d['question']
del d['answer']
del d['required']
if d:
fieldObject.extra = d
# convert submitted forms
name = 'consultation-%s' % item['id']
newname = 'consultationtmp-%s' % consultation.id
storage.get_storage().rename_table(name, newname)
migrateOldData = classmethod(migrateOldData)

View File

@ -3,12 +3,18 @@ import os
from quixote import get_request, get_response, get_session
from quixote.directory import Directory
from sqlobject import *
from wcs import errors
from wcs import misc
from wcs import storage
from wcs import template
from wcs.form import *
from wcs.categories import Category
from wcs.consultationdef import Consultation
from wcs.formdata import FormData
from wcs.users import User
def html_top [html] (title = None):
template.html_top(title = title, script = '<script src="/js/sorttable.js"></script>')
@ -19,29 +25,25 @@ class BackOfficeDirectory(Directory):
_q_exports = [""]
def _q_index [html] (self):
html_top(_("Consultations"))
"<h1>%s</h1>" % _('Back Office')
html_top(_('Back Office'))
'<h1>%s</h1>' % _('Consultations')
session = get_session()
user = None
if session and session.user and not session.user.startswith(str('anonymous-')):
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
pass
if session and session.user and not str(session.user).startswith(str('anonymous-')):
user = User.get(session.user)
l = []
for k in storage.get_storage().keys(str('consultations')):
consultation = storage.get_storage().retrieve('consultations', k)
if user and (consultation.receiver in (user.roles or []) or 'site-admin' in user.roles):
l.append(consultation)
if user:
for consultation in Consultation.select(orderBy = Consultation.q.name):
if user.is_admin or consultation.receiver in user.roles:
l.append(consultation)
l.sort(lambda x,y: cmp(x.name, y.name))
cats = storage.get_storage().values('categories')
cats.sort(lambda x,y: cmp(x.name, y.name))
cats = Category.select(orderBy = Category.q.name)
one = False
for c in cats:
l2 = [x for x in l if x.category == c.id]
l2 = [x for x in l if x.categoryID == c.id]
if l2:
"<h2>%s</h2>" % c.name
"<ul>"
@ -59,6 +61,9 @@ class BackOfficeDirectory(Directory):
"""<li><a href="%s/">%s</a></li>""" % (consultation.id, consultation.name)
"</ul>"
if not one and not l2:
raise errors.AccessForbiddenError()
'<a href="..">%s</a>' % _('Back')
html_foot()
@ -71,21 +76,15 @@ class ConsultationPage(Directory):
def __init__(self, component):
self.component = component
try:
self.consultation = storage.get_storage().retrieve('consultations', component)
except KeyError:
raise errors.TraversalError()
self.consultation = Consultation.get(component)
session = get_session()
user = None
if session:
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
pass
if session and session.user and not str(session.user).startswith(str('anonymous-')):
user = User.get(session.user)
if not user:
raise errors.AccessUnauthorizedError()
if not 'site-admin' in user.roles and not self.consultation.receiver in (user.roles or []):
if not user.is_admin and not self.consultation.receiver in user.roles:
if session.user:
raise errors.AccessForbiddenError()
else:
@ -107,84 +106,79 @@ class ConsultationPage(Directory):
def listing [html] (self):
html_top('%s - %s' % (_('Consultation'), self.consultation.name))
"<h1>%s</h1>" % _('Back Office')
'<h1>%s</h1>' % _('Back Office')
'<h2>%s</h2>' % self.consultation.name
names = 'consultation-' + self.consultation.id
"""<table id="listing" class="sortable"><thead><tr>"""
"<th></th>"
for f in self.consultation.questions:
if f['answer'] in ('title', 'subtitle', 'comment', 'newpage'):
'<table id="listing" class="sortable"><thead><tr>'
'<th></th>'
for f in self.consultation.fields:
if f.type in ('title', 'subtitle', 'comment', 'newpage'):
continue
"<th>%s</th>" % f['question']
"</tr></thead>"
"<tbody>"
for k in storage.get_storage().keys(names):
filled = storage.get_storage().retrieve(names, k)
'<th>%s</th>' % f.label
'</tr></thead>'
'<tbody>'
items = FormData.select(FormData.q.consultationID == self.consultation.id)
for filled in items:
if filled.status != 'done':
continue
"<tr>"
'<tr>'
link = os.path.join(get_request().environ['SCRIPT_NAME'], 'consultations',
self.consultation.id, filled.id, 'status')
str(self.consultation.id), str(filled.id), 'status')
'<td><a href="/%s">&#9758;</a></td>' % link
for f in self.consultation.questions:
if f['answer'] in ('title', 'subtitle', 'comment', 'newpage'):
for f in self.consultation.fields:
if f.type in ('title', 'subtitle', 'comment', 'newpage'):
continue
"<td>%s</td>" % filled.data.get(f['question'], '')
"</tr>\n"
"</tbody></table>"
'<td>%s</td>' % filled.data.get(f.id, '')
'</tr>\n'
'</tbody></table>'
'<a href=".">%s</a>' % _('Back')
html_foot()
def csv [plain] (self):
names = 'consultation-' + self.consultation.id
def fixcsv(s):
if type(s) is not str: return s
if not s: return s
return s.replace('\n', ' ').replace(';', ',')
questions = [x for x in self.consultation.questions if x['answer'] not in (
'title', 'subtitle', 'comment', 'newpage')]
fields = [x for x in self.consultation.fields \
if x.type not in ('title', 'subtitle', 'comment', 'newpage')]
for k in storage.get_storage().keys(names):
filled = storage.get_storage().retrieve(names, k)
if filled.status != 'done':
continue
';'.join([str(fixcsv(filled.data.get(x['question'], ''))) for x in questions]) + '\r\n'
items = FormData.select(FormData.q.consultationID == self.consultation.id)
for filled in items:
';'.join([str(fixcsv(filled.data.get(x.id, ''))) for x in fields]) + '\r\n'
response = get_response()
response.set_content_type('text/csv')
def stats [html] (self):
html_top('%s - %s' % (_('Consultation'), self.consultation.name))
names = 'consultation-' + self.consultation.id
filled_values = [x for x in storage.get_storage().values(names) if x.status == 'done']
filled_values = FormData.select(AND(FormData.q.consultationID == self.consultation.id,
FormData.q.status == str('done')))
'<p>%s %d</p>' % (_('Number of filled consultations:'), len(filled_values))
no_records = filled_values.count()
'<p>%s %d</p>' % (_('Number of filled consultations:'), no_records)
for f in self.consultation.questions:
if f['answer'] == 'title':
'<h3>%s</h3>' % f['question']
for f in self.consultation.fields:
if f.type == 'title':
'<h3>%s</h3>' % f.label
continue
if f['answer'] == 'subtitle':
'<h4>%s</h4>' % f['question']
if f.type == 'subtitle':
'<h4>%s</h4>' % f.label
continue
if f['answer'] in ('comment', 'newpage'):
if f.type in ('comment', 'newpage'):
continue
'<div class="question">'
'<p class="label">'
f['question']
f.label
'</p>'
if f['answer'] == 'item':
options = f['items']
if f.type == 'item':
options = f.extra.get('items', [])
'<table>'
no_records = len(filled_values)
for o in options:
'<tr>'
'<td class="label">'
o
'</td>'
no = len([None for x in filled_values if x.data.get(f['question']) == o])
no = len([None for x in filled_values if x.data.get(f.id) == o])
if no_records:
'<td class="percent">'
' %.2f %%' % (100.*no/no_records)
@ -200,12 +194,12 @@ class ConsultationPage(Directory):
#'NaN'
'</tr>'
'</table>'
elif f['answer'] in ('email', 'string', 'text'):
elif f.type in ('email', 'string', 'text'):
'<div class="all-the-answers">'
for t in filled_values:
if t.data.get(f['question'], None):
if t.data.get(f.id):
'<div>'
t.data.get(f['question'])
t.data.get(f.id)
'<hr /></div>'
'</div>'
else:

View File

@ -1,17 +1,24 @@
import random
import time
from quixote import get_request, get_response, get_session, redirect
from quixote.directory import Directory, AccessControlled
from sqlobject import *
from wcs import errors
from wcs import formdata
from wcs import storage
from wcs import misc
from wcs import users
from wcs import template
from wcs.form import *
from wcs.categories import Category
from wcs.anonylink import AnonymityLink
from wcs.consultationdef import Consultation
from wcs.users import User
from wcs.roles import get_logged_users_role
from wcs.formdata import FormData
from backoffice import BackOfficeDirectory
def html_top [html] (title = None):
@ -20,34 +27,16 @@ def html_top [html] (title = None):
def html_foot [html] ():
template.html_foot()
def get_consultation_key(session, consultation, generate = True):
if session.user:
return session.user
if session.anonymous_id:
return session.anonymous_id
if not generate:
return None
while True:
random_key = 'random-%d' % random.randint(0, 1000000000)
if not storage.get_storage().has_key(
"consultation-" + consultation.id, random_key):
break
session.anonymous_id = random_key
return random_key
def get_summary [html] (consultation, filled):
for f in consultation.questions:
if f['answer'] in ('newpage', 'title', 'subtitle', 'comment'):
for f in consultation.fields:
if f.type in ('newpage', 'title', 'subtitle', 'comment'):
continue
'<div class="question">'
'<p class="label">'
f['question']
f.label
'</p>'
'<p>%s</p>' % filled.data.get(f['question'], '')
'<p>%s</p>' % filled.data.get(f.id, '')
'</div>'
@ -56,21 +45,16 @@ class ConsultationStatusPage(Directory):
def __init__(self, consultation, component):
self.consultation = consultation
try:
self.filled = storage.get_storage().retrieve(
"consultation-" + self.consultation.id, component)
except KeyError:
self.filled = FormData.get(component)
if self.filled.consultation != self.consultation:
raise errors.TraversalError()
def check_receiver(self):
session = get_session()
if not session or not session.user:
raise errors.AccessUnauthorizedError()
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
raise errors.AccessUnauthorizedError()
if not (self.consultation.receiver in user.roles or 'site-admin' in user.roles):
user = User.get(session.user)
if not (user.is_admin or self.formdef.receiver in user.roles):
raise errors.AccessForbiddenError()
def status [html] (self):
@ -91,44 +75,43 @@ class ConsultationPage(Directory):
_q_exports = ["", "submit"]
def __init__(self, component):
self.component = component
try:
self.consultation = storage.get_storage().retrieve('consultations', component)
except KeyError:
self.consultation = Consultation.byUrl_name(component)
except errors.SQLObjectNotFound:
raise errors.TraversalError()
self.page_number = len([
x for x in self.consultation.questions if x['answer'] == 'newpage']) + 1
x for x in self.consultation.fields if x.type == 'newpage']) + 1
session = get_session()
user = None
if session:
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
user = users.User()
if session and session.user and not str(session.user).startswith(str('anonymous-')):
user = User.get(session.user)
if self.consultation.roles:
if not session.user:
raise errors.AccessUnauthorizedError()
if 'logged-users' not in self.consultation.roles and not 'site-admin' in user.roles:
for q in user.roles or []:
if not user.is_admin and not get_logged_users_role() not in formdef.roles:
for q in user.roles:
if q in self.consultation.roles or q == self.consultation.receiver:
break
else:
raise errors.AccessForbiddenError()
consultation_key = get_consultation_key(session, self.consultation)
try:
self.filled = storage.get_storage().retrieve(
"consultation-" + self.consultation.id, consultation_key)
except KeyError:
self.filled = formdata.FormData()
self.filled.names = 'consultation-' + self.component
self.filled.data = {}
self.filled.status = 'started'
self.filled.id = consultation_key
if session and session.user:
self.filled.user_id = session.user
self.filled = self.consultation.get_filled(session)
except SQLObjectNotFound:
self.filled = FormData()
self.filled.consultation = self.consultation
if user:
self.filled.user = user
else:
a = AnonymityLink(formdata = self.filled)
if session.name_identifier:
a.name_identifier = session.name_identifier
else:
a.key = session.get_anonymous_key()
self.filled.status = 'watched'
def _q_index [html] (self):
self.page(0)
@ -173,12 +156,16 @@ class ConsultationPage(Directory):
return self.page(page_no, page_change = False)
page_no = int(page_no) + 1
self.filled.data.update(self.consultation.get_data(form))
data = self.filled.data
data.update(self.consultation.get_data(form))
self.filled.data = data
if self.filled.status == 'watched':
self.filled.status = 'started'
if page_no == self.page_number:
self.filled.receipt_time = time.localtime()
self.filled.status = 'done'
storage.get_storage().store(self.filled)
if page_no == self.page_number:
return self.page_end()
@ -203,43 +190,40 @@ class RootDirectory(Directory):
session = get_session()
user = None
if session and session.user and not session.user.startswith(str('anonymous-')):
if session and session.user and not str(session.user).startswith(str('anonymous-')):
try:
user = storage.get_storage().retrieve('users', session.user)
user = User.get(session.user)
"<p>%s</p>" % _('You are logged in as %s.') % user.name
except KeyError:
user = users.User()
except (KeyError, ValueError):
pass
backoffice_link = False
l = []
for k in storage.get_storage().keys(str('consultations')):
consultation = storage.get_storage().retrieve('consultations', k)
if user and consultation.receiver in (user.roles or []):
for consultation in Consultation.select(orderBy = Consultation.q.name):
if user and consultation.receiver in user.roles:
backoffice_link = True
if consultation.roles:
if not session.user:
continue
if 'logged-users' not in consultation.roles:
for q in user.roles or []:
if get_logged_users_role() not in consultation.roles:
for q in (user and user.roles) or []:
if q in consultation.roles:
break
else:
continue
l.append(consultation)
if user and 'site-admin' in user.roles:
if user and user.is_admin:
backoffice_link = True
l.sort(lambda x,y: cmp(x.name, y.name))
cats = storage.get_storage().values('categories')
cats.sort(lambda x,y: cmp(x.name, y.name))
cats = Category.select(orderBy = Category.q.name)
one = False
for c in cats:
l2 = [x for x in l if x.category == c.id]
l2 = [x for x in l if x.categoryID == c.id]
if l2:
self.consultation_list(l2, title = c.name, session = session)
one = True
l2 = [x for x in l if not x.category]
l2 = [x for x in l if not x.categoryID]
if l2:
if one:
title = _('Misc')
@ -252,7 +236,7 @@ class RootDirectory(Directory):
if backoffice_link:
'<a href="backoffice/">%s</a> ' % _('Back Office')
if not misc.cfg.get('misc', {}).get('do-not-token', False) and \
session.user.startswith(str('anonymous-')):
str(session.user).startswith(str('anonymous-')):
"""<a href="../token">%s</a> - """ % _('Enter Identification Token')
"""<a href="../logout">%s</a></p>""" % _('Logout')
@ -266,16 +250,21 @@ class RootDirectory(Directory):
'<h2>%s</h2>' % title
'<ul>'
for consultation in list:
'<li><a href="%s/">%s</a>' % (consultation.id, consultation.name)
try:
filled = consultation.get_filled(session)
except SQLObjectNotFound:
filled = None
consultation_key = get_consultation_key(session, consultation, generate = False)
if consultation_key and storage.get_storage().has_key(
'consultation-' + consultation.id, consultation_key):
filled = storage.get_storage().retrieve(
"consultation-" + consultation.id, consultation_key)
if filled.status == 'done':
' (%s)' % _('Already completed')
'</li>'
if filled and filled.status == 'done':
'<li>%s (%s, <a href="%s/">%s</a>)</li>' % (consultation.name,
_('already completed'),
consultation.url_name,
_('review'))
elif filled and filled.status == 'started':
'<li><a href="%s/">%s</a> (%s)</li>' % (consultation.url_name,
consultation.name, _('being filled'))
else:
'<li><a href="%s/">%s</a></li>' % (consultation.url_name, consultation.name)
'</ul>'
def _q_lookup(self, component):

View File

@ -1,5 +1,6 @@
import quixote
from quixote.errors import *
from sqlobject import SQLObjectNotFound
import template

View File

@ -1,28 +1,101 @@
import storage
from sqlobject import *
from extracols import DictPickleCol
import time
class Evolution:
who = None
status = None
time = None
comment = None
from anonylink import AnonymityLink
def __init__(self, status, comment, who):
self.status = status
self.comment = comment
self.who = who
self.time = time.localtime()
class FormDataEvolution(SQLObject):
_cacheValues = False
class FormData(storage.Storable):
who = ForeignKey('User', default = None)
status = StringCol(length = 40)
time = DateTimeCol()
comment = StringCol(default = None)
formdata = ForeignKey('FormData')
#def __init__(self, status, comment, who):
# self.time = time.localtime() #XXX: do not forget
class FormData(SQLObject):
key = 'id'
names = 'forms'
user_id = None
receipt_time = None
status = None
evolution = None
_cacheValues = False
def __init__(self):
self.id = None
self.data = None
self.user_id = None
user = ForeignKey('User', default = None)
receipt_time = DateTimeCol(default = None)
status = StringCol(default = None, length = 40)
evolution = MultipleJoin('FormDataEvolution', joinMethodName = 'evolution',
joinColumn = 'formdata_id')
data = DictPickleCol(default = None)
formdef = ForeignKey('FormDef', default = None)
consultation = ForeignKey('Consultation', default = None)
def migrateOldData(cls):
import storage
from formdef import FormDef
from consultationdef import Consultation
from users import User
def migrate(objectdef):
if isinstance(objectdef, FormDef):
formdataname = 'formtmp-%s' % objectdef.id
elif isinstance(objectdef, Consultation):
formdataname = 'consultationtmp-%s' % objectdef.id
formdata_init_method = FormData.__init__
FormData.__init__ = lambda x: x
formdatadicts = [x.__dict__ for x in storage.get_storage().values(formdataname)]
FormData.__init__ = formdata_init_method
fields = objectdef.fields
for formdict in formdatadicts:
formdata = FormData()
if isinstance(objectdef, FormDef):
formdata.formdef = objectdef
elif isinstance(objectdef, Consultation):
formdata.consultation = objectdef
if formdict.get('user_id'):
user_id = formdict.get('user_id')
if user_id == 'ultra-user':
pass # ignore, this shouldn't have happened
elif str(user_id).startswith('anonymous-'):
a = AnonymityLink(formdata = formdata)
a.name_identifier = user_id[10:]
elif str(user_id).startswith('_'): # really old wcs versions
a = AnonymityLink(formdata = formdata)
a.name_identifier = user_id
else:
try:
formdata.user = User.get(user_id)
except ValueError:
a = AnonymityLink(formdata = formdata)
a.key = user_id
formdata.status = formdict.get('status')
formdata.receipt_time = formdict.get('receipt_time')
newdata = {}
for k, v in formdict.get('data', {}).items():
# changed keys from 'field name' to 'field id'
try:
field = [x for x in fields if x.label == k][0]
except IndexError:
continue
newdata[field.id] = v
formdata.data = newdata
for evo in formdict.get('evolution', []):
evo_dict = evo.__dict__
evo = FormDataEvolution(formdata = formdata,
time = evo_dict.get('time'),
status = evo_dict.get('status'))
if evo_dict.get('who'):
evo.who = User.get(evo_dict.get('who'))
evo.comment = evo_dict.get('comment')
for objectdef in FormDef.select():
migrate(objectdef)
for objectdef in Consultation.select():
migrate(objectdef)
migrateOldData = classmethod(migrateOldData)
class Evolution: # for backward compatibility with old pickled files
pass

View File

@ -1,10 +1,13 @@
import emails
from sqlobject import *
from extracols import DictPickleCol
from quixote import get_request, get_session
import storage
import emails
from form import *
from misc import simplify
from formdata import FormData
widget_classes = {
'string': StringWidget,
@ -28,103 +31,112 @@ status_labels = {
'finished': N_('Finished')
}
class FormField(SQLObject):
label = StringCol(length = 300)
type = StringCol(length = 20)
required = BoolCol(default = True)
extra = DictPickleCol(default = None)
formdef = ForeignKey('FormDef')
position = IntCol(default = None)
class FormDef(storage.Storable):
class FormDef(SQLObject):
key = 'id'
names = 'formdefs'
receiver = None
roles = None
category = None
discussion = False
confirmation = True
public = False
detailed_emails = False
def __init__(self):
self.id = ''
self.name = ''
self.fields = []
self.receiver = ''
self.category = ''
self.roles = []
name = StringCol(length = 200)
url_name = StringCol(default = None, length = 200, alternateID = True)
fields = MultipleJoin('FormField', joinMethodName = 'fields', joinColumn = 'formdef_id',
orderBy = FormField.q.position)
receiver = ForeignKey('Role', default = None)
category = ForeignKey('Category', default = None)
roles = RelatedJoin('Role')
discussion = BoolCol(default = False)
confirmation = BoolCol(default = True)
public = BoolCol(default = False)
detailed_emails = BoolCol(default = False)
def _set_name(self, value):
self._SO_set_name(value)
if not self.url_name or FormData.select(FormData.q.formdefID == self.id).count() == 0:
self.url_name = simplify(value)
def create_form(self): # XXX: merge create_form and create_view_form
form = Form(enctype = "multipart/form-data", use_tokens = not self.confirmation)
for i, field in enumerate(self.fields):
if field['type'] == 'title':
form.widgets.append(HtmlWidget(htmltext("<h3>%s</h3>" % field['name'])))
for field in self.fields:
if field.type == 'title':
form.widgets.append(HtmlWidget(htmltext('<h3>%s</h3>' % field.label)))
continue
if field['type'] == 'subtitle':
form.widgets.append(HtmlWidget(htmltext("<h4>%s</h4>" % field['name'])))
if field.type == 'subtitle':
form.widgets.append(HtmlWidget(htmltext('<h4>%s</h4>' % field.label)))
continue
if field['type'] == 'comment':
form.widgets.append(HtmlWidget(htmltext("<p>%s</p>" % field['name'])))
if field.type == 'comment':
form.widgets.append(HtmlWidget(htmltext('<p>%s</p>' % field.label)))
continue
widget_class = widget_classes[field['type']]
widget_class = widget_classes[field.type]
kwargs = {'required': field.get('required', True)}
if field['type'] == 'item':
kwargs['options'] = field.get('items', ['---'])
if field.get('show-as-radio'):
kwargs = {'required': field.required}
if field.type == 'item':
kwargs['options'] = field.extra.get('items', ['---'])
if field.extra.get('show-as-radio'):
widget_class = RadiobuttonsWidget
if widget_extra_attributes.has_key(field['type']):
for k in widget_extra_attributes[field['type']]:
v = field.get(k, None)
if v is not None:
if widget_extra_attributes.has_key(field.type):
for k in widget_extra_attributes[field.type]:
v = field.extra.get(k)
if v:
kwargs[k] = v
form.add(widget_class, "f%d" % i, title = field['name'], **kwargs)
form.add(widget_class, 'f%d' % field.id, title = field.label, **kwargs)
return form
def create_view_form(self, dict = {}):
form = Form(enctype = "multipart/form-data")
session = get_session()
for i, field in enumerate(self.fields):
for field in self.fields:
kwargs = {}
if field['type'] == 'title':
form.widgets.append(HtmlWidget(htmltext("<h3>%s</h3>" % field['name'])))
if field.type == 'title':
form.widgets.append(HtmlWidget(htmltext('<h3>%s</h3>' % field.label)))
continue
if field['type'] == 'subtitle':
form.widgets.append(HtmlWidget(htmltext("<h4>%s</h4>" % field['name'])))
if field.type == 'subtitle':
form.widgets.append(HtmlWidget(htmltext('<h4>%s</h4>' % field.label)))
continue
if field['type'] == 'comment':
form.widgets.append(HtmlWidget(htmltext("<p>%s</p>" % field['name'])))
if field.type == 'comment':
form.widgets.append(HtmlWidget(htmltext('<p>%s</p>' % field.label)))
continue
widget_class = widget_classes[field['type']]
value = dict.get(field['name'], '')
if field['type'] == 'item':
widget_class = widget_classes[field.type]
value = dict.get(field.label, '')
if field.type == 'item':
widget_class = StringWidget
if field['type'] == 'file':
if field.type == 'file':
kwargs['preview'] = True
if field['type'] == 'bool':
if field.type == 'bool':
kwargs['disabled'] = 'disabled'
if widget_extra_attributes.has_key(field['type']):
for k in widget_extra_attributes[field['type']]:
v = field.get(k, None)
if v is not None:
if widget_extra_attributes.has_key(field.type):
for k in widget_extra_attributes[field.type]:
v = field.extra.get(k)
if v:
kwargs[k] = v
if field['type'] == 'file':
if field.type == 'file':
widget_class = FakeFileWidget
form.add(widget_class, "f%d" % i, title = field['name'],
readonly = "readonly", value = value, **kwargs)
form.add(widget_class, 'f%d' % field.id, title = field.label,
value = value, readonly = 'readonly', **kwargs)
return form
def get_data(self, form):
d = {}
for i, field in enumerate(self.fields):
if field['type'] in ('title', 'subtitle', 'comment'):
continue
d[field['name']] = form.get_widget('f%d' % i).parse()
for field in self.fields:
widget = form.get_widget('f%s' % field.id)
if widget:
d[field.id] = widget.parse()
return d
def notify_new(self, formdata):
receiver = storage.get_storage().retrieve('roles', self.receiver)
if not receiver.emails:
if not self.receiver.emails:
return
url = '%s/%s/status' % (get_request().get_url(1), formdata.id)
mail_body = _("""Hi,
@ -134,50 +146,42 @@ link: %(url)s
""") % {'url': url}
if self.detailed_emails:
details = []
submitter = None
try:
submitter = storage.get_storage().retrieve('users', formdata.user_id)
except KeyError:
pass
if submitter:
if formdata.user:
details.append(_('User name'))
details.append(' %s' % submitter.name)
for i, f in enumerate(self.fields):
if not formdata.data.has_key(f['name']):
details.append(' %s' % formdata.user.name)
fields = self.fields
for field in self.fields:
data = formdata.data
if not data.has_key(field.id):
continue
details.append(f['name'])
if f['type'] == 'bool':
if formdata.data[f['name']]:
details.append(field.label)
if field.type == 'bool':
if data[field.id]:
details.append(' %s' % _('Yes'))
else:
details.append(' %s' % _('No'))
elif f['type'] == 'file':
file_url = url.replace('status', 'download?f=%d' % i)
elif field.type == 'file':
file_url = url.replace('status', 'download?f=%d' % field.id)
details.append(' %s' % file_url)
#details.append(' %s' % formdata.data[f['name']])
elif f['type'] == 'text':
elif field.type == 'text':
# XXX: howto support preformatted text in a dl in docutils ?
details.append(formdata.data[f['name']].replace('\n', '\n '))
details.append(data[field.id].replace('\n', '\n '))
else:
details.append(' %s' % formdata.data[f['name']])
details.append(' %s' % data[field.id])
mail_body += '\n\n' + '\n'.join(details)
emails.email(_("New form (%s)") % self.name, mail_body, receiver.emails[0],
bcc = receiver.emails[1:])
emails.email(_("New form (%s)") % self.name, mail_body,
self.receiver.emails[0].email,
bcc = [x.email for x in self.receiver.emails[1:]])
def notify_change_user(self, formdata, old_status):
if not formdata.user_id:
if not formdata.user:
return
try:
user = storage.get_storage().retrieve('users', formdata.user_id)
except KeyError:
return
if not user.email:
if not formdata.user.email:
return
receiver = storage.get_storage().retrieve('roles', self.receiver)
if receiver.emails:
from_email = receiver.emails[0]
if self.receiver.emails:
from_email = self.receiver.emails[0].email
else:
from_email = None
@ -203,7 +207,7 @@ consult it with this link:
mail_body += self.get_detailed_evolution(formdata)
emails.email(_('Form status change'), mail_body, user.email,
emails.email(_('Form status change'), mail_body, formdata.user.email,
replyto = from_email)
def get_detailed_evolution(self, formdata):
@ -213,13 +217,8 @@ consult it with this link:
details = []
evo = formdata.evolution[-1]
if evo.who:
try:
user = storage.get_storage().retrieve('users', evo.who)
except KeyError:
pass
else:
details.append(_('User name'))
details.append(' %s' % user.name)
details.append(_('User name'))
details.append(' %s' % evo.who.name)
if evo.status:
details.append(_('Status'))
details.append(' %s' % _(status_labels[evo.status]))
@ -229,8 +228,7 @@ consult it with this link:
def notify_change_receiver(self, formdata):
receiver = storage.get_storage().retrieve('roles', self.receiver)
if not receiver.emails:
if not self.receiver.emails:
return
url = '%s/status' % get_request().get_url(1)
@ -242,5 +240,57 @@ A form just changed, you can consult it with this link:
""") % {'url': url}
mail_body += self.get_detailed_evolution(formdata)
emails.email(_('Form status change (%s)') % self.name, mail_body, receiver.emails)
emails.email(_('Form status change (%s)') % self.name, mail_body,
[x.email for x in self.receiver.emails])
def migrateOldData(cls):
import storage
from roles import Role, get_logged_users_role
from categories import Category
init_method = cls.__init__
cls.__init__ = lambda x: x
formdefs = storage.get_storage().values('formdefs')
items = [x.__dict__ for x in formdefs]
cls.__init__ = init_method
items.sort(lambda x,y: cmp(x['id'], y['id']))
logged_users = get_logged_users_role()
for item in items:
formdef = FormDef(name = item['name'])
formdef.url_name = item['id']
try:
formdef.receiver = Role.get(item['receiver'])
except SQLObjectNotFound:
formdef.receiver = None
if item.get('category'):
formdef.category = Category.get(item['category'])
for role in item.get('roles', []):
if role == 'logged-users':
formdef.addRole(logged_users)
continue
formdef.addRole(Role.get(role))
formdef.discussion = item.get('discussion', False)
formdef.confirmation = item.get('confirmation', True)
formdef.public = item.get('public', False)
formdef.detailed_emails = item.get('detailed_emails', False)
for i, field in enumerate(item['fields']):
fieldObject = FormField(label = field['name'], type = field['type'],
formdef = formdef)
fieldObject.required = field.get('required', False)
fieldObject.position = i
d = field.copy()
del d['name']
del d['type']
if d.has_key('required'):
del d['required']
if d:
fieldObject.extra = d
# convert submitted forms
name = 'form-%s' % item['id']
newname = 'formtmp-%s' % formdef.id
storage.get_storage().rename_table(name, newname)
migrateOldData = classmethod(migrateOldData)

View File

@ -5,9 +5,12 @@ from quixote.directory import Directory
from wcs import errors
from wcs import misc
from wcs import storage
from wcs import template
from wcs.form import *
from wcs.categories import Category
from wcs.formdata import FormData
from wcs.formdef import FormDef
from wcs.users import User
from root import status_labels
@ -29,29 +32,24 @@ class BackOfficeDirectory(Directory):
_q_exports = [""]
def _q_index [html] (self):
html_top()
"<h1>%s</h1>" % _('Back Office')
html_top(_('Back Office'))
'<h1>%s</h1>' % _('Forms')
session = get_session()
user = None
if session and session.user and not session.user.startswith(str('anonymous-')):
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
pass
if session and session.user and not str(session.user).startswith(str('anonymous-')):
user = User.get(session.user)
l = []
for k in storage.get_storage().keys(str('formdefs')):
formdef = storage.get_storage().retrieve('formdefs', k)
if user and (formdef.receiver in (user.roles or []) or 'site-admin' in user.roles):
l.append(formdef)
l.sort(lambda x,y: cmp(x.name, y.name))
if user:
for formdef in FormDef.select(orderBy = FormDef.q.name):
if user.is_admin or formdef.receiver in user.roles:
l.append(formdef)
cats = storage.get_storage().values('categories')
cats.sort(lambda x,y: cmp(x.name, y.name))
cats = Category.select(orderBy = Category.q.name)
one = False
for c in cats:
l2 = [x for x in l if x.category == c.id]
l2 = [x for x in l if x.categoryID == c.id]
if l2:
"<h2>%s</h2>" % c.name
"<ul>"
@ -84,19 +82,17 @@ class FormDefUI:
self.formdef = formdef
def listing [html] (self, url_action = None):
names = 'form-' + self.formdef.id
"""<table id="listing" class="sortable"><thead><tr>"""
"<th></th>"
'<table id="listing" class="sortable"><thead><tr>'
'<th></th>'
for f in self.formdef.fields:
if f['type'] in ('title', 'subtitle', 'comment'):
if f.type in ('title', 'subtitle', 'comment'):
continue
"<th>%s</th>" % f['name']
"<th>%s</th>" % _('Status')
"</tr></thead>"
"<tbody>"
items = storage.get_storage().values(names)
items.sort(lambda x,y: cmp(x.receipt_time, y.receipt_time))
items.reverse()
'<th>%s</th>' % f.label
'<th>%s</th>' % _('Status')
'</tr></thead>'
'<tbody>'
items = FormData.select(FormData.q.formdefID == self.formdef.id,
orderBy = str('-receipt_time'))
if url_action:
url_action = '/' + url_action
else:
@ -104,24 +100,24 @@ class FormDefUI:
for filled in items:
'<tr class="status-%s">' % filled.status
link = os.path.join(get_request().environ['SCRIPT_NAME'], 'forms',
self.formdef.id, filled.id)
self.formdef.url_name, str(filled.id))
'<td><a href="/%s%s">&#9758;</a></td>' % (link, url_action)
for i, f in enumerate(self.formdef.fields):
if f['type'] in ('title', 'subtitle', 'comment'):
if f.type in ('title', 'subtitle', 'comment'):
continue
if f['type'] == 'bool':
if f.type == 'bool':
'<td>'
if filled.data[f['name']]:
if filled.data[f.id]:
_('Yes')
else:
_('No')
'</td>'
elif f['type'] == 'text':
"<td>%s</td>" % ellipsize(filled.data.get(f['name'], '') or '')
elif f['type'] == 'file':
'<td><a href="/%s/download?f=%d">%s</a></td>' % (link, i, filled.data[f['name']])
elif f.type == 'text':
"<td>%s</td>" % ellipsize(filled.data.get(f.id, '') or '')
elif f.type == 'file':
'<td><a href="/%s/download?f=%d">%s</a></td>' % (link, f.id, filled.data[f.id])
else:
"<td>%s</td>" % filled.data.get(f['name'], '')
"<td>%s</td>" % filled.data.get(f.id, '')
"<td>%s</td>" % _(status_labels[filled.status])
"</tr>\n"
"</tbody></table>"
@ -132,22 +128,18 @@ class FormPage(Directory):
_q_exports = ["", "listing", "csv"]
def __init__(self, component):
self.component = component
try:
self.formdef = storage.get_storage().retrieve('formdefs', component)
except KeyError:
self.formdef = FormDef.get(component)
except errors.SQLObjectNotFound:
raise errors.TraversalError()
session = get_session()
user = None
if session:
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
pass
user = User.get(session.user)
if not user:
raise errors.AccessUnauthorizedError()
if not 'site-admin' in user.roles and not self.formdef.receiver in (user.roles or []):
if not user.is_admin and not self.formdef.receiver in user.roles:
if session.user:
raise errors.AccessForbiddenError()
else:
@ -174,18 +166,16 @@ class FormPage(Directory):
html_foot()
def csv [plain] (self):
names = 'form-' + self.formdef.id
def fixcsv(s):
if type(s) is not str: return s
if not s: return s
return s.replace('\n', ' ').replace(';', ',')
fields = [x for x in self.formdef.fields if x['type'] not in ('title', 'subtitle', 'file')]
fields = [x for x in self.formdef.fields if x.type not in ('title', 'subtitle', 'file')]
for k in storage.get_storage().keys(names):
filled = storage.get_storage().retrieve(names, k)
';'.join([str(fixcsv(filled.data.get(x['name'], ''))) for x in fields]) + '\r\n'
items = FormData.select(FormData.q.formdefID == self.formdef.id)
for filled in items:
';'.join([str(fixcsv(filled.data.get(x.id, ''))) for x in fields]) + '\r\n'
response = get_response()
response.set_content_type('text/csv')

View File

@ -4,15 +4,21 @@ import time
from quixote import get_request, get_response, get_session, redirect
from quixote.directory import Directory, AccessControlled
from sqlobject import *
from wcs import errors
from wcs import formdata
from wcs import storage
from wcs import misc
from wcs import users
from wcs import template
from wcs.form import *
from wcs.formdef import status_labels
from wcs.anonylink import AnonymityLink
from wcs.categories import Category
from wcs.formdef import FormField, FormDef, status_labels
from wcs.formdata import FormData
from wcs.users import User
from wcs.roles import get_logged_users_role
from backoffice import BackOfficeDirectory, FormDefUI
@ -30,40 +36,48 @@ class FormStatusPage(Directory):
def __init__(self, formdef, component):
self.formdef = formdef
try:
self.filled = storage.get_storage().retrieve("form-" + formdef.id, component)
except KeyError:
self.filled = FormData.get(component)
except errors.SQLObjectNotFound:
raise errors.TraversalError()
if self.filled.formdef != self.formdef:
raise errors.TraversalError()
def _q_index [html] (self):
session = get_session()
if not self.formdef.public and (not session or self.filled.user_id != session.user):
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
mine = False
if self.filled.userID != session.user:
mine = True
elif str(session.user).startswith(str('anonymous-')):
anonylink = AnonymityLink.select(AND(
AnonymityLink.q.name_identifier == session.name_identifier,
AnonymityLink.q.formdataID == self.filled.id))
if anonylink.count() == 0:
mine = True
if not self.formdef.public and not mine:
user = User.get(session.user)
if not user.is_admin:
raise errors.AccessError()
if not 'site-admin' in user.roles:
raise errors.AccessError()
html_top(self.formdef.name + ' - ' + self.filled.id)
html_top('%s - %s' % (self.formdef.name, self.filled.id))
if self.filled.receipt_time is not None:
tm = time.strftime(str("%Y-%m-%d %H:%M"), self.filled.receipt_time)
tm = self.filled.receipt_time.strftime(str("%Y-%m-%d %H:%M"))
else:
tm = '???'
"<p>"
_('The form has been recorded on %s with the number %s.') % (tm, self.filled.id)
"</p>"
if self.formdef.receiver and session.user and self.filled.user_id == session.user:
try:
receiver = storage.get_storage().retrieve('roles', self.formdef.receiver)
details = receiver.details
except KeyError:
details = self.formdef.receiver # was done like that before
if details:
"<p>"
_('Your case is handled by:')
"</p>"
if self.formdef.receiver and mine:
if self.formdef.receiver.details:
'<p>'
if self.filled.status != 'finished':
_('Your case is handled by:')
else:
_('Your case has been handled by:')
'</p>'
'<p id="receiver">'
htmltext(details.replace(str('\n'), str('<br />')))
"</p>"
htmltext(self.formdef.receiver.details.replace(str('\n'), str('<br />')))
'</p>'
self.receipt()
@ -94,14 +108,9 @@ class FormStatusPage(Directory):
'<h2>%s</h2>' % _('Log')
'<dl id="evolutions">'
for evo in self.filled.evolution:
"<dt>%s" % time.strftime(str("%Y-%m-%d %H:%M"), evo.time)
"<dt>%s" % evo.time.strftime(str("%Y-%m-%d %H:%M"))
if evo.who:
try:
user = storage.get_storage().retrieve('users', evo.who)
except KeyError:
pass
else:
' <span class="user">%s</span>' % user.name
' <span class="user">%s</span>' % evo.who.name
'</dt>'
'<dd>'
if evo.status:
@ -118,47 +127,41 @@ class FormStatusPage(Directory):
session = get_session()
if not session or not session.user:
raise errors.AccessUnauthorizedError()
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
raise errors.AccessUnauthorizedError()
if not (self.formdef.receiver in user.roles or 'site-admin' in user.roles):
user = User.get(session.user)
if not (user.is_admin or self.formdef.receiver in user.roles):
raise errors.AccessForbiddenError()
def receipt [html] (self, always_include_user = False, show_status = True, form_url = ''):
user = None
if always_include_user or self.filled.user_id != get_session().user:
try:
user = storage.get_storage().retrieve('users', self.filled.user_id)
except KeyError:
pass
if always_include_user or self.filled.userID != get_session().user:
user = self.filled.user
'<dl id="receipt">'
if user:
'<dt>%s</dt>' % _('User name')
'<dd>%s</dd>' % user.name
for i, f in enumerate(self.formdef.fields):
if not self.filled.data.has_key(f['name']):
for f in self.formdef.fields:
if not self.filled.data.has_key(f.id):
continue
'<dt>%s</dt>' % f['name']
'<dt>%s</dt>' % f.label
'<dd>'
if f['type'] == 'bool':
if self.filled.data[f['name']]:
if f.type == 'bool':
if self.filled.data[f.id]:
_('Yes')
else:
_('No')
elif f['type'] == 'file':
'<a href="%sdownload?f=%d">%s</a>' % (form_url, i, self.filled.data[f['name']])
elif f['type'] == 'text':
if f.get('pre', False):
elif f.type == 'file':
'<a href="%sdownload?f=%d">%s</a>' % (form_url, f.id, self.filled.data[f.id])
elif f.type == 'text':
if f.extra.get('pre', False):
'<pre>'
self.filled.data[f['name']]
if f.get('pre', False):
self.filled.data[f.id]
if f.extra.get('pre', False):
'</pre>'
else:
self.filled.data[f['name']]
self.filled.data[f.id]
'</dd>'
if show_status:
@ -191,9 +194,9 @@ class FormStatusPage(Directory):
response.headers[str('location')] = get_request().get_url()
response.content_type = 'text/plain'
return "Your browser should redirect you"
html_top(self.formdef.name + ' - ' + self.filled.id)
html_top('%s - %s' % (self.formdef.name, self.filled.id))
if self.filled.receipt_time is not None:
tm = time.strftime(str("%Y-%m-%d %H:%M"), self.filled.receipt_time)
tm = self.filled.receipt_time.strftime(str("%Y-%m-%d %H:%M"))
else:
tm = '???'
"<p>"
@ -232,15 +235,14 @@ class FormStatusPage(Directory):
who = None
session = get_session()
if session.user and not session.user.startswith('anonymous-'):
if session.user and not str(session.user).startswith('anonymous-'):
who = session.user
evo = formdata.Evolution(status, comment, who)
if not self.filled.evolution:
self.filled.evolution = []
self.filled.evolution.append(evo)
evo = formdata.FormDataEvolution(status = status, time = time.localtime(),
formdata = self.filled)
evo.who = who
evo.comment = comment
if status:
self.filled.status = status
storage.get_storage().store(self.filled)
if comment_only:
self.formdef.notify_change_receiver(self.filled)
@ -248,18 +250,19 @@ class FormStatusPage(Directory):
self.formdef.notify_change_user(self.filled, old_status)
def download(self):
if not self.formdef.public and not self.filled.user_id == get_session().user:
if not self.formdef.public and not self.filled.userID == get_session().user:
self.check_receiver()
try:
fn = get_request().form['f']
f = self.formdef.fields[int(fn)]
f = FormField.get(fn)
except (KeyError, ValueError):
raise
raise TraversalError()
if f['type'] != 'file':
if f.type != 'file':
raise TraversalError()
file = self.filled.data[f['name']]
file = self.filled.data[int(fn)]
response = get_response()
if file.content_type:
response.set_content_type(file.content_type)
@ -278,24 +281,23 @@ class FormPage(Directory):
_q_exports = ["", "submit", 'listing']
def __init__(self, component):
self.component = component
try:
self.formdef = storage.get_storage().retrieve('formdefs', component)
except KeyError:
self.formdef = FormDef.byUrl_name(component)
except errors.SQLObjectNotFound:
raise errors.TraversalError()
session = get_session()
user = None
if session:
if session and session.user:
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
user = users.User()
user = User.get(session.user)
except (KeyError, ValueError):
pass
if self.formdef.roles:
if not session.user:
raise errors.AccessUnauthorizedError()
if 'logged-users' not in self.formdef.roles and not 'site-admin' in user.roles:
for q in user.roles or []:
if get_logged_users_role() not in self.formdef.roles and not (user and user.is_admin):
for q in (user and user.roles) or []:
if q in self.formdef.roles or q == self.formdef.receiver:
break
else:
@ -350,15 +352,17 @@ class FormPage(Directory):
if step == '0' and self.formdef.confirmation:
return self.validating()
else:
filled = formdata.FormData()
filled.names = 'form-' + self.component
filled = formdata.FormData(formdef = self.formdef)
filled.data = self.formdef.get_data(form)
filled.receipt_time = time.localtime()
filled.status = 'new'
session = get_session()
if session and session.user:
filled.user_id = session.user
storage.get_storage().store(filled)
if session and session.user and not str(session.user).startswith(str('anonymous-')):
filled.user = User.get(session.user)
else:
a = AnonymityLink(formdata = filled)
if session.name_identifier:
a.name_identifier = session.name_identifier
self.formdef.notify_new(filled)
return self.receipt_page(filled)
@ -383,34 +387,31 @@ class FormPage(Directory):
else:
self.step(1)
tm = time.strftime(str("%Y-%m-%d %H:%M"), filled.receipt_time)
tm = filled.receipt_time.strftime(str("%Y-%m-%d %H:%M"))
"<p>"
_('The form has been recorded on %s with the number %s.') % (tm, filled.id)
"</p>"
if self.formdef.receiver:
try:
receiver = storage.get_storage().retrieve('roles', self.formdef.receiver)
details = receiver.details
except KeyError:
details = self.formdef.receiver # was done like that before
if details:
if self.formdef.receiver.details:
"<p>"
_('Your case will be handled by:')
"</p>"
'<p id="receiver">'
htmltext(details.replace(str('\n'), str('<br />')))
htmltext(self.formdef.receiver.details.replace(str('\n'), str('<br />')))
"</p>"
form_status = FormStatusPage(self.formdef, filled.id)
form_status.receipt(show_status = False,
form_url = os.path.join(get_request().get_url(1), filled.id, ''))
form_url = os.path.join(get_request().get_url(1), str(filled.id), ''))
'<a href="..">%s</a>' % _("Back Home")
html_foot()
def listing [html] (self):
html_top('%s - %s' % (_('Listing'), self.formdef.name))
if 'site-admin' in self.user.roles or self.formdef.receiver in (self.user.roles or []):
if self.user and (
'site-admin' in self.user.roles or
self.formdef.receiver in (self.user.roles or [])):
FormDefUI(self.formdef).listing('status')
else:
FormDefUI(self.formdef).listing()
@ -437,43 +438,40 @@ class RootDirectory(AccessControlled, Directory):
session = get_session()
user = None
if session and session.user and not session.user.startswith(str('anonymous-')):
if session and session.user and not str(session.user).startswith(str('anonymous-')):
try:
user = storage.get_storage().retrieve('users', session.user)
user = User.get(session.user)
"<p>%s</p>" % _('You are logged in as %s.') % user.name
except KeyError:
user = users.User()
except (KeyError, ValueError):
pass
backoffice_link = False
l = []
for k in storage.get_storage().keys(str('formdefs')):
formdef = storage.get_storage().retrieve('formdefs', k)
if user and formdef.receiver in (user.roles or []):
for formdef in FormDef.select(orderBy = FormDef.q.name):
if user and formdef.receiver in user.roles:
backoffice_link = True
if formdef.roles:
if not session.user:
continue
if 'logged-users' not in formdef.roles:
for q in user.roles or []:
if get_logged_users_role() not in formdef.roles:
for q in (user and user.roles) or []:
if q in formdef.roles:
break
else:
continue
l.append(formdef)
if user and 'site-admin' in user.roles:
if user and user.is_admin:
backoffice_link = True
l.sort(lambda x,y: cmp(x.name, y.name))
cats = storage.get_storage().values('categories')
cats.sort(lambda x,y: cmp(x.name, y.name))
cats = Category.select(orderBy = Category.q.name)
one = False
for c in cats:
l2 = [x for x in l if x.category == c.id]
l2 = [x for x in l if x.categoryID == c.id]
if l2:
self.form_list(l2, title = c.name, session = session)
one = True
l2 = [x for x in l if not x.category]
l2 = [x for x in l if not x.categoryID]
if l2:
if one:
title = _('Misc')
@ -483,34 +481,32 @@ class RootDirectory(AccessControlled, Directory):
if session and session.user:
l = []
for k in storage.get_storage().keys('formdefs'):
formdef = storage.get_storage().retrieve('formdefs', k)
formnames = "form-" + formdef.id
for q in storage.get_storage().keys(formnames):
filled = storage.get_storage().retrieve(formnames, q)
filled.formdef = formdef
if filled.user_id == session.user:
l.append(filled)
l.sort(lambda x,y: -cmp(x.receipt_time, y.receipt_time))
current = [x for x in l if x.status in ('new', 'accepted')]
if str(session.user).startswith(str('anonymous-')):
anonylinks = AnonymityLink.select(
AnonymityLink.q.name_identifier == session.name_identifier)
user_forms = [x.formdata for x in anonylinks if x.formdata]
else:
user_forms = FormData.select(FormData.q.userID == session.user,
orderBy = FormData.q.receipt_time)
current = [x for x in user_forms if x.status in ('new', 'accepted')]
if current:
'<h2 id="submitted">%s</h2>' % _('Your Current Forms')
'<ul>'
for f in current:
'<li><a href="%s/%s">%s</a>, %s, %s: %s</li>' % (
f.formdef.id, f.id, f.formdef.name,
time.strftime(str("%Y-%m-%d %H:%M"), f.receipt_time),
f.formdef.url_name, f.id, f.formdef.name,
f.receipt_time.strftime(str("%Y-%m-%d %H:%M")),
_('status'),
_(status_labels[f.status]) )
'</ul>'
done = [x for x in l if x.status in ('rejected', 'finished')]
done = [x for x in user_forms if x.status in ('rejected', 'finished')]
if done:
'<h2 id="done">%s</h2>' % _('Your Old Forms')
'<ul>'
for f in done:
'<li><a href="%s/%s">%s</a>, %s, %s: %s</li>' % (
f.formdef.id, f.id, f.formdef.name,
time.strftime(str("%Y-%m-%d %H:%M"), f.receipt_time),
f.formdef.url_name, f.id, f.formdef.name,
f.receipt_time.strftime(str("%Y-%m-%d %H:%M")),
_('status'),
_(status_labels[f.status]) )
'</ul>'
@ -519,7 +515,7 @@ class RootDirectory(AccessControlled, Directory):
if backoffice_link:
'<a href="backoffice/">%s</a> ' % _('Back Office')
if not misc.cfg.get('misc', {}).get('do-not-token', False) and \
session.user.startswith(str('anonymous-')):
str(session.user).startswith(str('anonymous-')):
"""<a href="../token">%s</a> - """ % _('Enter Identification Token')
"""<a href="../logout">%s</a></p>""" % _('Logout')
@ -533,9 +529,9 @@ class RootDirectory(AccessControlled, Directory):
'<h2>%s</h2>' % title
'<ul>'
for formdef in list:
'<li><a href="%s/">%s</a>' % (formdef.id, formdef.name)
'<li><a href="%s/">%s</a>' % (formdef.url_name, formdef.name)
if formdef.public:
' <a class="listing" href="%s/listing">%s</a>' % (formdef.id, _('(listing)'))
' <a class="listing" href="%s/listing">%s</a>' % (formdef.url_name, _('(listing)'))
'</li>'
'</ul>'

View File

@ -13,7 +13,7 @@ from wcs import misc
from wcs import storage
from wcs.form import *
from wcs.template import *
from wcs.users import User
from wcs.users import User, NameIdentifier
class RootDirectory(Directory):
@ -83,7 +83,11 @@ class RootDirectory(Directory):
login.processAuthnResponseMsg(get_field('LARES'))
login.acceptSso()
session = get_session()
session.lasso_session_dump = login.session.dump()
if login.isSessionDirty:
if login.session:
session.lasso_session_dump = login.session.dump()
else:
session.lasso_session_dump = None
user = self.lookup_user(session, login)
if user:
session.set_user(user.id)
@ -104,11 +108,11 @@ class RootDirectory(Directory):
def lookup_user(self, session, login):
ni = login.nameIdentifier.content
session.name_identifier = ni
for user in storage.get_storage().values('users'):
if ni in user.name_identifiers:
break
nis = NameIdentifier.select(NameIdentifier.q.name_identifier == ni)
if nis.count():
user = nis[0].user
else:
if lasso.WSF_SUPPORT and misc.cfg.get('misc', {}).get('grab-user-with-wsf', False):
if False and lasso.WSF_SUPPORT and misc.cfg.get('misc', {}).get('grab-user-with-wsf', False):
disco = lasso.Discovery(login.server)
disco.setSessionFromDump(session.lasso_session_dump)
disco.initQuery()
@ -150,18 +154,15 @@ class RootDirectory(Directory):
if email and name:
user = User()
user.id = email
user.email = email
user.name = name
user.name_identifiers = [login.nameIdentifier.content]
NameIdentifier(name_identifier = login.nameIdentifier.content, user = user)
user.lasso_dump = login.identity.dump()
storage.get_storage().store(user)
return user
return None
user.lasso_dump = login.identity.dump()
storage.get_storage().store(user)
return user
def singleLogout(self):
@ -196,8 +197,8 @@ class RootDirectory(Directory):
if session.lasso_session_dump:
logout.setSessionFromDump(session.lasso_session_dump)
if session.user:
if not session.user.startswith('anonymous-'):
user = storage.get_storage().retrieve('users', session.user)
if not str(session.user).startswith('anonymous-'):
user = User.Get(session.user)
if user.lasso_dump:
logout.setIdentityFromDump(user.lasso_dump)
elif session.lasso_anonymous_identity_dump:
@ -228,9 +229,9 @@ class RootDirectory(Directory):
if session.lasso_session_dump:
logout.setSessionFromDump(session.lasso_session_dump)
if session.user:
if not session.user.startswith('anonymous-'):
if not str(session.user).startswith('anonymous-'):
try:
user = storage.get_storage().retrieve('users', session.user)
user = User.get(session.user)
except KeyError:
get_session_manager().expire_session()
return redirect('/')
@ -306,7 +307,7 @@ class RootDirectory(Directory):
def fedterm(self, defederation, session):
defederation.setSessionFromDump(session.lasso_session_dump)
try:
user = storage.get_storage().retrieve('users', session.user)
user = User.get(session.user)
except KeyError:
pass
else:
@ -323,7 +324,6 @@ class RootDirectory(Directory):
user.lasso_dump = None
else:
user.lasso_dump = defederation.identity.dump()
storage.get_storage().store(user)
if defederation.isSessionDirty:
if defederation.session:

View File

@ -1,16 +1,104 @@
import storage
from sqlobject import *
class Role(storage.Storable):
def get_logged_users_role():
return Role.select(AND(Role.q.name == 'Logged Users', Role.q.system == True))[0]
class RoleEmail(SQLObject):
email = StringCol(length = 100)
role = ForeignKey('Role')
_cacheValues = False
class Role(SQLObject):
key = 'id'
names = 'roles'
_cacheValues = False
id = None
name = None
details = None
emails = None
name = StringCol(length = 100)
details = StringCol(default = None)
emails = MultipleJoin('RoleEmail', joinMethodName = 'emails')
system = BoolCol(default = False)
def __init__(self):
self.id = ''
self.name = ''
self.details = ''
users = RelatedJoin('User')
formdefs = RelatedJoin('FormDef')
consultations = RelatedJoin('Consultation')
def initTable(cls):
role = Role(name = N_('Logged Users'))
role.details = 'System Group for Logged Users'
role.system = True
initTable = classmethod(initTable)
def migrateOldData(cls):
import storage
from users import User
from formdef import FormDef
from consultationdef import Consultation
init_method = cls.__init__
cls.__init__ = lambda x: x
roles = storage.get_storage().values('roles')
items = [x.__dict__ for x in roles]
cls.__init__ = init_method
new_ids_mapping = [('site-admin', 'site-admin')]
for item in items:
role = Role(name = item['name'])
role.details = item.get('details')
for email in item.get('emails', []):
RoleEmail(email = email, role = role)
new_ids_mapping.append( (item['id'], role.id) )
## fixing other tables to get the new ids
# users
user_init_method = User.__init__
User.__init__ = lambda x: x
users = storage.get_storage().values('users')
for user in users:
current_roles = user.__dict__['roles'][:]
user.__dict__['roles'] = []
for role in current_roles:
if not role in [x[0] for x in new_ids_mapping]:
continue # unknown role, skipping
user.__dict__['roles'].append([x[1] for x in new_ids_mapping if x[0] == role][0])
storage.get_storage().store(user)
User.__init__ = user_init_method
# formdefs
formdef_init_method = FormDef.__init__
FormDef.__init__ = lambda x: x
formdefs = storage.get_storage().values('formdefs')
for formdef in formdefs:
role = formdef.__dict__['receiver']
if not role in [x[0] for x in new_ids_mapping]:
formdef.__dict__['receiver'] = -1
else:
formdef.__dict__['receiver'] = [x[1] for x in new_ids_mapping if x[0] == role][0]
current_roles = formdef.__dict__.get('roles', [])[:]
formdef.__dict__['roles'] = []
for role in current_roles:
if not role in [x[0] for x in new_ids_mapping]:
continue # unknown role, skipping
formdef.__dict__['roles'].append([x[1] for x in new_ids_mapping if x[0] == role][0])
storage.get_storage().store(formdef)
FormDef.__init__ = formdef_init_method
# consultations
consultation_init_method = Consultation.__init__
Consultation.__init__ = lambda x: x
consultations = storage.get_storage().values('consultations')
for consultation in consultations:
role = consultation.__dict__['receiver']
consultation.__dict__['receiver'] = [x[1] for x in new_ids_mapping if x[0] == role][0]
current_roles = formdef.__dict__.get('roles', [])[:]
consultation.__dict__['roles'] = []
for role in current_roles:
if not role in [x[0] for x in new_ids_mapping]:
continue # unknown role, skipping
consultation.__dict__['roles'].append([x[1] for x in new_ids_mapping \
if x[0] == role][0])
storage.get_storage().store(consultation)
Consultation.__init__ = consultation_init_method
migrateOldData = classmethod(migrateOldData)

View File

@ -11,10 +11,13 @@ import liberty.root
import errors
import misc
import storage
import template
from form import *
from users import User
from formdata import FormData
from anonylink import AnonymityLink
class RootDirectory(Directory):
_q_exports = ["", "admin", "forms", "consultations", "login", "logout", "liberty", "token"]
@ -59,36 +62,31 @@ class RootDirectory(Directory):
session = get_session()
if session.lasso_anonymous_identity_dump:
lasso_dump = session.lasso_anonymous_identity_dump
elif session.user and not session.user.startswith('anonymous-'):
current_user = storage.get_storage().retrieve('users', session.user)
elif session.user and not str(session.user).startswith('anonymous-'):
current_user = User.get(session.user)
lasso_dump = current_user.lasso_dump
else:
return template.error_page("No Lasso Identity Dump (???)")
token = form.get_widget('token').parse()
for user in storage.get_storage().values('users'):
if user.identification_token == token:
user.identification_token = None
if not user.name_identifiers:
user.name_identifiers = []
user.name_identifiers.append(str(session.name_identifier))
user.lasso_dump = str(lasso_dump)
old_name = session.user
session.set_user(user.id)
storage.get_storage().store(user)
users_with_token = User.select(User.q.identification_token == token)
if users_with_token.count() == 0:
return template.error_page(_('Unknown Token'))
user = users_with_token[0]
NameIdentifier(name_identifier = session.name_identifier, user = user)
user.lasso_dump = str(lasso_dump)
old_name = session.user
session.set_user(user.id)
for formdef in storage.get_storage().values('formdefs'):
formnames = "form-" + formdef.id
for filled in storage.get_storage().values(formnames):
if filled.user_id == old_name:
filled.user_id = user.id
storage.get_storage().store(filled)
for consultationdef in storage.get_storage().values('consultations'):
consultationnames = "consultation-" + consultationdef.id
for filled in storage.get_storage().values(consultationnames):
if filled.user_id == old_name:
filled.user_id = user.id
storage.get_storage().store(filled)
break
for formdata in FormData.select(FormData.q.userID == old_name):
formdata.user = user
for anonylink in AnonymityLink.select(
AnonymityLink.q.name_identifier == session.name_identifier):
anonylink.formdata.user = user
return redirect('.')

View File

@ -1,67 +1,117 @@
from sqlobject import *
from quixote.session import Session, SessionManager
import storage
from quixote.util import randbytes
class BasicSession(Session, storage.Storable):
key = 'id'
names = 'sessions'
from extracols import DictPickleCol, ListPickleCol
class SqlSession(SQLObject):
_cacheValue = False
session_id = StringCol(notNone = True, alternateID = True, unique = True, length = 255)
user = StringCol(default = None)
remote_address = StringCol(default = None, length=30)
creation_time = FloatCol(default = None)
access_time = FloatCol(default = None)
name_identifier = StringCol(length = 100, default = None)
anonymous_key = StringCol(length = 100, default = None)
after_url = StringCol(default = None)
lasso_session_dump = StringCol(default = None)
lasso_anonymous_identity_dump = StringCol(default = None)
tempfiles = DictPickleCol(default = None)
form_tokens = ListPickleCol(default = None)
def _init(self, *args, **kw):
return SQLObject._init(self, *args, **kw)
class BasicSession(Session):
name_identifier = None
after_url = None
anonymous_id = None
anonymous_key = None
tempfiles = None
lasso_session_dump = None
lasso_anonymous_identity_dump = None
tempfiles = None
def has_info(self):
return self.name_identifier or self.after_url or self.anonymous_id or \
return self.name_identifier or self.after_url or self.anonymous_key or \
self.tempfiles or self.lasso_session_dump or \
self.lasso_anonymous_identity_dump or Session.has_info(self)
is_dirty = has_info
def get_anonymous_key(self, generate = False):
if self.anonymous_key:
return self.anonymous_key
if generate:
self.anonymous_key = randbytes(20)
return self.anonymous_key
class StorageSessionManager(SessionManager):
def __init__(self):
self.storage = storage.get_storage()
SessionManager.__init__(self, session_class=BasicSession)
def forget_changes(self, session):
pass
db_attributes = ['user', 'name_identifier', 'anonymous_key', 'after_url', 'lasso_session_dump',
'lasso_anonymous_identity_dump', 'tempfiles']
db_underscore_attributes = ['_remote_address', '_creation_time', '_access_time',
'_form_tokens']
def __getitem__(self, session_id):
try:
return storage.get_storage().retrieve('sessions', session_id)
except KeyError:
raise KeyError
def get(self, session_id, default = None):
try:
return storage.get_storage().retrieve('sessions', session_id)
except KeyError:
return default
def commit_changes(self, session):
if session and session.id:
storage.get_storage().store(session)
class SqlSessionMapping:
def keys(self):
return storage.get_storage().keys('sessions')
return [x.session_id for x in SqlSession.select()]
def values(self):
return [self.get(k) for k in self.keys()]
return [self._from_db(x) for x in SqlSession.select()]
def items(self):
return [(k, self.get(k)) for k in self.keys()]
return [(x.session_id, self._from_db(x)) for x in SqlSession.select()]
def has_key(self, session_id):
return session_id in self.keys()
def _from_db(self, sqlsession):
session = BasicSession(sqlsession.session_id)
for attr in db_attributes:
setattr(session, attr, getattr(sqlsession, attr))
for attr in db_underscore_attributes:
setattr(session, attr, getattr(sqlsession, attr[1:]))
return session
def __setitem__(self, session_id, session):
storage.get_storage().store(session)
def __delitem__(self, session_id):
if not session_id:
return
def _to_db(self, session):
try:
storage.get_storage().remove_id('sessions', session_id)
except OSError:
raise KeyError
sqlsession = SqlSession.bySession_id(session.id)
except SQLObjectNotFound:
sqlsession = SqlSession(session_id = session.id)
kw = {}
for attr in db_attributes:
kw[attr] = getattr(session, attr)
for attr in db_underscore_attributes:
kw[attr[1:]] = getattr(session, attr)
sqlsession.set(**kw)
def get(self, session_id, default = None):
if session_id is None:
return default
try:
return self._from_db(SqlSession.bySession_id(session_id))
except SQLObjectNotFound:
return default
def __getitem__(self, k):
return self.get(k)
def has_key(self, k):
return (self.get(k) is not None)
def __setitem__(self, k, v):
pass
def __delitem__(self, k):
SqlSession.bySession_id(k).destroySelf()
class SqlSessionManager(SessionManager):
def __init__(self):
SessionManager.__init__(self, BasicSession, SqlSessionMapping())
def commit_changes(self, session):
if session and session.has_info():
self.sessions._to_db(session)

View File

@ -24,6 +24,12 @@ class FilesStorage:
if self.base_dir and not os.path.exists(self.base_dir):
os.mkdir(self.base_dir)
def rename_table(self, obname, newname):
dirname = os.path.join(self.base_dir, str(obname))
newdirname = os.path.join(self.base_dir, str(newname))
if os.path.exists(dirname):
os.rename(dirname, newdirname)
def keys(self, obname):
dirname = os.path.join(self.base_dir, str(obname))
if not os.path.exists(dirname):
@ -43,7 +49,7 @@ class FilesStorage:
raise KeyError()
def store(self, object):
obname = object.get_table_name()
obname = object.names
dirname = os.path.join(self.base_dir, obname)
if not os.path.exists(dirname):
os.mkdir(dirname)

View File

@ -1,15 +1,86 @@
import storage
from sqlobject import *
class User(storage.Storable):
class NameIdentifier(SQLObject):
name_identifier = StringCol()
user = ForeignKey('User')
_cacheValues = False
class User(SQLObject):
key = 'id'
names = 'users'
_cacheValues = False
def __init__(self):
self.id = ''
self.name = ''
self.email = ''
self.roles = []
self.name_identifiers = []
self.identification_token = None
self.lasso_dump = None
name = StringCol(default = None, length = 100)
email = StringCol(default = None, length = 100)
roles = RelatedJoin('Role')
name_identifiers = MultipleJoin('NameIdentifier', joinMethodName = 'name_identifiers')
identification_token = StringCol(default = None)
lasso_dump = StringCol(default = None)
is_admin = BoolCol(default = False)
def migrateOldData(cls):
import storage
from roles import Role
from formdef import FormDef
from formdata import FormData
from consultationdef import Consultation
init_method = cls.__init__
cls.__init__ = lambda x: x
users = storage.get_storage().values('users')
items = [x.__dict__ for x in users]
cls.__init__ = init_method
items.sort(lambda x,y: cmp(x['id'], y['id']))
new_ids_mapping = []
for item in items:
user = User(name = item['name'])
user.email = item.get('email')
user.identification_token = item.get('identification_token')
user.lasso_dump = item.get('lasso_dump')
if 'site-admin' in item.get('roles', []):
user.is_admin = True
for ni in item.get('name_identifiers', []):
NameIdentifier(name_identifier = ni, user = user)
for role in item.get('roles', []):
if role == 'site-admin':
continue
user.addRole(Role.get(role))
new_ids_mapping.append( (item['id'], user.id) )
## Fixing other tables to get the new id
formdef_init_method = FormDef.__init__
FormDef.__init__ = lambda x: x
consultation_init_method = Consultation.__init__
Consultation.__init__ = lambda x: x
formdata_init_method = FormData.__init__
FormData.__init__ = lambda x: x
formdefs = storage.get_storage().values('formdefs')
consultations = storage.get_storage().values('consultations')
for elem in formdefs + consultations:
if isinstance(elem, FormDef):
formdataname = 'form-%s' % elem.__dict__['id']
elif isinstance(elem, Consultation):
formdataname = 'consultation-%s' % elem.__dict__['id']
else:
raise "unknown"
for formdata in storage.get_storage().values(formdataname):
if hasattr(formdata, 'user_id') and formdata.user_id:
try:
formdata.user_id = [x[1] for x in new_ids_mapping \
if x[0] == formdata.user_id][0]
except IndexError:
pass
if formdata.__dict__.has_key('evolution'):
for evo in formdata.__dict__['evolution']:
if not hasattr(evo, 'who') or not evo.who:
continue
evo.who = [x[1] for x in new_ids_mapping if x[0] == evo.who][0]
storage.get_storage().store(formdata)
FormData.__init__ = formdata_init_method
FormDef.__init__ = formdef_init_method
Consultation.__init__ = consultation_init_method
migrateOldData = classmethod(migrateOldData)