Пример #1
0
def bootstrap_voteit(echo=True):
    """ Bootstrap site root.
        Will add:
        - Site root
        - Agenda template folder
        - Users folder
        - An administrative user with login: admin and pass: admin
    """
    if echo:
        print "Bootstrapping site - creating 'admin' user with password 'admin'"
    #Add root
    root = createContent('Root', title = _(u"VoteIT"), creators = ['admin'])
    root.set_field_value('footer', u'<a href="http://www.voteit.se">www.voteit.se</a> &mdash; '
                                   u'<a href="http://manual.voteit.se">User and developer manual</a> &mdash; '
                                   u'<a href="https://github.com/VoteIT">Source code and bugtracker</a>')
    #Add templates if available
    try:
        root['agenda_templates'] = createContent('AgendaTemplates',
                                                 title = _(u"Agenda templates"),
                                                 creators = ['admin'])
    except KeyError:
        pass #For tests etc
    #Add users folder
    root['users'] = createContent('Users', title = _(u"Registered users"), creators = ['admin'])
    users = root.users
    #Add user admin - note that creators also set owner, which is important for changing password
    admin = createContent('User',
                          password = '******',
                          creators = ['admin'],
                          first_name = _(u'VoteIT'),
                          last_name = _(u'Administrator'))
    users['admin'] = admin
    #Add admin to group managers
    root.add_groups('admin', [ROLE_ADMIN])
    return root
Пример #2
0
    def add_form(self):
        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('AddUserSchema')
        add_csrf_token(self.context, self.request, schema)
        schema = schema.bind(context=self.context, request=self.request, api = self.api)
        form = Form(schema, buttons=(button_add, button_cancel))
        self.api.register_form_resources(form)

        if 'add' in post:
            controls = post.items()
            try:
                #appstruct is deforms convention. It will be the submitted data in a dict.
                appstruct = form.validate(controls)
            except ValidationFailure, e:
                self.response['form'] = e.render()
                return self.response
            
            #Userid and name should be consistent
            name = appstruct['userid']
            del appstruct['userid']
            
            #creators takes care of setting the role owner as well as adding it to creators attr.
            obj = createContent('User', creators=[name], **appstruct)
            self.context[name] = obj

            self.api.flash_messages.add(_(u"Successfully added"))

            url = resource_url(self.context, self.request)            
            return HTTPFound(location=url)
Пример #3
0
    def order_agenda_items(self):
        self.response['title'] = _(
            u"order_agenda_items_view_title",
            default=u"Drag and drop agenda items to reorder")
        post = self.request.POST
        if 'cancel' in self.request.POST:
            url = resource_url(self.context, self.request)
            return HTTPFound(location=url)

        if 'save' in post:
            controls = self.request.POST.items()
            ais = []
            order = 0
            for (k, v) in controls:
                if k == 'agenda_items':
                    ai = self.context[v]
                    ai.set_field_appstruct({'order': order})
                    order += 1
            self.api.flash_messages.add(_('Order updated'))

        context_path = resource_path(self.context)
        query = dict(
            path=context_path,
            content_type='AgendaItem',
            sort_index='order',
        )
        self.response['brains'] = self.api.get_metadata_for_query(**query)
        return self.response
Пример #4
0
    def add_permission(self):
        if ISiteRoot.providedBy(self.context):
            self.response['title'] = _(u"Add permission")
        post = self.request.POST
        if 'cancel' in post:
            url = resource_url(self.context, self.request)
            return HTTPFound(location=url)
        schema = createSchema('SinglePermissionSchema')
        add_csrf_token(self.context, self.request, schema)
        schema = schema.bind(context=self.context,
                             request=self.request,
                             api=self.api)
        form = Form(schema, buttons=(button_add, button_cancel))
        self.api.register_form_resources(form)
        if IMeeting.providedBy(self.context):
            self.response['tabs'] = self.api.render_single_view_component(
                self.context, self.request, 'tabs', 'manage_tickets')
        if 'add' in post:
            controls = post.items()
            try:
                appstruct = form.validate(controls)
            except ValidationFailure, e:
                self.response['form'] = e.render()
                return self.response

            #Set permissions
            self.context.set_groups(appstruct['userid'],
                                    appstruct['groups'],
                                    event=True)
            msg = _(u"Added permssion for user ${userid}",
                    mapping={'userid': appstruct['userid']})
            self.api.flash_messages.add(msg)
            url = resource_url(self.context, self.request)
            return HTTPFound(location=url)
Пример #5
0
    def __call__(self):
        post = self.request.POST
        if 'cancel' in self.request.POST:
            url = self.request.resource_url(self.context)
            return HTTPFound(location=url)
        if 'change' in post:
            state_id = self.request.POST['state_id']
            block_proposals = self.request.POST.get('block_proposals', None)
            block_proposals = _BLOCK_VALS.get(block_proposals, None)
            block_discussion = self.request.POST.get('block_discussion', None)
            block_discussion = _BLOCK_VALS.get(block_discussion, None)

            controls = self.request.POST.items()
            agenda_items = []
            for (k, v) in controls:
                if k == 'ais':
                    agenda_items.append(self.context[v])
            output_msg = ""
            translate = self.request.localizer.translate
            #WF state change
            if state_id:
                states_changed = 0
                for ai in agenda_items:
                    try:
                        ai.set_workflow_state(self.request, state_id)
                        states_changed += 1
                    except WorkflowError, e:
                        self.flash_messages.add(_(
                            'Unable to change state on ${title}: ${error}',
                            mapping={
                                'title': ai.title,
                                'error': e
                            }),
                                                type='danger')
                if states_changed:
                    output_msg += translate(
                        _("${num} changed state",
                          mapping={'num': states_changed}))
                    output_msg += "<br/>"
            #Block states
            if block_proposals != None or block_discussion != None:
                blocked = 0
                for ai in agenda_items:
                    blocked += 1
                    if block_proposals != None:
                        ai.set_field_value('proposal_block', block_proposals)
                    if block_discussion != None:
                        ai.set_field_value('discussion_block',
                                           block_discussion)
                if blocked:
                    output_msg += translate(
                        _("Changing block state for ${num} agenda items.",
                          mapping={'num': blocked}))

            if output_msg:
                self.flash_messages.add(output_msg, type='success')
            else:
                self.flash_messages.add(_('Nothing updated'), type='warning')
            return HTTPFound(location=self.request.resource_url(
                self.context, 'manage_agenda'))
