def add_participants(self): """ Add participants to this meeting. Renders a form where you can paste a csv with the users and select which roles they should have once they register. When the form is submitted, a list of userids and passwords is displayed """ self.response['title'] = _(u"Add meeting participants") post = self.request.POST if 'cancel' in post: self.api.flash_messages.add(_(u"Canceled")) url = resource_url(self.context, self.request) return HTTPFound(location=url) schema = createSchema('AddParticipantsSchema').bind(context=self.context, request=self.request, api=self.api) add_csrf_token(self.context, self.request, schema) form = Form(schema, buttons=(button_add, button_cancel)) self.api.register_form_resources(form) if 'add' in post: controls = post.items() try: appstruct = form.validate(controls) except ValidationFailure, e: self.response['form'] = e.render() return self.response roles = appstruct['roles'] output = self._import_participants(appstruct['csv'], roles) msg = _('added_participants_text', default=u"Successfully added ${participant_count} participants", mapping={'participant_count':len(output)} ) self.api.flash_messages.add(msg) self.response['heading'] = "%s %s" % (len(output), self.api.pluralize(self.api.translate(_("participant added")), self.api.translate(_("participants added")), len(output))) self.response['participants'] = output return Response(render("add_participants.pt", self.response, request = self.request))
import colander import deform from betahaus.pyracont.decorators import schema_factory from voteit.core import security from voteit.importparticipants.validators import csv_participant_validator from voteit.importparticipants import VoteITImportParticipants as _ @schema_factory('AddParticipantsSchema', title = _(u"Add meeting participants"), description = _(u"add_participants_schema_main_description", default = u"""Import participants from CSV. If different participants should have different rights you should import one level of rights at a time. Normally users have discuss, propose and vote.""")) class AddParticipantsSchema(colander.Schema): roles = colander.SchemaNode( deform.Set(), title = _(u"Roles"), default = (security.ROLE_DISCUSS, security.ROLE_PROPOSE, security.ROLE_VOTER), description = _(u"add_participants_roles_description", default = u"""A user can have more than one role. Note that to be able to propose, discuss and vote you need respective role. This is selected by default. If you want to add a user that can only view, select View and uncheck everything else."""), widget = deform.widget.CheckboxChoiceWidget(values=security.MEETING_ROLES,), ) csv = colander.SchemaNode(colander.String(), title = _(u"add_participants_csv_title", default=u"CSV list of participants"), description = _(u"add_participants_csv_description",
def __call__(self, node, value): html_string_validator(node, value) users = find_root(self.context).users nouserid = set() invalid = set() notunique = set() email = set() password = set() row_count = 0 # the value shoud be in unicode from colander and csv wants ascii or utf-8 value = value.encode('UTF-8') data = csv.reader(StringIO(value), delimiter=';', quotechar='"') try: for row in data: row_count = row_count + 1 if not row[0]: nouserid.add("%s" % row_count) if row[0] and not NEW_USERID_PATTERN.match(row[0]): invalid.add(row[0]) if row[0] in users: notunique.add(row[0]) # only validate email if there is an email if len(row) > 2 and row[2]: try: UniqueEmail(self.context)(node, row[2]) except colander.Invalid: email.add("%s" % row[2]) except IndexError: raise colander.Invalid(node, _('add_participants_invalid_csv', default=u"""CSV file is not valid, make sure at least userid is specified on each row and field delimiter is ; and text delimiter is " """)) msgs = [] if nouserid: msgs.append(self.api.translate(_('add_participants_no_userid_error', default=u"The following rows had no userid specified: ${nouserid}.", mapping={'nouserid': nouserid}))) if invalid: invalid = ", ".join(invalid) msgs.append(self.api.translate(_('add_participants_userid_char_error', default=u"The following userids is invalid: ${invalid}. UserID must be 3-30 chars, start with lowercase a-z and only contain lowercase a-z, numbers, minus and underscore.", mapping={'invalid': invalid}))) if notunique: notunique = ", ".join(notunique) msgs.append(self.api.translate(_('add_participants_notunique_error', default=u"The following userids is already registered: ${notunique}.", mapping={'notunique': notunique}))) if email: email = ", ".join(email) msgs.append(self.api.translate(_('add_participants_email_error', default=u"The following email addresses is invalid or already registered: ${email}.", mapping={'email': email}))) if password: password = "******".join(password) msgs.append(self.api.translate(_('add_participants_password_error', default=u"The following rows has invalid password: ${password}. Password must be between 6 and 100 characters", mapping={'password': password}))) if msgs: msg = "\n".join(msgs) raise colander.Invalid(node, msg)
from pyramid.url import resource_url from betahaus.viewcomponent import view_action from voteit.core.security import MANAGE_GROUPS from voteit.importparticipants import VoteITImportParticipants as _ @view_action('participants_menu', 'add_participants', title = _(u"Import participants"), link = "add_participants", permission = MANAGE_GROUPS) def generic_menu_link(context, request, va, **kw): """ This is for simple menu items for the meeting root """ api = kw['api'] url = "%s%s" % (api.meeting_url, va.kwargs['link']) return """<li><a href="%s">%s</a></li>""" % (url, api.translate(va.title))