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))