Пример #6
0
def userid_node():
    return colander.SchemaNode(colander.String(),
                               title = _(u"UserID"),
                               description = _('userid_description',
                                               default=u"Used as a nickname, in @-links and as a unique id. "
                                                       u"You can't change this later. OK characters are: a-z, 0-9, '.', '-', '_'."),
                               validator=deferred_new_userid_validator,)
Пример #7
0
    def group_form(self):
        if IMeeting.providedBy(self.context):
            self.response['title'] = _(u"Edit permissions")
        else:
            self.response['title'] = _(u"Root permissions")
        post = self.request.POST
        if 'cancel' in post:
            url = resource_url(self.context, self.request)
            return HTTPFound(location=url)

        schema = createSchema('PermissionsSchema')
        add_csrf_token(self.context, self.request, schema)
        schema = schema.bind(context=self.context,
                             request=self.request,
                             api=self.api)

        form = Form(schema, buttons=('save', 'cancel'))
        self.api.register_form_resources(form)

        if 'save' in post:
            controls = post.items()
            try:
                appstruct = form.validate(controls)
            except ValidationFailure, e:
                self.response['form'] = e.render()
                return self.response

            #Set permissions
            self.context.set_security(appstruct['userids_and_groups'])
            url = resource_url(self.context, self.request)
            return HTTPFound(location=url)
Пример #8
0
def root_schema_adjustments(schema, event):
    schema.add(
        colander.SchemaNode(
            colander.String(),
            missing="",
            name='support_email',
            title=_(u"Support email for this site"),
            description=_(
                u"support_email_schema_desription",
                default=u"This email will receive mail sent through the support "
                "request form visible in the help menu."),
            validator=colander.Email(),
        ))
    schema.add(
        colander.SchemaNode(
            colander.String(),
            missing="",
            name='body',
            title=_("Main body text"),
            description=_(
                "main_body_desc",
                default=
                "This is the first page of VoteIT. Describe your instance here."
            ),
            widget=deform.widget.RichTextWidget(),
            validator=richtext_validator,
        ))
Пример #9
0
 def agenda_template_select(self):
     #FIXME: Should this be a migrate script?
     try:
         agenda_templates = self.api.root['agenda_templates']
     except KeyError:  # pragma: no coverage
         obj = createContent('AgendaTemplates', title = _(u"Agenda templates"), creators = ['admin'])
         agenda_templates = self.api.root['agenda_templates'] = obj
     
     get = self.request.GET
     if 'apply' in get:
         template_name = get['apply']
         if template_name in agenda_templates:
             template = agenda_templates[template_name]
             template.populate_meeting(self.context)
             
             msg = _(u"agenda_template_apply_template_success",
                     default = u"Selected template applied to meeting.")
             self.api.flash_messages.add(msg)
             
             return HTTPFound(location = resource_url(self.context, self.request))
         else:
             err_msg = _(u"agenda_template_apply_invalid_template",
                     default = u"No template named ${template} could be found.",
                     mapping = {'template': template_name})
             self.api.flash_messages.add(err_msg, type="error")
     
     self.response['agenda_templates'] = agenda_templates
     
     return self.response
Пример #10
0
    def render_result(self, view):
        votes = [x['uid']['proposal'] for x in self.context.poll_result]
        novotes = set(self.context.proposal_uids) - set(votes)
        translate = view.request.localizer.translate
        vote_singular = translate(_("vote_singular", default = "Vote"))
        vote_plural = translate(_("vote_plural", default = "Votes"))
        def _vote_text(count):
            return view.request.localizer.pluralize(vote_singular, vote_plural, count)

        results = []
        #Adjust result layout
        for res in tuple(self.context.poll_result):
            results.append({'uid': res['uid']['proposal'],
                            'count': res['count'],
                            'num': res['num'],
                            'perc': int(round(res['num'] * 100, 0))})
        for uid in novotes:
            results.append({'uid': uid, 'count': 0, 'num': 0, 'perc': 0})
        response = {}
        response['results'] = results
        #response['novotes'] = novotes
        response['vote_text'] = _vote_text
        response['total'] = sum([x[1] for x in self.context.ballots])
        proposals = {}
        for prop in self.context.get_proposal_objects():
            proposals[prop.uid] = prop
        response['proposals'] = proposals
        return render('templates/majority_poll.pt', response, request = view.request)
Пример #11
0
 def configure_access_policy(self):
     access_policy_name = self.context.get_field_value('access_policy', 'invite_only')
     access_policy = self.request.registry.queryAdapter(self.context, IAccessPolicy, name = access_policy_name)
     if not access_policy:
         err_msg = _(u"access_policy_not_found_moderator",
                     default = u"""Can't find an access policy with the id '${policy}'.
                                 This might mean that the registered access type for this meeting doesn't exist anylonger.
                                 Please change access policy.""",
                     mapping = {'policy': access_policy_name})
         self.api.flash_messages.add(err_msg, type="error")
         url = self.request.resource_url(self.api.meeting, 'access_policy')
         return HTTPFound(location=url)
     form = access_policy.config_form(self.api)
     post = self.request.POST
     if 'save' in post:
         controls = post.items()
         try:
             appstruct = form.validate(controls)
         except deform.ValidationFailure, e:
             self.response['form'] = e.render()
             return self.response
         self.context.set_field_appstruct(appstruct)
         self.api.flash_messages.add(_(u"Saved"))
         url = self.request.resource_url(self.api.meeting)
         return HTTPFound(location=url)
Пример #12
0
class CondorcetLooser(Criteria):
    title = _("Condorcet looser")
    help = _(
        "criteria_cl_help",
        default=
        "The looser must loose against every other candidate in a pairwise comparison.",
    )
Пример #13
0
class CloneProof(Criteria):
    title = _("Clone proof")
    help = _(
        "criteria_cp_help",
        default=
        "The winner must not change due to strategic nomination, for instance that a similar candidate runs.",
    )
Пример #14
0
class CondorcetWinner(Criteria):
    title = _("Condorcet winner")
    help = _(
        "criteria_cw_help",
        default=
        "The winner must beat every other candidate in a pairwise comparison.",
    )
Пример #15
0
class MajorityLooser(Criteria):
    title = _("Majority looser")
    help = _(
        "criteria_ml_help",
        default=
        "If a majority of voters do not want this, it shouldn't be able to win.",
    )
