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)
def get_read_count(request, meeting, type_name, userid=None): if userid is None: userid = request.authenticated_userid assert isinstance(userid, string_types) assert IMeeting.providedBy(meeting) rnc = request.registry.getMultiAdapter([meeting, request], IReadNamesCounter) return rnc.get_read_count(type_name, userid)
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)
def main(): parser = argparse.ArgumentParser() parser.add_argument("config_uri", help="Paster ini file to load settings from") parser.add_argument("path", help="from which path to clear likes (meeting or agenda item)") args = parser.parse_args() env = bootstrap(args.config_uri) root = env['root'] request = env['request'] context = traverse(root, args.path).get('context') if IMeeting.providedBy(context) or IAgendaItem.providedBy(context): print('Clearing likes on {}'.format(context.title)) path_query = query.Eq('path', args.path) cleared = False for type_name in ('Proposal', 'DiscussionPost'): count, docids = root.catalog.query(path_query & query.Eq('type_name', type_name)) response = input('Found {} {} on {}. Do you want to clear likes on these? (y/N) '.format( count, type_name, context.title).encode('utf8')) if response.lower() in ('y', 'yes', 'j', 'ja'): cleared = True for obj in request.resolve_docids(docids, perm=None): like = request.registry.getAdapter(obj, IUserTags, name='like') like.storage.clear() like._notify() if cleared: transaction.commit() env['closer']() else: print('Path does not match a meeting or agenda item')
def render_invite_ticket(ticket, request, message="", **kw): """ Render invite ticket email html. Uses ticket as a context. """ assert IInviteTicket.providedBy(ticket) #FIXME: Include meeting logo in mail? roles = dict(security.MEETING_ROLES) meeting = find_interface(ticket, IMeeting) root = find_root(meeting) assert IMeeting.providedBy(meeting) response = {} response['access_link'] = request.resource_url(meeting, 'ticket', query={ 'email': ticket.email, 'token': ticket.token }) response['message'] = message response['meeting'] = meeting response['context'] = ticket response['contact_mail'] = meeting.get_field_value('meeting_mail_address') response['sender_profile'] = root.users.get(ticket.sent_by) response['roles'] = [roles.get(x) for x in ticket.roles] return render('voteit.core:templates/email/invite_ticket_email.pt', response, request=request)
def evolve(root): """ Reorder all agenda items + make sure keys are within order attribute. """ if 'order' in root.catalog: del root.catalog['order'] for obj in root.values(): if IMeeting.providedBy(obj): _reorder_ais(obj)
def save_success(self, appstruct): from_meeting = self.request.root[appstruct['meeting_name']] assert IMeeting.providedBy(from_meeting) for (userid, roles) in from_meeting.local_roles.items(): self.context.local_roles.add(userid, roles, event=False) event_obj = ObjectUpdatedEvent(self.context, changed=['local_roles']) objectEventNotify(event_obj) return HTTPFound(location=self.request.resource_url(self.context))
def set_voter_role(self, userid, voter = False): assert IMeeting.providedBy(self.context) groups = self.context.local_roles.get(userid, ()) if voter: if security.ROLE_VOTER not in groups: self.context.local_roles.add(userid, security.ROLE_VOTER) else: if security.ROLE_VOTER in groups: self.context.local_roles.remove(userid, security.ROLE_VOTER)
def evolve(root): """ Remove old redis attrs """ for meeting in [x for x in root.values() if IMeeting.providedBy(x)]: if hasattr(meeting, '_read_names_counter'): delattr(meeting, '_read_names_counter') for ai in [x for x in meeting.values() if IAgendaItem.providedBy(x)]: if hasattr(ai, '_read_names'): delattr(ai, '_read_names')
def proposals(): for name, obj in self.context.items(): if not IMeeting.providedBy(obj): continue settings = dict(IEffectSettings(obj)) effect_actors = set( str.lower() for str in settings.get('effect_actors', ())) result, docids = self.request.root.catalog.query( Eq('path', self.request.resource_path(obj)) & Eq('type_name', 'AgendaItem')) voter_count = self.get_voter_count(obj) for ai in self.request.resolve_docids(docids, perm=None): result, polldocids = self.request.root.catalog.query( Eq('path', self.request.resource_path(ai)) & Eq('type_name', 'Poll')) polls = [ poll for poll in self.request.resolve_docids(polldocids, perm=None) if poll.poll_plugin_name == 'sorted_schulze' and poll.poll_result ] result, pdocids = self.request.root.catalog.query( Eq('path', self.request.resource_path(ai)) & Eq('type_name', 'Proposal')) for prop in self.request.resolve_docids(pdocids, perm=None): tags = set(prop.tags) effect_tags = effect_actors.intersection(tags) other_tags = tags.difference(effect_tags) position = voters = '' for poll in polls: if poll.poll_result: winners = poll.poll_result['winners'] if prop.uid in winners: position = '{}/{}'.format( winners.index(prop.uid) + 1, len(winners)) voters = '{}/{}'.format( len(poll.ballots), voter_count) creator = self.users[prop.creator[0]] yield ([ obj.title, ai.title, prop.text, creator.title, self.kommun_dict.get(creator.kommun), ' '.join( self.org_dict.get(org) for org in creator.organisation), self.request.dt_handler.format_dt(prop.created), ' '.join(effect_tags), ' '.join(other_tags), position, voters, ])
def get_view_meeting_userids(object, default): """ Userids that are allowed to view a meeting. Only index meeting contexts. """ if not IMeeting.providedBy(object): return default try: userids = find_authorized_userids(object, [VIEW]) return userids and userids or default except ComponentLookupError: # pragma : no cover #This is to avoid having security fixture for each catalog test. return default
def get_view_meeting_userids(obj, default): """ Userids that are allowed to view a meeting. Only index meeting contexts. """ if not IMeeting.providedBy(obj): return default try: userids = find_authorized_userids(obj, [VIEW]) return userids and userids or default except ComponentLookupError: # pragma : no cover #This is to avoid having security fixture for each catalog test. return default
def evolve(root): """ Swap the existing Agenda portets for the fixed agenda portlet type. """ from arche.portlets import get_portlet_manager for obj in root.values(): if IMeeting.providedBy(obj): manager = get_portlet_manager(obj) for portlet in manager.get_portlets('left', 'agenda'): manager.remove('left', portlet.uid) if not manager.get_portlets('left_fixed', 'agenda_fixed'): manager.add('left_fixed', 'agenda_fixed')
def deferred_roles_widget(node, kw): """ Only handles role-like groups with prefix 'role:'""" context = kw['context'] if ISiteRoot.providedBy(context): roles_choices = ROOT_ROLES elif IMeeting.providedBy(context): roles_choices = MEETING_ROLES else: TypeError("Wrong context for deferred_roles_widget - must be IMeeting or ISiteRoot.") return deform.widget.CheckboxChoiceWidget(values=roles_choices, missing=colander.null,)
def __call__(self): if not self.request.authenticated_userid: raise HTTPUnauthorized("Must login first") show_roles = IMeeting.providedBy(self.context) local_roles = [] if show_roles: for role in self.context.local_roles.get(self.request.authenticated_userid, ()): local_roles.append(self.request.registry.roles[role]) return {'show_roles': show_roles, 'local_roles': local_roles, 'role_icons': ROLE_ICONS}
def list_meetings(self): """ Return a list of all meetings. See _meeting for properties """ result = [] for meeting in self.context.values(): if not IMeeting.providedBy(meeting): continue result.append(self._meeting(meeting)) return result
def test_import_data(self): from voteit.core.models.interfaces import IMeeting self.config.include('voteit.core.models.catalog') self.config.include('voteit.core.models.user_tags') root = dummy_zodb_root(self.config) self._meeting_fixture(root) filedata = self._make_filedata_from_context(root['meeting']) ei = self._cut(root) ei.import_data(root, 'new_meeting', filedata) self.failUnless('new_meeting' in root) self.failUnless(IMeeting.providedBy(root['new_meeting']))
def find_ais(root, found): for m in root.values(): if not IMeeting.providedBy(m): continue print "Processing: /%s" % m.__name__ for ai in m.values(): if not IAgendaItem.providedBy(ai) or ai.uid not in found: continue mark_read(ai, found.pop(ai.uid)) if not found: return
def check_ongoing_poll(context): """ Check if a poll is ongoing, return number of ongoing polls """ meeting = find_interface(context, IMeeting) assert IMeeting.providedBy(meeting) root = find_root(meeting) query = ( Eq("type_name", "Poll") & Eq("path", resource_path(meeting)) & Eq("workflow_state", "ongoing") ) res = root.catalog.query(query)[0] return res.total
def save_success(self, appstruct): from_meeting = self.request.root[appstruct['meeting_name']] reset_wf = appstruct['all_props_published'] only_copy_prop_states = appstruct['only_copy_prop_states'] copy_types = appstruct['copy_types'] assert IMeeting.providedBy(from_meeting) counter = 0 for ai in from_meeting.values(): if not IAgendaItem.providedBy(ai): continue counter += copy_ai(self.context, ai, reset_wf=reset_wf, only_copy_prop_states=only_copy_prop_states, copy_types=copy_types) self.flash_messages.add(_("Copied ${num} objects", mapping={'num': counter})) return HTTPFound(location=self.request.resource_url(self.context))
def evolve(root): from voteit.core.models.interfaces import IMeeting from BTrees.OOBTree import OOSet from sfs_ga.interfaces import IMeetingDelegations for obj in root.values(): if not IMeeting.providedBy(obj): continue delegations = IMeetingDelegations(obj) for delegation in delegations.values(): for attr in ('pn_leaders', 'pn_members'): if not hasattr(delegation, attr): setattr(delegation, attr, OOSet())
def __call__(self, node, value): if not has_permission(MANAGE_GROUPS, self.context, self.request): raise colander.Invalid(node, _(u"You can't change groups in this context")) roles = [] if ISiteRoot.providedBy(self.context): roles.extend([x[0] for x in ROOT_ROLES]) elif IMeeting.providedBy(self.context): roles.extend([x[0] for x in MEETING_ROLES]) for v in value: if v not in roles: raise colander.Invalid(node, _(u"wrong_context_for_roles_error", default = u"Group ${group} can't be assigned in this context", mapping = {'group': v}))
def __call__(self): if not self.request.authenticated_userid: raise HTTPUnauthorized("Must login first") show_roles = IMeeting.providedBy(self.context) local_roles = [] if show_roles: for role in self.context.local_roles.get( self.request.authenticated_userid, ()): local_roles.append(self.request.registry.roles[role]) return { 'show_roles': show_roles, 'local_roles': local_roles, 'role_icons': ROLE_ICONS }
def deferred_roles_widget(node, kw): """ Only handles role-like groups with prefix 'role:'""" context = kw['context'] if ISiteRoot.providedBy(context): roles_choices = ROOT_ROLES elif IMeeting.providedBy(context): roles_choices = MEETING_ROLES else: TypeError( "Wrong context for deferred_roles_widget - must be IMeeting or ISiteRoot." ) return deform.widget.CheckboxChoiceWidget( values=roles_choices, missing=colander.null, )
def render_claimed_ticket_notification(ticket, request, message = "", user = None, **kw): assert IInviteTicket.providedBy(ticket) roles = dict(security.MEETING_ROLES) meeting = find_interface(ticket, IMeeting) root = find_root(meeting) assert IMeeting.providedBy(meeting) response = {} response['message'] = message response['meeting'] = meeting response['context'] = ticket response['contact_mail'] = meeting.get_field_value('meeting_mail_address') response['sender_profile'] = root.users.get(ticket.sent_by) response['roles'] = [roles.get(x) for x in ticket.roles] response['user'] = user return render('voteit.core:templates/email/claimed_ticket_email.pt', response, request = request)
def evolve(root): """ Migrate all old unread markers that were stored in the catalog. """ if '__name__' not in root.catalog: raise KeyError("__name__ index must be in catalog before this migration." "Run catalog update first.") #We only need to care about meetings for m in root.values(): if not IMeeting.providedBy(m): continue users = set(root.local_roles) | set(m.local_roles) for ai in m.values(): if not IAgendaItem.providedBy(ai): continue convert_ai(ai, users) print "Processed '%s' for %s users" % (m.__name__, len(users))
def proposals(): for name, obj in self.context.items(): if not IMeeting.providedBy(obj): continue settings = dict(IEffectSettings(obj)) effect_actors = set(str.lower() for str in settings.get('effect_actors', ())) result, docids = self.request.root.catalog.query( Eq('path', self.request.resource_path(obj)) & Eq('type_name', 'AgendaItem') ) voter_count = self.get_voter_count(obj) for ai in self.request.resolve_docids(docids, perm=None): result, polldocids = self.request.root.catalog.query( Eq('path', self.request.resource_path(ai)) & Eq('type_name', 'Poll') ) polls = [poll for poll in self.request.resolve_docids(polldocids, perm=None) if poll.poll_plugin_name=='sorted_schulze' and poll.poll_result] result, pdocids = self.request.root.catalog.query( Eq('path', self.request.resource_path(ai)) & Eq('type_name', 'Proposal') ) for prop in self.request.resolve_docids(pdocids, perm=None): tags = set(prop.tags) effect_tags = effect_actors.intersection(tags) other_tags = tags.difference(effect_tags) position = voters = '' for poll in polls: if poll.poll_result: winners = poll.poll_result['winners'] if prop.uid in winners: position = '{}/{}'.format(winners.index(prop.uid)+1, len(winners)) voters = '{}/{}'.format(len(poll.ballots), voter_count) creator = self.users[prop.creator[0]] yield([ obj.title, ai.title, prop.text, creator.title, self.kommun_dict.get(creator.kommun), ' '.join(self.org_dict.get(org) for org in creator.organisation), self.request.dt_handler.format_dt(prop.created), ' '.join(effect_tags), ' '.join(other_tags), position, voters, ])
def __call__(self, node, value): if not has_permission(MANAGE_GROUPS, self.context, self.request): raise colander.Invalid( node, _(u"You can't change groups in this context")) roles = [] if ISiteRoot.providedBy(self.context): roles.extend([x[0] for x in ROOT_ROLES]) elif IMeeting.providedBy(self.context): roles.extend([x[0] for x in MEETING_ROLES]) for v in value: if v not in roles: raise colander.Invalid( node, _(u"wrong_context_for_roles_error", default= u"Group ${group} can't be assigned in this context", mapping={'group': v}))
def evolve(root): """ Renamed attributes + move values contained in meetings main data storage.""" _marker = object() keys_to_migrate = ( 'speaker_list_plugin', 'enable_voteit_debate', 'speaker_list_count', 'safe_positions', 'reload_manager_interface', ) keys_to_delete = ( 'max_times_in_list', 'reload_speaker_in_queue', 'reload_speaker_not_in_queue', ) for obj in root.values(): if not IMeeting.providedBy(obj): continue if hasattr(obj, '__active_speaker_list__'): delattr(obj, '__active_speaker_list__') if hasattr(obj, '__speaker_lists__'): obj._speaker_lists = obj.__speaker_lists__ delattr(obj, '__speaker_lists__') #Simply remove any key with enabled, since we want moderators to revisit this obj.field_storage.pop('enable_voteit_debate', None) #Store old vals in new adapter settings = ISpeakerListSettings(obj) for k in keys_to_migrate: val = obj.field_storage.pop(k, _marker) if val != _marker: settings[k] = val #Remove keys for k in keys_to_delete: obj.field_storage.pop(k, None) #Add new defaults settings['user_update_interval'] = 5 #The speakers attribute contained the speakers before. It should be labeled data now for sl in getattr(obj, '_speaker_lists', {}).values(): speakers = [] if hasattr(sl, 'speakers'): speakers.extend(sl.speakers) delattr(sl, 'speakers') sl.data = speakers
def render_invite_ticket(ticket, request, message = "", **kw): """ Render invite ticket email html. Uses ticket as a context. """ assert IInviteTicket.providedBy(ticket) #FIXME: Include meeting logo in mail? roles = dict(security.MEETING_ROLES) meeting = find_interface(ticket, IMeeting) root = find_root(meeting) assert IMeeting.providedBy(meeting) response = {} response['access_link'] = request.resource_url(meeting, 'ticket', query = {'email': ticket.email, 'token': ticket.token}) response['message'] = message response['meeting'] = meeting response['context'] = ticket response['contact_mail'] = meeting.get_field_value('meeting_mail_address') response['sender_profile'] = root.users.get(ticket.sent_by) response['roles'] = [roles.get(x) for x in ticket.roles] return render('voteit.core:templates/email/invite_ticket_email.pt', response, request = request)
def evolve(root): """ Change all poll descriptions to plaintext. """ from arche.interfaces import ICataloger from arche.utils import find_all_db_objects from webhelpers.html.render import sanitize #Move description to body root.body = root.description root.description = "" ICataloger(root).index_object() for obj in find_all_db_objects(root): if IPoll.providedBy(obj): #Turn description into plaintext obj.description = sanitize(obj.description) ICataloger(obj).index_object() elif IMeeting.providedBy(obj): #Move HTML field content to body obj.body = obj.description obj.description = "" ICataloger(obj).index_object()
def render_claimed_ticket_notification(ticket, request, message="", user=None, **kw): assert IInviteTicket.providedBy(ticket) roles = dict(security.MEETING_ROLES) meeting = find_interface(ticket, IMeeting) root = find_root(meeting) assert IMeeting.providedBy(meeting) response = {} response['message'] = message response['meeting'] = meeting response['context'] = ticket response['contact_mail'] = meeting.get_field_value('meeting_mail_address') response['sender_profile'] = root.users.get(ticket.sent_by) response['roles'] = [roles.get(x) for x in ticket.roles] response['user'] = user return render('voteit.core:templates/email/claimed_ticket_email.pt', response, request=request)
def evolve(root): """ Change the Proposals so title and text are separated. Old proposals used to have only a title field that could be really long. Also add portlets to all meetings. """ from arche.utils import find_all_db_objects from arche.portlets import get_portlet_manager from voteit.core.models.meeting import add_default_portlets_meeting manager = get_portlet_manager(root) if not manager.get_portlets('right', 'meeting_list'): manager.add('right', 'meeting_list') for obj in find_all_db_objects(root): if IProposal.providedBy(obj): try: obj.text = obj.field_storage.pop('title') except KeyError: pass elif IMeeting.providedBy(obj): add_default_portlets_meeting(obj)
def main(): parser = argparse.ArgumentParser() parser.add_argument("config_uri", help="Paster ini file to load settings from") parser.add_argument( "path", help="from which path to clear likes (meeting or agenda item)") args = parser.parse_args() env = bootstrap(args.config_uri) root = env['root'] request = env['request'] context = traverse(root, args.path).get('context') if IMeeting.providedBy(context) or IAgendaItem.providedBy(context): print('Clearing likes on {}'.format(context.title)) path_query = query.Eq('path', args.path) cleared = False for type_name in ('Proposal', 'DiscussionPost'): count, docids = root.catalog.query( path_query & query.Eq('type_name', type_name)) response = input( 'Found {} {} on {}. Do you want to clear likes on these? (y/N) ' .format(count, type_name, context.title).encode('utf8')) if response.lower() in ('y', 'yes', 'j', 'ja'): cleared = True for obj in request.resolve_docids(docids, perm=None): like = request.registry.getAdapter(obj, IUserTags, name='like') like.storage.clear() like._notify() if cleared: transaction.commit() env['closer']() else: print('Path does not match a meeting or agenda item')
def render_claimed_ticket_notification(ticket, request, message="", user=None, **kw): assert IInviteTicket.providedBy(ticket) roles = dict(security.MEETING_ROLES) meeting = find_interface(ticket, IMeeting) root = find_root(meeting) assert IMeeting.providedBy(meeting) response = { 'message': message, 'meeting': meeting, 'context': ticket, 'contact_mail': meeting.get_field_value('meeting_mail_address'), 'sender_profile': root.users.get(ticket.sent_by), 'roles': [roles.get(x) for x in ticket.roles], 'user': user } response['invite_common'] = render_invite_common(response, request) return render('voteit.core:templates/email/claimed_ticket_email.pt', response, request=request)
def evolve(root): """ Migrate unread to redis """ try: from fakeredis import FakeStrictRedis maybe_fr = True except ImportError: maybe_fr = False request = get_current_request() if maybe_fr: if isinstance(request.redis_conn, FakeStrictRedis): raise Exception( "Your redis connection is a FakeRedis instance. " "All migrated data would be thrown away! " "Please set voteit.redis_url in your paster.ini file.") for meeting in [x for x in root.values() if IMeeting.providedBy(x)]: if hasattr(meeting, '_read_names_counter'): delattr(meeting, '_read_names_counter') for ai in [x for x in meeting.values() if IAgendaItem.providedBy(x)]: if not hasattr(ai, '_read_names'): continue read_names = request.get_read_names(ai) for (userid, names) in ai._read_names.items(): read_names.mark_read(names, userid)
def __init__(self, context): assert IMeeting.providedBy(context) self.context = context
def add_gender_in_meeting(schema, event): context = getattr(event, 'context', None) if not IMeeting.providedBy(context): return if context.type_name == 'Meeting' and context.access_policy == ParticipantNumberAPWithGender.name: add_gender_in_schema(schema, event)
def __init__(self, userid, meeting): self.userid = userid assert IMeeting.providedBy(meeting) self.meeting = meeting
def moderator_context_delete(context, request, va, **kw): """ This is already a part of the Arches context menu, so it shouldn't be shown in meetings or agenda items. """ if IAgendaItem.providedBy(context) or IMeeting.providedBy(context): return return moderator_context_action(context, request, va, **kw)
def __init__(self, node, kw): self.context = kw['context'] self.request = kw['request'] assert IMeeting.providedBy( self.context), "context must be a meeting object, got %r" % self.context
def deferred_single_delegation_validator(node, kw): """ Check both GlobalExistingUserId and SingleDelegationValidator""" context = kw['context'] request = kw['request'] assert IMeeting.providedBy(context) return SingleDelegationValidator(context, request)
def __init__(self, context, request): assert IMeeting.providedBy(context), "context must be a meeting object, got %r" % context self.context = context self.request = request