def assign(self, request): """ Assign multiple agenda items to a new parent item. Send POST {... see schema ...} to assign the new parent. This aslo checks the parent field to prevent hierarchical loops. """ schema = { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Agenda items assign new parent schema", "description": "An object containing an array of agenda item ids and the new parent id the items should be assigned to.", "type": "object", "propterties": { "items": { "description": "An array of agenda item ids where the items should be assigned to the new parent id.", "type": "array", "items": { "type": "integer" }, "minItems": 1, "uniqueItems": True, }, "parent_id": { "description": "The agenda item id of the new parent item.", "type": "integer", }, }, "required": ["items", "parent_id"], } # Validate request data. try: jsonschema.validate(request.data, schema) except jsonschema.ValidationError as err: raise ValidationError({"detail": str(err)}) # Check parent item try: parent = Item.objects.get(pk=request.data["parent_id"]) except Item.DoesNotExist: raise ValidationError({ "detail": f"Parent item {request.data['parent_id']} does not exist" }) # Collect ancestors ancestors = [parent.pk] grandparent = parent.parent while grandparent is not None: ancestors.append(grandparent.pk) grandparent = grandparent.parent # First validate all items before changeing them. items = [] for item_id in request.data["items"]: # Prevent hierarchical loops. if item_id in ancestors: raise ValidationError({ "detail": f"Assigning item {item_id} to one of its children is not possible." }) # Check every item try: items.append(Item.objects.get(pk=item_id)) except Item.DoesNotExist: raise ValidationError( {"detail": f"Item {item_id} does not exist"}) # OK, assign new parents. for item in items: # Assign new parent. item.parent = parent item.save(skip_autoupdate=True) # Now inform all clients. inform_changed_data(items) # Send response. return Response( {"detail": f"{len(items)} items successfully assigned."})
def reset(self, request, pk): poll = self.get_object() poll.reset() self.extend_history_information(["Voting reset"]) return Response()
def refresh(self, request, pk): poll = self.get_object() inform_changed_data(poll) inform_changed_data(poll.get_options()) inform_changed_data(poll.get_votes()) return Response()
def create(self, request, *args, **kwargs): """ Customized view endpoint to upload a new file. """ # The form data may send the groups_id if isinstance(request.data, QueryDict): request.data._mutable = True self.convert_access_groups(request) is_directory = bool(request.data.get("is_directory", False)) mediafile = request.data.get("mediafile") # Check, that it is either a file or a directory if is_directory and mediafile: raise ValidationError( {"detail": "Either create a path or a file, but not both"} ) if not mediafile and not is_directory: raise ValidationError({"detail": "You forgot to provide a file."}) if mediafile: if mediafile.size > max_upload_size: max_size_for_humans = bytes_to_human(max_upload_size) raise ValidationError( {"detail": f"The maximum upload file size is {max_size_for_humans}"} ) # set original filename request.data["original_filename"] = mediafile.name # Remove mediafile from request.data, we will put it into the storage ourself. request.data.pop("mediafile", None) # Create mediafile serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) db_mediafile = serializer.save() # Set filesize, mimetype and check for pdfs. if mediafile: db_mediafile.filesize = bytes_to_human(mediafile.size) db_mediafile.mimetype = mediafile.content_type if db_mediafile.mimetype == "application/pdf": db_mediafile.pdf_information = get_pdf_information(mediafile) else: db_mediafile.pdf_information = {} db_mediafile.save() # Custom modifications for the database storage: Set original filename here and # insert the file into the foreing storage if use_mediafile_database: with connections["mediafiles"].cursor() as cursor: cursor.execute( "INSERT INTO mediafile_data (id, data, mimetype) VALUES (%s, %s, %s)", [ db_mediafile.id, mediafile.open().read(), mediafile.content_type, ], ) else: db_mediafile.mediafile = mediafile db_mediafile.save() return Response(data={"id": db_mediafile.id}, status=status.HTTP_201_CREATED)
def start_voting(self, request, model): vc = self.get_object() poll, poll_id = self.get_request_object(request, model) # get voting principle and type from motion or assignment principle = None voting_type = None # Get candidate name (if is an election with one candidate only) candidate_str = '' # projector message and images projector_yes = '<button type="button" class="btn btn-default btn-voting-sm btn-yes"> \ <i class="fa fa-thumbs-o-up fa-2x"></i></button>' projector_no = '<button type="button" class="btn btn-default btn-voting-sm btn-no"> \ <i class="fa fa-thumbs-o-down fa-2x"></i></button>' projector_abstain = '<button type="button" class="btn btn-default btn-voting-sm btn-abstain"> \ <i class="fa fa-circle-o fa-2x"></i></button>' if type(poll) == MotionPoll: projector_message = _(config['voting_start_prompt_motions']) principle = VotingPrinciple.get(motion=poll.motion) try: voting_type = MotionPollType.objects.get(poll=poll).type except MotionPollType.DoesNotExist: voting_type = config['voting_default_voting_type'] if voting_type.startswith('votecollector'): if 'Interact' in vc.device_status: projector_abstain = '2 = ' elif 'Reply' in vc.device_status: projector_yes = '1 = ' projector_no = '2 = ' projector_abstain = '3 = ' projector_message += '<br>' + \ '<span class="nobr">' + projector_yes + _('Yes') + '</span> ' + \ '<span class="nobr spacer-left">' + projector_no + _('No') + '</span> ' + \ '<span class="nobr spacer-left">' + projector_abstain + _('Abstain') + '</span>' votecollector_mode = 'YesNoAbstain' votecollector_options = None votecollector_resource = '/vote/' ballot = MotionBallot(poll, principle) elif type(poll) == AssignmentPoll: projector_message = _(config['voting_start_prompt_assignments']) principle = VotingPrinciple.get(assignment=poll.assignment) try: voting_type = AssignmentPollType.objects.get(poll=poll).type except AssignmentPollType.DoesNotExist: voting_type = config['voting_default_voting_type'] options = AssignmentOption.objects.filter( poll=poll).order_by('weight') # check, if the pollmethod is supported by the votecollector # If so, calculate the projector message for the voting prompt if voting_type.startswith('votecollector'): if poll.pollmethod == 'yn' or (poll.pollmethod == 'yna' and options.count() is not 1): raise ValidationError({ 'detail': 'The votecollector does not support the pollmethod {} (with {} candidates).' .format(poll.pollmethod, options.count()) }) if 'Interact' in vc.device_status: projector_abstain = '2 = ' elif 'Reply' in vc.device_status: projector_yes = '1 = ' projector_no = '2 = ' projector_abstain = '3 = ' # calculate the candidate string if poll.pollmethod == 'yna': candidate = str(options.all()[0].candidate) projector_message += '<br>' + \ '<span class="nobr">' + projector_yes + _('Yes') + '</span> ' + \ '<span class="nobr">' + projector_no + _('No') + '</span> ' + \ '<span class="nobr">' + projector_abstain + _('Abstain') + '</span>' + \ '<div class="spacer candidate">' + candidate + '</div>' votecollector_mode = 'YesNoAbstain' votecollector_options = None votecollector_resource = '/vote/' else: # votes projector_message += '<div><ul class="columns" data-columns="3">' for index, option in enumerate(options.all()): projector_message += \ '<li><span class="key">' + str(index + 1) + '</span>' + \ '<span class="candidate">' + str(option.candidate) + '</span></li>' projector_message += '<li><span class="key">0</span><span class="candidate">' + \ _('Abstain') + '</span></li></ul></div>' if options.count() < 10: votecollector_mode = 'SingleDigit' votecollector_options = '10' # unlock all keys 0 to 9 else: votecollector_mode = 'MultiDigit' votecollector_options = '2' # limit votes to 2 digits, only applies to simulator votecollector_resource = '/candidate/' ballot = AssignmentBallot(poll) else: raise ValidationError( {'detail': 'Not supported type {}.'.format(type(poll))}) # Delete all old votes and create absentee ballots ballot.delete_ballots() absentee_ballots_created = 0 if config['voting_enable_proxies']: absentee_ballots_created = ballot.create_absentee_ballots() if voting_type.startswith('votecollector'): if not config['voting_enable_votecollector']: raise ValidationError( {'detail': 'The VoteCollector is not enabled'}) # Stop any active voting no matter what mode. self.force_stop_active_votecollector() url = rpc.get_callback_url(request) + votecollector_resource url += '%s/' % poll_id try: vc.votes_count, vc.device_status = rpc.start_voting( votecollector_mode, url, votecollector_options) except rpc.VoteCollectorError as e: raise ValidationError({'detail': e.value}) # Limit voters count to length of admitted delegates list. admitted_count, admitted_delegates = get_admitted_delegates( principle, keypad=True) if not voting_type == 'votecollector_anonymous': vc.votes_count = admitted_count elif voting_type == 'named_electronic': # Limit voters count to length of admitted delegates list. vc.votes_count, admitted_delegates = get_admitted_delegates( principle) else: # 'token_based_electronic' admitted_delegates = None vc.votes_count = 0 # We do not know, how many votes will come.. vc.voting_mode = model.__name__ vc.voting_target = poll_id vc.votes_received = absentee_ballots_created vc.is_voting = True vc.principle = principle vc.save() # Update AuthorizedVoter object if type(poll) == MotionPoll: AuthorizedVoters.set_voting(admitted_delegates, voting_type, motion_poll=poll) else: AuthorizedVoters.set_voting(admitted_delegates, voting_type, assignment_poll=poll) # Add projector message # search projector with an projected "related item". This item might be the motion/assignment # itself or the voting/(motion/assignment)-poll slide. If none was found, use the default projector if type(poll) == MotionPoll: objectElementName = 'motions/motion' objectElementId = poll.motion.id pollElementName = 'voting/motion-poll' else: objectElementName = 'assignments/assignment' objectElementId = poll.assignment.id pollElementName = 'voting/assignment-poll' projector = None found_projector = False for p in Projector.objects.all(): if found_projector: break for uuid, element in p.elements.items(): if found_projector: break if element['name'] == objectElementName and element[ 'id'] == objectElementId: projector = p found_projector = True if element['name'] == pollElementName and element[ 'id'] == poll_id: projector = p found_projector = True if not found_projector: projector = Projector.objects.get(id=1) projector.config[self.prompt_key] = { 'name': 'voting/prompt', 'message': projector_message, 'stable': True } # Auto start countdown and add it to projector. if config['voting_auto_countdown']: # Use countdown 2 since 1 is reserved for speakers list. countdown, created = Countdown.objects.get_or_create( pk=2, description=_('Poll is open'), defaults={ 'default_time': config['projector_default_countdown'], 'countdown_time': config['projector_default_countdown'] }) if not created: countdown.control(action='reset') countdown.control(action='start') projector.config[self.countdown_key] = { 'name': 'core/countdown', 'id': 2, 'stable': True } projector.save(information={'voting_prompt': True}) return Response()
def manage_speaker(self, request, pk=None): """ Special view endpoint to add users to the list of speakers or remove them. Send POST {'user': <user_id>} to add a new speaker. Omit data to add yourself. Send DELETE {'speaker': <speaker_id>} or DELETE {'speaker': [<speaker_id>, <speaker_id>, ...]} to remove one or more speakers from the list of speakers. Omit data to remove yourself. Send PATCH {'user': <user_id>, 'marked': <bool>} to mark the speaker. Checks also whether the requesting user can do this. He needs at least the permissions 'agenda.can_see' (see self.check_view_permissions()). In case of adding himself the permission 'agenda.can_be_speaker' is required. In case of adding someone else the permission 'agenda.can_manage' is required. In case of removing someone else 'agenda.can_manage' is required. In case of removing himself no other permission is required. """ # Retrieve item. item = self.get_object() if request.method == 'POST': # Retrieve user_id user_id = request.data.get('user') # Check permissions and other conditions. Get user instance. if user_id is None: # Add oneself if not has_perm(self.request.user, 'agenda.can_be_speaker'): self.permission_denied(request) if item.speaker_list_closed: raise ValidationError( {'detail': _('The list of speakers is closed.')}) user = self.request.user else: # Add someone else. if not has_perm(self.request.user, 'agenda.can_manage_list_of_speakers'): self.permission_denied(request) try: user = get_user_model().objects.get(pk=int(user_id)) except (ValueError, get_user_model().DoesNotExist): raise ValidationError( {'detail': _('User does not exist.')}) # Try to add the user. This ensurse that a user is not twice in the # list of coming speakers. try: Speaker.objects.add(user, item) except OpenSlidesError as e: raise ValidationError({'detail': str(e)}) message = _( 'User %s was successfully added to the list of speakers.' ) % user # Send new speaker via autoupdate because users without permission # to see users may not have it but can get it now. inform_changed_data([user]) # Toggle 'marked' for the speaker elif request.method == 'PATCH': # Check permissions if not has_perm(self.request.user, 'agenda.can_manage_list_of_speakers'): self.permission_denied(request) # Retrieve user_id user_id = request.data.get('user') try: user = get_user_model().objects.get(pk=int(user_id)) except (ValueError, get_user_model().DoesNotExist): raise ValidationError({'detail': _('User does not exist.')}) marked = request.data.get('marked') if not isinstance(marked, bool): raise ValidationError( {'detail': _('Marked has to be a bool.')}) queryset = Speaker.objects.filter(item=item, user=user) try: # We assume that there aren't multiple entries because this # is forbidden by the Manager's add method. We assume that # there is only one speaker instance or none. speaker = queryset.get() except Speaker.DoesNotExist: raise ValidationError( {'detail': _('The user is not in the list of speakers.')}) else: speaker.marked = marked speaker.save() if speaker.marked: message = _('You are successfully marked the speaker.') else: message = _('You are successfully unmarked the speaker.') else: # request.method == 'DELETE' speaker_ids = request.data.get('speaker') # Check permissions and other conditions. Get speaker instance. if speaker_ids is None: # Remove oneself queryset = Speaker.objects.filter( item=item, user=self.request.user).exclude(weight=None) try: # We assume that there aren't multiple entries because this # is forbidden by the Manager's add method. We assume that # there is only one speaker instance or none. speaker = queryset.get() except Speaker.DoesNotExist: raise ValidationError( {'detail': _('You are not on the list of speakers.')}) else: speaker.delete() message = _( 'You are successfully removed from the list of speakers.' ) else: # Remove someone else. if not has_perm(self.request.user, 'agenda.can_manage_list_of_speakers'): self.permission_denied(request) if type(speaker_ids) is int: speaker_ids = [speaker_ids] deleted_speaker_count = 0 for speaker_id in speaker_ids: try: speaker = Speaker.objects.get(pk=int(speaker_id)) except (ValueError, Speaker.DoesNotExist): pass else: speaker.delete(skip_autoupdate=True) deleted_speaker_name = speaker deleted_speaker_count += 1 # send autoupdate if speakers are deleted if deleted_speaker_count > 0: inform_changed_data(item) if deleted_speaker_count > 1: message = str(deleted_speaker_count) + ' ' + _( 'speakers have been removed from the list of speakers.' ) elif deleted_speaker_count == 1: message = _( 'User %s has been removed from the list of speakers.' ) % deleted_speaker_name else: message = _( 'No speakers have been removed from the list of speakers.' ) # Initiate response. return Response({'detail': message})
def move(self, request): """ { ids: [<id>, <id>, ...], directory_id: <id> } Move <ids> to the given directory_id. This will raise an error, if the move would be recursive. """ # Validate data: if not isinstance(request.data, dict): raise ValidationError({"detail": "The data must be a dict"}) ids = request.data.get("ids") if not isinstance(ids, list): raise ValidationError({"detail": "The ids must be a list"}) for id in ids: if not isinstance(id, int): raise ValidationError({"detail": "All ids must be an int"}) directory_id = request.data.get("directory_id") if directory_id is not None and not isinstance(directory_id, int): raise ValidationError({"detail": "The directory_id must be an int"}) if directory_id is None: directory = None else: try: directory = Mediafile.objects.get(pk=directory_id, is_directory=True) except Mediafile.DoesNotExist: raise ValidationError({"detail": "The directory does not exist"}) ids_set = set(ids) # keep them in a set for fast lookup ids = list(ids_set) # make ids unique mediafiles = [] for id in ids: try: mediafiles.append(Mediafile.objects.get(pk=id)) except Mediafile.DoesNotExist: raise ValidationError( {"detail": "The mediafile with id {0} does not exist", "args": [id]} ) # Search for valid parents (None is not included, but also safe!) if directory is not None: valid_parent_ids = set() queue = list(Mediafile.objects.filter(parent=None, is_directory=True)) for mediafile in queue: if mediafile.pk in ids_set: continue # not valid, because this is in the input data valid_parent_ids.add(mediafile.pk) queue.extend( list(Mediafile.objects.filter(parent=mediafile, is_directory=True)) ) if directory_id not in valid_parent_ids: raise ValidationError({"detail": "The directory is not valid"}) # Ok, update all mediafiles with watch_and_update_configs(): for mediafile in mediafiles: mediafile.parent = directory mediafile.save(skip_autoupdate=True) if directory is None: inform_changed_data(Mediafile.objects.all()) else: inform_changed_data(directory.get_children_deep()) return Response()
def delete_all_speakers(self, request): Speaker.objects.all().delete() inform_changed_data(ListOfSpeakers.objects.all()) return Response()
def manage_speaker(self, request, pk=None): """ Special view endpoint to add users to the list of speakers or remove them. Send POST {'user': <user_id>} to add a new speaker. Send POST {'user': <user_id>, 'point_of_order': True } to add a point of order to the list of speakers. Omit data to add yourself. Send DELETE {'speaker': <speaker_id>} or DELETE {'speaker': [<speaker_id>, <speaker_id>, ...]} to remove one or more speakers from the list of speakers. Omit data to remove yourself. Send PATCH {'user': <user_id>, 'marked': <bool>} to mark the speaker. Checks also whether the requesting user can do this. He needs at least the permissions 'agenda.can_see_list_of_speakers' (see self.check_view_permissions()). In case of adding himself the permission 'agenda.can_be_speaker' is required. In case of adding or removing someone else the permission 'agenda.can_manage_list_of_speakers' is required. In case of removing himself no other permission is required. """ # Retrieve list of speakers. list_of_speakers = self.get_object() if request.method == "POST": # Add new speaker # Retrieve user_id user_id = request.data.get("user") point_of_order = request.data.get("point_of_order") or False if not isinstance(point_of_order, bool): raise ValidationError( {"detail": "point_of_order has to be a bool."}) # Check permissions and other conditions. Get user instance. if user_id is None: # Add oneself if not has_perm(self.request.user, "agenda.can_be_speaker"): self.permission_denied(request) # even if the list is closed, point of order has to be accepted if not point_of_order and list_of_speakers.closed: raise ValidationError( {"detail": "The list of speakers is closed."}) user = self.request.user else: if not isinstance(user_id, int): raise ValidationError( {"detail": "user_id has to be an int."}) point_of_order = False # not for someone else # Add someone else. if not has_perm(self.request.user, "agenda.can_manage_list_of_speakers"): self.permission_denied(request) try: user = get_user_model().objects.get(pk=user_id) except get_user_model().DoesNotExist: raise ValidationError({"detail": "User does not exist."}) # Try to add the user. This ensurse that a user is not twice in the # list of coming speakers. try: speaker = Speaker.objects.add(user, list_of_speakers, point_of_order=point_of_order) except OpenSlidesError as e: raise ValidationError({"detail": str(e)}) # Send new speaker via autoupdate because users without permission # to see users may not have it but can get it now. inform_changed_data(user, disable_history=True) # Set 'marked' for the speaker elif request.method == "PATCH": # Check permissions if not has_perm(self.request.user, "agenda.can_manage_list_of_speakers"): self.permission_denied(request) # Retrieve user_id user_id = request.data.get("user") try: user = get_user_model().objects.get(pk=int(user_id)) except (ValueError, get_user_model().DoesNotExist): raise ValidationError({"detail": "User does not exist."}) marked = request.data.get("marked") if not isinstance(marked, bool): raise ValidationError({"detail": "Marked has to be a bool."}) queryset = Speaker.objects.filter( list_of_speakers=list_of_speakers, user=user, begin_time=None) if not queryset.exists(): raise ValidationError( {"detail": "The user is not in the list of speakers."}) for speaker in queryset.all(): speaker.marked = marked speaker.save() else: # request.method == 'DELETE' speaker_ids = request.data.get("speaker") # Check permissions and other conditions. Get speaker instance. if speaker_ids is None: point_of_order = request.data.get("point_of_order") or False if not isinstance(point_of_order, bool): raise ValidationError( {"detail": "point_of_order has to be a bool."}) # Remove oneself queryset = Speaker.objects.filter( list_of_speakers=list_of_speakers, user=self.request.user, point_of_order=point_of_order, ).exclude(weight=None) if not queryset.exists(): raise ValidationError( {"detail": "The user is not in the list of speakers."}) # We delete all() from the queryset and do not use get(): # The Speaker.objects.add method should assert, that there # is only one speaker. But due to race conditions, sometimes # there are multiple ones. Using all() ensures, that there is # no server crash, if this happens. queryset.all().delete() inform_changed_data(list_of_speakers) else: # Remove someone else. if not has_perm(self.request.user, "agenda.can_manage_list_of_speakers"): self.permission_denied(request) if isinstance(speaker_ids, int): speaker_ids = [speaker_ids] deleted_some_speakers = False for speaker_id in speaker_ids: try: speaker = Speaker.objects.get(pk=int(speaker_id)) except (ValueError, Speaker.DoesNotExist): pass else: speaker.delete(skip_autoupdate=True) deleted_some_speakers = True # send autoupdate if speakers are deleted if deleted_some_speakers: inform_changed_data(list_of_speakers) return Response()
def reset(self, request, pk): poll = self.get_object() poll.reset() return Response()
def manage_speaker(self, request, pk=None): """ Special view endpoint to add users to the list of speakers or remove them. Send POST {'user': <user_id>} to add a new speaker. Omit data to add yourself. Send DELETE {'speaker': <speaker_id>} or DELETE {'speaker': [<speaker_id>, <speaker_id>, ...]} to remove one or more speakers from the list of speakers. Omit data to remove yourself. Send PATCH {'user': <user_id>, 'marked': <bool>} to mark the speaker. Checks also whether the requesting user can do this. He needs at least the permissions 'agenda.can_see_list_of_speakers' (see self.check_view_permissions()). In case of adding himself the permission 'agenda.can_be_speaker' is required. In case of adding or removing someone else the permission 'agenda.can_manage_list_of_speakers' is required. In case of removing himself no other permission is required. """ # Retrieve list of speakers. list_of_speakers = self.get_object() if request.method == "POST": # Add new speaker # Retrieve user_id user_id = request.data.get("user") # Check permissions and other conditions. Get user instance. if user_id is None: # Add oneself if not has_perm(self.request.user, "agenda.can_be_speaker"): self.permission_denied(request) if list_of_speakers.closed: raise ValidationError({"detail": "The list of speakers is closed."}) user = self.request.user else: # Add someone else. if not has_perm( self.request.user, "agenda.can_manage_list_of_speakers" ): self.permission_denied(request) try: user = get_user_model().objects.get(pk=int(user_id)) except (ValueError, get_user_model().DoesNotExist): raise ValidationError({"detail": "User does not exist."}) # Try to add the user. This ensurse that a user is not twice in the # list of coming speakers. try: speaker = Speaker.objects.add(user, list_of_speakers) except OpenSlidesError as e: raise ValidationError({"detail": str(e)}) # Send new speaker via autoupdate because users without permission # to see users may not have it but can get it now. inform_changed_data(user, disable_history=True) # Toggle 'marked' for the speaker elif request.method == "PATCH": # Check permissions if not has_perm(self.request.user, "agenda.can_manage_list_of_speakers"): self.permission_denied(request) # Retrieve user_id user_id = request.data.get("user") try: user = get_user_model().objects.get(pk=int(user_id)) except (ValueError, get_user_model().DoesNotExist): raise ValidationError({"detail": "User does not exist."}) marked = request.data.get("marked") if not isinstance(marked, bool): raise ValidationError({"detail": "Marked has to be a bool."}) queryset = Speaker.objects.filter( list_of_speakers=list_of_speakers, user=user, begin_time=None ) try: # We assume that there aren't multiple entries for speakers that # did not yet begin to speak, because this # is forbidden by the Manager's add method. We assume that # there is only one speaker instance or none. speaker = queryset.get() except Speaker.DoesNotExist: raise ValidationError( {"detail": "The user is not in the list of speakers."} ) else: speaker.marked = marked speaker.save() else: # request.method == 'DELETE' speaker_ids = request.data.get("speaker") # Check permissions and other conditions. Get speaker instance. if speaker_ids is None: # Remove oneself queryset = Speaker.objects.filter( list_of_speakers=list_of_speakers, user=self.request.user ).exclude(weight=None) try: # We assume that there aren't multiple entries because this # is forbidden by the Manager's add method. We assume that # there is only one speaker instance or none. speaker = queryset.get() except Speaker.DoesNotExist: raise ValidationError( {"detail": "You are not on the list of speakers."} ) else: speaker.delete() else: # Remove someone else. if not has_perm( self.request.user, "agenda.can_manage_list_of_speakers" ): self.permission_denied(request) if isinstance(speaker_ids, int): speaker_ids = [speaker_ids] deleted_speaker_count = 0 for speaker_id in speaker_ids: try: speaker = Speaker.objects.get(pk=int(speaker_id)) except (ValueError, Speaker.DoesNotExist): pass else: speaker.delete(skip_autoupdate=True) deleted_speaker_count += 1 # send autoupdate if speakers are deleted if deleted_speaker_count > 0: inform_changed_data(list_of_speakers) return Response()
def manage_speaker(self, request, pk=None): """ Special view endpoint to add users to the list of speakers or remove them. Send POST {'user': <user_id>} to add a new speaker. Omit data to add yourself. Send DELETE {'speaker': <speaker_id>} or DELETE {'speaker': [<speaker_id>, <speaker_id>, ...]} to remove one or more speakers from the list of speakers. Omit data to remove yourself. Checks also whether the requesting user can do this. He needs at least the permissions 'agenda.can_see' (see self.check_view_permissions()). In case of adding himself the permission 'agenda.can_be_speaker' is required. In case of adding someone else the permission 'agenda.can_manage' is required. In case of removing someone else 'agenda.can_manage' is required. In case of removing himself no other permission is required. """ # Retrieve item. item = self.get_object() if request.method == 'POST': # Retrieve user_id user_id = request.data.get('user') # Check permissions and other conditions. Get user instance. if user_id is None: # Add oneself if not self.request.user.has_perm('agenda.can_be_speaker'): self.permission_denied(request) if item.speaker_list_closed: raise ValidationError({'detail': _('The list of speakers is closed.')}) user = self.request.user else: # Add someone else. if not self.request.user.has_perm('agenda.can_manage'): self.permission_denied(request) try: user = get_user_model().objects.get(pk=int(user_id)) except (ValueError, get_user_model().DoesNotExist): raise ValidationError({'detail': _('User does not exist.')}) # Try to add the user. This ensurse that a user is not twice in the # list of coming speakers. try: Speaker.objects.add(user, item) except OpenSlidesError as e: raise ValidationError({'detail': str(e)}) message = _('User %s was successfully added to the list of speakers.') % user else: # request.method == 'DELETE' speaker_ids = request.data.get('speaker') # Check permissions and other conditions. Get speaker instance. if speaker_ids is None: # Remove oneself queryset = Speaker.objects.filter( item=item, user=self.request.user).exclude(weight=None) try: # We assume that there aren't multiple entries because this # is forbidden by the Manager's add method. We assume that # there is only one speaker instance or none. speaker = queryset.get() except Speaker.DoesNotExist: raise ValidationError({'detail': _('You are not on the list of speakers.')}) else: speaker.delete() message = _('You are successfully removed from the list of speakers.') else: # Remove someone else. if not self.request.user.has_perm('agenda.can_manage'): self.permission_denied(request) if type(speaker_ids) is int: speaker_ids = [speaker_ids] for speaker_id in speaker_ids: try: speaker = Speaker.objects.get(pk=int(speaker_id)) except (ValueError, Speaker.DoesNotExist): raise ValidationError({'detail': _('Speaker does not exist.')}) # Delete the speaker. speaker.delete() message = _('Speaker %s was successfully removed from the list of speakers.') % speaker # Initiate response. return Response({'detail': message})