Пример #16
0
class MajorityWinner(Criteria):
    title = _("Majority winner")
    help = _(
        "criteria_mw_help",
        default=
        "A majority (more than 50%) must prefer this, otherwise it shouldn't be able to win.",
    )
Пример #17
0
 def _results_ts(count):
     """ Note about the odd syntax: pluralize returns unicode, so it won't be translated.
         Hence it needs to be converted back to a translation string.
     """
     return _(self.api.pluralize(_(u"item"),
                               _(u"items"),
                               count))
Пример #18
0
 def edit_form(self):
     """ For configuring polls that haven't started yet. """
     schema_name = self.api.get_schema_name(self.context.content_type, 'edit')
     schema = createSchema(schema_name, after_bind=poll_schema_after_bind)
     add_csrf_token(self.context, self.request, schema)
     schema = schema.bind(context=self.context, request=self.request, api = self.api)
     form = Form(schema, buttons=(button_update, button_cancel))
     self.api.register_form_resources(form)
     post = self.request.POST
     if self.request.method == 'POST':
         if 'update' in post:
             controls = post.items()
             try:
                 #appstruct is deforms convention. It will be the submitted data in a dict.
                 appstruct = form.validate(controls)
             except ValidationFailure, e:
                 self.response['form'] = e.render()
                 return self.response
             removed_uids = set(self.context.proposal_uids) - set(appstruct['proposals'])
             if removed_uids:
                 #Adjust removed proposals back to published state, if they're locked
                 for uid in removed_uids:
                     prop = self.context.get_proposal_by_uid(uid)
                     if prop.get_workflow_state() == 'voting':
                         prop.set_workflow_state(self.request, u'published')
             updated = self.context.set_field_appstruct(appstruct)
             if updated:
                 self.api.flash_messages.add(_(u"Successfully updated"))
             else:
                 self.api.flash_messages.add(_(u"Nothing changed"))
         if 'cancel' in post:
             self.api.flash_messages.add(_(u"Canceled"))
         url = self.request.resource_url(self.context.__parent__, anchor = self.context.uid)
         return HTTPFound(location = url)
Пример #19
0
def bootstrap_voteit(echo=True):
    """ Bootstrap site root.
        Will add:
        - Site root
        - Agenda template folder
        - Users folder
        - An administrative user with login: admin and pass: admin
    """
    if echo:
        print "Bootstrapping site - creating 'admin' user with password 'admin'"
    #Add root
    root = createContent('SiteRoot', title=_(u"VoteIT"), creators=['admin'])
    root.set_field_value(
        'footer', u'<a href="http://www.voteit.se">www.voteit.se</a> &mdash; '
        u'<a href="http://manual.voteit.se">User and developer manual</a> &mdash; '
        u'<a href="https://github.com/VoteIT">Source code and bugtracker</a>')
    #Add users folder
    root['agenda_templates'] = createContent('AgendaTemplates',
                                             title=_(u"Agenda templates"),
                                             creators=['admin'])
    #Add users folder
    root['users'] = createContent('Users',
                                  title=_(u"Registered users"),
                                  creators=['admin'])
    users = root.users
    #Add user admin - note that creators also set owner, which is important for changing password
    admin = createContent('User',
                          password='******',
                          creators=['admin'],
                          first_name=_(u'VoteIT'),
                          last_name=_(u'Administrator'))
    users['admin'] = admin
    #Add admin to group managers
    root.add_groups('admin', [ROLE_ADMIN])
    return root
Пример #20
0
class ImmediateAP(AccessPolicy):
    """ Grant access for specific permissions immediately if a user requests it.
        No moderator approval requred. This is for very public meetings.
    """
    name = 'public'
    title = _(u"public_access_title", default=u"Public access")
    description = _(
        u"public_access_description",
        default=
        u"Users will be granted the permissions you select without prior moderator approval. This is for public meetings."
    )
    configurable = True

    def schema(self, api):
        return colander.Schema(
            title=_(u"Would you like to participate?"),
            description=_(
                u"Clicking request access will grant you access right away!"))

    def handle_success(self, api, appstruct):
        rolesdict = dict(security.STANDARD_ROLES)
        roles = self.context.get_field_value('immediate_access_grant_roles')
        self.context.add_groups(api.userid, roles)
        api.flash_messages.add(_(u"Access granted - welcome!"))

    def config_schema(self, api):
        return createSchema('ImmediateAPConfigSchema')
Пример #21
0
    def render_result(self, view):
        votes = [x['uid']['proposal'] for x in self.context.poll_result]
        novotes = set(self.context.proposal_uids) - set(votes)
        translate = view.request.localizer.translate
        vote_singular = translate(_("vote_singular", default="Vote"))
        vote_plural = translate(_("vote_plural", default="Votes"))

        def _vote_text(count):
            return view.request.localizer.pluralize(vote_singular, vote_plural,
                                                    count)

        results = []
        #Adjust result layout
        for res in tuple(self.context.poll_result):
            results.append({
                'uid': res['uid']['proposal'],
                'count': res['count'],
                'num': res['num'],
                'perc': int(round(res['num'] * 100, 0))
            })
        for uid in novotes:
            results.append({'uid': uid, 'count': 0, 'num': 0, 'perc': 0})
        response = {}
        response['results'] = results
        #response['novotes'] = novotes
        response['vote_text'] = _vote_text
        response['total'] = sum([x[1] for x in self.context.ballots])
        proposals = {}
        for prop in self.context.get_proposal_objects():
            proposals[prop.uid] = prop
        response['proposals'] = proposals
        return render('templates/majority_poll.pt',
                      response,
                      request=view.request)
Пример #22
0
 def adjust_proposal_states(self, uid_states, request = None):
     assert isinstance(uid_states, dict)
     if request is None:
         request = get_current_request()
     changed = []
     change_error = []
     for (uid, state) in uid_states.items():
         if uid not in self.proposal_uids:
             raise ValueError("The poll plugins close() method returned a uid that doesn't exist in this poll.")
         proposal = self.get_proposal_by_uid(uid)
         #Adjust state?
         prop_wf_state = proposal.get_workflow_state()
         if prop_wf_state != state:
             try:
                 proposal.set_workflow_state(request, state)
                 changed.append(proposal)
             except WorkflowError:
                 change_error.append(proposal)
     fm = get_flash_messages(request)
     msg = _('poll_closed_info',
             default = "Poll has now closed. ${num} proposal(s) are now set in another state due to the outcome of the poll.",
             mapping = {'num': len(changed)})
     fm.add(msg)
     if change_error:
         msg = _('poll_closed_proposal_wf_change_error',
                 default = "Couldn't adjust the state of the following proposal(s): '${props}'. "
                 "You may wish to review them manually.",
                 mapping = {'props': "', '".join(prop.aid for prop in change_error)})
         fm.add(msg, type = 'danger')
Пример #23
0
    def sort(self):
        self.response['title'] = _(u"order_agenda_template_view_title",
                                   default = u"Drag and drop agenda items to reorder")

        post = self.request.POST
        if 'cancel' in self.request.POST:
            url = resource_url(self.context, self.request)
            return HTTPFound(location = url)

        if 'save' in post:
            controls = self.request.POST.items()
            ais = self.context.get_field_value('agenda_items')
            order = 0
            agenda_items = []
            for (k, v) in controls:
                if k == 'agenda_items':
                    ai = ais[int(v)]
                    ai['order'] = order
                    order += 1
                    agenda_items.append(ai)
            self.context.set_field_value('agenda_items', agenda_items)
            self.api.flash_messages.add(_(u'Order updated'))
            url = resource_url(self.context, self.request)
            return HTTPFound(location = url)

        return self.response
Пример #24
0
 def ticket_claim(self):
     """ After login or registration, redirect back here, where information about the ticket will be displayed,
         and a confirmation that you want to use the ticket for the current user.
         
         While we use a regular deform form, it's not ment to be displayed or handle any validation.
     """
     if not self.api.userid:
         raise HTTPForbidden("Direct access to this view for unauthorized users not allowed.")
     schema = createSchema('ClaimTicketSchema', validator = deferred_token_form_validator)
     schema = schema.bind(context=self.context, request=self.request, api = self.api)
     form = deform.Form(schema, buttons=(button_add, button_cancel))
     if self.request.GET.get('claim'):
         controls = self.request.params.items()
         try:
             appstruct = form.validate(controls)
         except deform.ValidationFailure, e:
             msg = _(u"ticket_validation_fail",
                     default = u"Ticket validation failed. Either the ticket doesn't exist, was already used or the url used improperly. "
                               u"If you need help, please contact the moderator that invited you to this meeting.")
             self.api.flash_messages.add(msg, type = 'error')
             url = self.request.resource_url(self.api.root)
             return HTTPFound(location = url)
         #Everything in order, claim ticket
         ticket = self.context.invite_tickets[appstruct['email']]
         ticket.claim(self.request)
         self.api.flash_messages.add(_(u"You've been granted access to the meeting. Welcome!"))
         url = self.request.resource_url(self.context)
         return HTTPFound(location=url)
Пример #25
0
    def render_result(self, view):
        votes = [x["uid"]["proposal"] for x in self.context.poll_result]
        novotes = set(self.context.proposal_uids) - set(votes)
        translate = view.request.localizer.translate
        vote_singular = translate(_("vote_singular", default="Vote"))
        vote_plural = translate(_("vote_plural", default="Votes"))

        def _vote_text(count):
            return view.request.localizer.pluralize(vote_singular, vote_plural,
                                                    count)

        results = []
        # Adjust result layout
        for res in tuple(self.context.poll_result):
            results.append({
                "uid": res["uid"]["proposal"],
                "count": res["count"],
                "num": res["num"],
                "perc": int(round(res["num"] * 100, 0)),
            })
        for uid in novotes:
            results.append({"uid": uid, "count": 0, "num": 0, "perc": 0})
        response = {}
        response["results"] = results
        # response['novotes'] = novotes
        response["vote_text"] = _vote_text
        response["total"] = sum([x[1] for x in self.context.ballots])
        proposals = {}
        for prop in self.context.get_proposal_objects():
            proposals[prop.uid] = prop
        response["proposals"] = proposals
        return render("templates/majority_poll.pt",
                      response,
                      request=view.request)
Пример #26
0
 def vote_success(self, appstruct):
     #Just in case the form rendered before the poll closed
     if not self.can_vote:
         raise HTTPForbidden(_("You're not allowed to vote"))
     Vote = self.poll_plugin.get_vote_class()
     if not IVote.implementedBy(Vote):
         raise TypeError("Poll plugins method get_vote_class returned something that didn't implement IVote.")
     appstruct.pop('csrf_token', None)
     userid = self.api.userid
     if userid in self.context:
         vote = self.context[userid]
         assert IVote.providedBy(vote)
         vote.set_vote_data(appstruct)
     else:
         vote = Vote(creators = [userid])
         #We don't need to send events here, since object added will take care of that
         vote.set_vote_data(appstruct, notify = False)
         #To fire events after set_vote_data is done
         self.context[userid] = vote
     success_msg = _(u"Your vote has been registered!")
     if self.request.is_xhr:
         self.response['success_msg'] = success_msg
         return Response(render("templates/snippets/vote_success.pt", self.response, request = self.request))
     self.api.flash_messages.add(success_msg)
     url = self.request.resource_url(self.context.__parent__, anchor = self.context.uid)
     return HTTPFound(location = url)
Пример #27
0
 def __call__(self):
     tags = self.request.params.getall('tag')
     response = {'tags': tags}
     if tags:
         trans = self.request.localizer.translate
         filter_msg = """%s
         <a href="%s"
            data-load-agenda-item="#content"
            class="btn btn-default btn-xs"
            data-ai-name="%s"> %s </a>
        """ % (
             trans(_("Filter active, showing ${num} tag(s)", mapping={'num': len(tags)})),
             self.request.clear_tags_url(self.context),
             self.context.__name__,
             trans(_("Show all")),
         )
         filter_msg = filter_msg.replace('\n','')
         response['filter_msg'] = filter_msg
     # 200 is a default, but None removes the setting, hence this
     collapsible_limit = self.context.collapsible_limit
     if collapsible_limit is None:
         collapsible_limit = 200
     if collapsible_limit == 0:
         collapsible_limit = None
     response['collapsible_limit'] = collapsible_limit
     return response
Пример #28
0
 def add_success(self, appstruct):
     emails = appstruct['emails'].splitlines()
     roles = appstruct['roles']
     added = 0
     rejected = 0
     for email in emails:
         result = self.context.add_invite_ticket(email, roles, sent_by = self.request.authenticated_userid)
         if result:
             added += 1
         else:
             rejected += 1
     if not rejected:
         msg = _('added_tickets_text', default = "Successfully added ${added} invites",
                 mapping={'added': added})
     elif not added:
         msg = _('no_tickets_added',
                 default = "No tickets added - all you specified probably exist already. "
                 "(Proccessed ${rejected})",
                 mapping = {'rejected': rejected})
         self.flash_messages.add(msg, type = 'warning', auto_destruct = False)
         url = self.request.resource_url(self.context, 'add_tickets')
         return HTTPFound(location = url)
     else:
         msg = _('added_tickets_text_some_rejected',
                 default = "Successfully added ${added} invites but discarded ${rejected} "
                 "since they already existed or were already used.",
                 mapping={'added': added, 'rejected': rejected})
     self.flash_messages.add(msg)
     self.request.session['send_tickets.emails'] = emails
     self.request.session['send_tickets.message'] = appstruct['message']
     url = self.request.resource_url(self.context, 'send_tickets')
     return HTTPFound(location = url)
Пример #29
0
 def ticket_claim(self):
     """ After login or registration, redirect back here, where information about the ticket will be displayed,
         and a confirmation that you want to use the ticket for the current user.
         
         While we use a regular deform form, it's not ment to be displayed or handle any validation.
     """
     if not self.request.authenticated_userid:
         raise HTTPForbidden("Direct access to this view for unauthorized users not allowed.")
     email = self.request.GET.get('email', '')
     ticket = self.context.invite_tickets.get(email, None)
     if ticket and ticket.closed != None:
         msg = _("This ticket has already been used.")
         self.flash_messages.add(msg, type = 'danger', auto_destruct = True, require_commit = False)
         return HTTPFound(location = self.request.resource_url(self.context))
     schema = get_content_schemas(self.request.registry)['Meeting']['claim_ticket']()
     schema = schema.bind(context = self.context, request = self.request, view = self)
     form = deform.Form(schema, buttons = (button_add, button_cancel,))
     if self.request.GET.get('claim'):
         controls = self.request.params.items()
         try:
             appstruct = form.validate(controls)
         except deform.ValidationFailure, e:
             msg = _("ticket_validation_fail",
                     default = "Ticket validation failed. Either the "
                     "ticket doesn't exist, was already used or the url used improperly. "
                     "If you need help, please contact the moderator that invited you to this meeting.")
             self.flash_messages.add(msg, type = 'danger', auto_destruct = False, require_commit = False)
             url = self.request.resource_url(self.root)
             return HTTPFound(location = url)
         #Everything in order, claim ticket
         ticket = self.context.invite_tickets[appstruct['email']]
         claim_ticket(ticket, self.request, self.request.authenticated_userid)
         self.flash_messages.add(_(u"You've been granted access to the meeting. Welcome!"))
         url = self.request.resource_url(self.context)
         return HTTPFound(location=url)
Пример #30
0
class ProposalSettingsSchema(colander.Schema):
    hide_proposal_states = colander.SchemaNode(
        colander.Set(),
        title=_("Hide proposal states"),
        description=_("hide_proposal_states_description",
                      default="Proposals in these states will be hidden by "
                      "default but can be shown by pressing "
                      "the link below the other proposals. They're not "
                      "by any means invisible to participants."),
        widget=proposal_states_widget,
        default=('retracted', 'denied', 'unhandled'),
    )
    system_userids = colander.SchemaNode(
        colander.List(),
        widget=MeetingUserReferenceWidget(multiple=True),
        validator=existing_userids,
        title=_("System user accounts"),
        description=_("system_userids_description",
                      default="Must be an existing userid. "
                      "If they're added here, moderators can use them "
                      "to add proposals in their name. "
                      "It's good practice to add things like 'propositions', "
                      "'board' or similar."),
        missing=(),
    )
    proposal_id_method = colander.SchemaNode(
        colander.String(),
        title=_("Proposal naming method"),
        widget=proposal_naming_widget,
        missing="",
    )
Пример #31
0
class GravatarProfileImagePlugin(object):
    name = u"gravatar_profile_image"
    title = _("Gravatar")
    description = _(
        "profile_gravatar_explanation",
        default=
        'Profile image from <a href="http://www.gravatar.com" target="_blank">Gravatar network</a>. '
        "It's taken from your current email address. If you want to change the picture, simply go to "
        "the Gravatar site and change your picture for the email you use in VoteIT.",
    )

    def __init__(self, context):
        self.context = context

    def url(self, size, request):
        url = "https://secure.gravatar.com/avatar/"
        email = self.context.get_field_value("email", "").strip().lower()
        if email:
            url += md5(email).hexdigest()
        url += "?s=%s" % size
        gt = request.root.site_settings.get(
            "gravatar_default_type",
            request.registry.settings.get("voteit.gravatar_default",
                                          "robohash"),
        )
        url += "&d=%s" % gt

        return url

    def is_valid_for_user(self):
        return True
Пример #32
0
 def request_password(self):        
     schema = createSchema('RequestNewPasswordSchema').bind(context=self.context, request=self.request)
     form = Form(schema, buttons=(button_request, button_cancel))
     self.api.register_form_resources(form)
 
     #Handle submitted information
     if 'request' in self.request.POST:
         controls = self.request.POST.items()
 
         try:
             #appstruct is deforms convention. It will be the submitted data in a dict.
             appstruct = form.validate(controls)
         except ValidationFailure, e:
             self.response['form'] = e.render()
             return self.response
         
         userid_or_email = appstruct['userid_or_email']
 
         #userid here can be either an email address or a login name
         if '@' in userid_or_email:
             #assume email
             user = self.context['users'].get_user_by_email(userid_or_email)
         else:
             user = self.context['users'].get(userid_or_email)
         
         if IUser.providedBy(user):
             user.new_request_password_token(self.request)
             self.api.flash_messages.add(_('Email sent.'))
             url = resource_url(self.api.root, self.request)
             return HTTPFound(location = url)
 
         self.api.flash_messages.add(_('Username or email not found.'), type='error')
Пример #33
0
 def add_success(self, appstruct):
     userid = appstruct['userid']
     roles = appstruct['roles']
     if roles and security.ROLE_VIEWER not in roles:
         roles.add(security.ROLE_VIEWER)
     old_roles = self.context.local_roles.get(userid, set())
     if old_roles:
         new_roles = roles - old_roles
         if new_roles:
             trans = self.request.localizer.translate
             role_titles = []
             for role_name in new_roles:
                 role = self.request.registry.roles.get(role_name)
                 role_titles.append(trans(role.title))
             msg = _("new_roles_appended_notice",
                     default = "User was already a part of this meeting, "
                     "but these new roles were added: ${roles}",
                     mapping = {'roles': ", ".join(role_titles)})
             self.flash_messages.add(msg, type = 'warning')
         else:
             self.flash_messages.add(_("No new roles added - user already had all of them."))
     else:
         #Userid wasn't registered in this meeting
         self.flash_messages.add(self.default_success, type = "success")
     self.context.local_roles.add(appstruct['userid'], roles)
     return HTTPFound(location = self.request.resource_url(self.context, 'add_userid'))
Пример #34
0
def lock_proposals(poll, request):
    """ Set proposals to voting. """
    count = 0
    for proposal in poll.get_proposal_objects():
        try:
            proposal.set_workflow_state(request, 'voting')
            count += 1
        except WorkflowError:
            # Skip those
            pass
    if count:
        fm = IFlashMessages(request, None)
        if fm:
            singular = request.localizer.translate(_(u"proposal"))
            plural = request.localizer.translate(_(u"proposals"))
            prop_form = request.localizer.pluralize(singular, plural, count)
            msg = _(
                u'poll_proposals_locked_notice',
                default=u"Setting ${count} ${prop_form} as 'locked for vote'. "
                u"They can no longer be edited or retracted by normal users. ",
                mapping={
                    'count': count,
                    'prop_form': prop_form
                })
            fm.add(msg)
Пример #35
0
    def view_edit_manage_connected_profiles(self):
        schema = createSchema('ManageConnectedProfilesSchema').bind(
            context=self.context, request=self.request)
        form = Form(schema, buttons=(button_delete, button_cancel))
        self.api.register_form_resources(form)

        #Handle submitted information
        if 'delete' in self.request.POST:
            controls = self.request.POST.items()
            try:
                appstruct = form.validate(controls)
            except ValidationFailure, e:
                self.response['form'] = e.render()
                return self.response
            domains = appstruct['auth_domains']
            if domains:
                for domain in domains:
                    del self.context.auth_domains[domain]
                msg = _(u"Removing information for: ${domains}",
                        mapping={'domains': ", ".join(domains)})
                self.api.flash_messages.add(msg)
            else:
                self.api.flash_messages.add(_(u"Nothing updated"))
            url = resource_url(self.context, self.request)
            return HTTPFound(location=url)
Пример #36
0
def inline_add_proposal_form(context, request, va, **kw):
    """ For agenda item contexts.
    """
    api = kw['api']
    jquery_form.need() #This isn't included in widgets for some reason
    form = inline_add_form(api, 'Proposal', {})
    api.register_form_resources(form)
    if not api.context_has_permission(ADD_PROPOSAL, context):
        if context.get_workflow_state() == 'closed':
            msg = api.translate(_(u"no_propose_ai_closed",
                                  default = u"The agenda item is closed, you can't add a proposal here"))
        elif api.meeting.get_workflow_state() == 'closed':
            msg = api.translate(_(u"no_propose_meeting_closed",
                                  default = u"The meeting is closed, you can't add a proposal here"))
        else:
            msg = api.translate(_(u"no_propose_perm_notice",
                                  default = u"You don't have the required permission to add a proposal here"))
        return "<hr/>%s" % msg
    response = {}
    query = {'content_type': 'Proposal'}
    tag = request.GET.get('tag', None)
    if tag:
        query['tag'] = tag
    response['url'] = request.resource_url(context, '_inline_form', query = query)
    response['text'] = _(u'${username} propose', mapping={'username': api.userid})
    return render('../templates/snippets/inline_dummy_proposal_button.pt', response, request = request)
Пример #37
0
    def agenda_template_select(self):
        #FIXME: Should this be a migrate script?
        try:
            agenda_templates = self.api.root['agenda_templates']
        except KeyError:  # pragma: no coverage
            obj = createContent('AgendaTemplates',
                                title=_(u"Agenda templates"),
                                creators=['admin'])
            agenda_templates = self.api.root['agenda_templates'] = obj

        get = self.request.GET
        if 'apply' in get:
            template_name = get['apply']
            if template_name in agenda_templates:
                template = agenda_templates[template_name]
                template.populate_meeting(self.context)

                msg = _(u"agenda_template_apply_template_success",
                        default=u"Selected template applied to meeting.")
                self.api.flash_messages.add(msg)

                return HTTPFound(
                    location=resource_url(self.context, self.request))
            else:
                err_msg = _(
                    u"agenda_template_apply_invalid_template",
                    default=u"No template named ${template} could be found.",
                    mapping={'template': template_name})
                self.api.flash_messages.add(err_msg, type="error")

        self.response['agenda_templates'] = agenda_templates

        return self.response
Пример #38
0
    def sort(self):
        self.response['title'] = _(
            u"order_agenda_template_view_title",
            default=u"Drag and drop agenda items to reorder")

        post = self.request.POST
        if 'cancel' in self.request.POST:
            url = resource_url(self.context, self.request)
            return HTTPFound(location=url)

        if 'save' in post:
            controls = self.request.POST.items()
            ais = self.context.get_field_value('agenda_items')
            order = 0
            agenda_items = []
            for (k, v) in controls:
                if k == 'agenda_items':
                    ai = ais[int(v)]
                    ai['order'] = order
                    order += 1
                    agenda_items.append(ai)
            self.context.set_field_value('agenda_items', agenda_items)
            self.api.flash_messages.add(_(u'Order updated'))
            url = resource_url(self.context, self.request)
            return HTTPFound(location=url)

        return self.response
Пример #39
0
def inline_add_discussion_form(context, request, va, **kw):
    """ For agenda item contexts.
    """
    api = kw['api']
    jquery_form.need()  #This isn't included in widgets for some reason
    form = inline_add_form(api, 'DiscussionPost', {})
    api.register_form_resources(form)
    if not api.context_has_permission(ADD_DISCUSSION_POST, context):
        if api.meeting.get_workflow_state() == 'closed':
            msg = api.translate(
                _(u"no_discuss_meeting_closed",
                  default=
                  u"The meeting is closed, you can't add a discussion post here"
                  ))
        else:
            msg = api.translate(
                _(u"no_discuss_perm_notice",
                  default=
                  u"You don't have the required permission to add a discussion post here"
                  ))
        return "<hr/>%s" % msg
    response = {}
    response['user_image_tag'] = api.user_profile.get_image_tag(
        request=request)
    query = {'content_type': 'DiscussionPost'}
    tag = request.GET.get('tag', None)
    if tag:
        query['tag'] = tag
    response['url'] = request.resource_url(context,
                                           '_inline_form',
                                           query=query)
    response['text'] = _(u'Add')
    return render('../templates/snippets/inline_dummy_form.pt',
                  response,
                  request=request)
Пример #40
0
 def vote_success(self, appstruct):
     #Just in case the form rendered before the poll closed
     if not self.can_vote:
         raise HTTPForbidden(_("You're not allowed to vote"))
     Vote = self.poll_plugin.get_vote_class()
     if not IVote.implementedBy(Vote):
         raise TypeError(
             "Poll plugins method get_vote_class returned something that didn't implement IVote."
         )
     appstruct.pop('csrf_token', None)
     userid = self.request.authenticated_userid
     if userid in self.context:
         vote = self.context[userid]
         assert IVote.providedBy(vote), "%r doesn't provide IVote" % vote
         vote.set_vote_data(appstruct)
         success_msg = _("Your vote was changed.")
     else:
         vote = Vote(creators=[userid])
         #We don't need to send events here, since object added will take care of that
         vote.set_vote_data(appstruct, notify=False)
         #To fire events after set_vote_data is done
         self.context[userid] = vote
         success_msg = _(
             "vote_success_msg",
             default="Your vote has been added. If you wish to change it, "
             "you may do so as long as the poll is open.")
     self.flash_messages.add(success_msg)
     return self._remove_modal_response()
Пример #41
0
def meeting_mail_name_node():
    return colander.SchemaNode(colander.String(),
                               title = _(u"Name of the contact person for this meeting"),
                               default = _deferred_current_fullname,
                               validator = colander.Regex(regex=NAME_PATTERN,
                                                          msg=_(u"name_pattern_error",
                                                                default = u"Must be at least 3 chars + only alphanumeric characters allowed")),)
Пример #42
0
    def get_vote_schema(self, request=None, api=None):
        """ Get an instance of the schema that this poll uses.
        """
        proposals = self.context.get_proposal_objects()
        
        #Choices should be something iterable with the contents [(UID for proposal, Title of proposal), <etc...>, ]
        choices = set()
        
        for prop in proposals:
            title = u"#%s - %s" % (prop.get_field_value('aid'), prop.title)
            choices.add((prop.uid, title))

        poll_wf_state = self.context.get_workflow_state()
        if poll_wf_state == 'ongoing':
            proposal_title = _(u"Vote for one")
        else:
            proposal_title = _(u"You can't change your vote now.")

        class Schema(colander.Schema):
            proposal = colander.SchemaNode(
                            colander.String(),
                            validator=colander.OneOf([x[0] for x in choices]),
                            widget=deform.widget.RadioChoiceWidget(values=choices),
                            title=proposal_title,
                            description=u'',)

        return Schema()
Пример #43
0
def meeting_default_description(node, kw):
    request = kw['request']
    choices = dict(PROPOSAL_ORDER_CHOICES)
    title = choices.get(request.meeting.poll_proposals_default_order, _('Unknown'))
    title = request.localizer.translate(title)
    return _("Meeting default is currently: ${title}",
             mapping={'title': title})
Пример #44
0
    def get_vote_schema(self, request=None, api=None):
        """ Get an instance of the schema that this poll uses.
        """
        proposals = self.context.get_proposal_objects()

        #Choices should be something iterable with the contents [(UID for proposal, Title of proposal), <etc...>, ]
        choices = set()

        for prop in proposals:
            title = u"#%s - %s" % (prop.get_field_value('aid'), prop.title)
            choices.add((prop.uid, title))

        poll_wf_state = self.context.get_workflow_state()
        if poll_wf_state == 'ongoing':
            proposal_title = _(u"Vote for one")
        else:
            proposal_title = _(u"You can't change your vote now.")

        class Schema(colander.Schema):
            proposal = colander.SchemaNode(
                colander.String(),
                validator=colander.OneOf([x[0] for x in choices]),
                widget=deform.widget.RadioChoiceWidget(values=choices),
                title=proposal_title,
                description=u'',
            )

        return Schema()
Пример #45
0
    def get_vote_schema(self):
        """ Get an instance of the schema that this poll uses.
        """
        proposals = self.context.get_proposal_objects()
        # Choices should be something iterable with the contents [(UID for proposal, Title of proposal), <etc...>, ]
        choices = set()
        for prop in proposals:
            title = "#%s - %s" % (prop.get_field_value("aid"), prop.text)
            choices.add((prop.uid, title))
        poll_wf_state = self.context.get_workflow_state()
        if poll_wf_state == "ongoing":
            proposal_title = _("Vote for one")
        else:
            proposal_title = _("You can't change your vote now.")

        class Schema(colander.Schema):
            widget = deform.widget.FormWidget(
                template="form_modal", readonly_template="readonly/form_modal")
            proposal = colander.SchemaNode(
                colander.String(),
                validator=colander.OneOf([x[0] for x in choices]),
                widget=deform.widget.RadioChoiceWidget(values=choices),
                title=proposal_title,
                description="",
            )

        return Schema()
Пример #46
0
class ImmediateAP(AccessPolicy):
    """ Grant access for specific permissions immediately if a user requests it.
        No moderator approval requred. This is for very public meetings.
    """
    name = 'public'
    title = _("Public access")
    description = _(
        "public_access_description",
        default=
        "Users will be granted the permissions you select without prior moderator approval. This is for public meetings."
    )

    def schema(self):
        return colander.Schema(
            title=_("Would you like to participate?"),
            description=_(
                "Clicking request access will grant you access right away!"))

    def handle_success(self, view, appstruct):
        roles = self.context.get_field_value('immediate_access_grant_roles')
        if not roles:
            roles = _DEFAULT_ROLES
        self.context.add_groups(view.request.authenticated_userid, roles)
        view.flash_messages.add(_("Access granted - welcome!"))
        return HTTPFound(location=view.request.resource_url(self.context))

    def config_schema(self):
        return ImmediateAPConfigSchema()
Пример #47
0
def password_node():
    return colander.SchemaNode(colander.String(),
                               validator=colander.All(password_validation, html_string_validator,),
                        widget=deform.widget.CheckedPasswordWidget(size=20),
                        title=_('Password'),
                        description = _(u"password_creation_tip",
                                        default = u"Use at least 6 chars. A good rule is to use long passwords that "
                                                  u"doesn't contain any personal information or things that someone else might guess."))
Пример #48
0
def recaptcha_node():
    return colander.SchemaNode(colander.String(),
                               #FIXME: write a good title and description here
                               title=_(u"Verify you are human"),
                               description = _(u"meeting_captcha_description",
                                               default=u"This is to prevent spambots from creating meetings"),
                               missing=u"",
                               widget=deferred_recaptcha_widget,)
Пример #49
0
def password_validation(node, value):
    """ check that password is
        - at least 6 chars and at most 100.
    """
    if len(value) < 6:
        raise colander.Invalid(node, _(u"Too short. At least 6 chars required."))
    if len(value) > 100:
        raise colander.Invalid(node, _(u"Less than 100 chars please."))
Пример #50
0
def public_description_node():
    return colander.SchemaNode(
        colander.String(),
        title = _(u"Public presentation"),
        description = _(u"meeting_public_description_description",
                        default=u"The public description is visible on the request access "
                            u"page and to not yet logged in visitors."),
        missing = u"",
        widget=deform.widget.RichTextWidget(),
        validator=richtext_validator,)
Пример #51
0
 def schema(self):
     #Just to make sure
     self.hashlist
     return colander.Schema(
         title=_("Would you like to participate?"),
         description=_(
             "Clicking request access will grant you access if your email address "
             "is allowed by the access policy."
         )
     )
Пример #52
0
 def manage_tickets(self):
     """ Handle and review tickets. """
     if self.request.method == 'POST':
         data = self.request.POST.dict_of_lists()
         if 'email' not in data:
             self.flash_messages.add(_("Nothing selected - nothing to do!"), type = "danger")
             return HTTPFound(location = self.request.url)
         if 'remove' in data:
             for email in data['email']:
                 del self.context.invite_tickets[email]
             self.flash_messages.add(_("Removed ${count} tickets", mapping = {'count': len(data['email'])}))
             return HTTPFound(location = self.request.url)
         if 'resend' in data:
             total = len(data['email'])
             if total > 1:
                 #bulk send
                 self.request.session['send_tickets.emails'] = data['email']
                 self.request.session['send_tickets.message'] = data['message'][0]
                 self.request.session.changed()
                 return HTTPFound(location = self.request.resource_url(self.context, 'send_tickets'))
             else:
                 resent = 0
                 aborted = 0
                 for email in data['email']:
                     ticket = self.context.invite_tickets[email]
                     if not ticket.closed:
                         send_invite_ticket(ticket, self.request, data['message'][0])
                         resent += 1
                     else:
                         aborted += 1
                 if not aborted:
                     msg = _(u"Resent ${count} successfully",
                             mapping = {'count': resent})
                 else:
                     msg = _(u"Resent ${count} of ${total}. ${aborted} were not sent since they're already claimed",
                             mapping = {'count': resent, 'total': total, 'aborted': aborted})
                 self.flash_messages.add(msg)
                 return HTTPFound(location = self.request.url)
     voteit_manage_tickets_js.need()
     #self.response['tabs'] = self.api.render_single_view_component(self.context, self.request, 'tabs', 'manage_tickets')
     closed = 0
     results = []
     never_invited = []
     for ticket in self.context.invite_tickets.values():
         results.append(ticket)
         if ticket.closed != None:
             closed += 1
         if len(ticket.sent_dates) == 0:
             never_invited.append(ticket.email)
     response = {}
     response['invite_tickets'] = results
     response['closed_count'] = closed
     response['never_invited'] = never_invited
     response['roles_dict'] = dict(security.MEETING_ROLES)
     return response
Пример #53
0
 def delete_success(self, appstruct):
     domains = appstruct['auth_domains']
     if domains:
         for domain in domains:
             del self.context.auth_domains[domain]
         msg = _(u"Removing information for: ${domains}",
                 mapping = {'domains': ", ".join(domains)})
         self.api.flash_messages.add(msg)
     else:
         self.api.flash_messages.add(_(u"Nothing updated"))
     return HTTPFound(location = self.request.resource_url(self.context))
Пример #54
0
 def __call__(self, node, value):
     if not NEW_USERID_PATTERN.match(value):
         msg = _('userid_char_error',
                 default=u"UserID must be 3-30 chars, start with lowercase a-z and only contain lowercase a-z, numbers, minus and underscore.")
         raise colander.Invalid(node, msg)
     
     root = find_root(self.context)
     if value in root.users:
         msg = _('already_registered_error',
                 default=u"UserID already registered. If it was registered by you, try to retrieve your password.")
         raise colander.Invalid(node, msg)
Пример #55
0
def no_html_validator(node, value):
    """ Checks that input doesn't contain html tags
    """
    tag_re = re.compile(r'<.*?>', re.S)
    comment_re = re.compile(r'<!--|-->')

    if comment_re.match(value):
        raise colander.Invalid(node, _("HTML comments not allowed."))

    if tag_re.match(value):
        raise colander.Invalid(node, _(u"HTML is not allowed."))
Пример #56
0
def description_node():
    return colander.SchemaNode(
        colander.String(),
        title = _(u"Participants description"),
        description = _(u"meeting_description_description",
                        default=u"This is only visible to participants, so don't put information on how to register here. "
                            u"Displayed on the first page of the meeting. You can include things "
                            u"like information about the meeting, how to contact the moderator and your logo."),
        missing = u"",
        widget=deform.widget.RichTextWidget(),
        validator=richtext_validator,)
Пример #57
0
    def __call__(self, form, value):
        email = value['email']
        token = self.context.invite_tickets.get(email)
        if not token:
            exc = colander.Invalid(form, 'Incorrect email')
            exc['token'] = _(u"Couldn't find any invitation for this email address.")
            raise exc

        if token.token != value['token']:
            exc = colander.Invalid(form, _(u"Email matches, but token doesn't"))
            exc['token'] = _(u"Check this field - token doesn't match")
            raise exc