def create_notification_based_on_task_changes(sender, instance, *args, **kwargs): """Notifies the user that something has changed in the task""" if get_current_user().is_anonymous: return # refresh task from DB instance = Task.objects.prefetch_common().get(pk=instance.pk) assigned_users = instance.assigned_users.all().exclude( pk=get_current_user().pk) for assigned_user in assigned_users: context = {'user': assigned_user, 'instance': instance} html_message = render_to_string('notification/task_changed.html', context) # check if a notification for this task and the assigned user has already been created by the current user # within the last 60 secs and if it is unread Notification.objects.update_or_create( user=assigned_user, content_type=instance.get_content_type(), object_id=instance.pk, read=False, sent__isnull=True, notification_type=NotificationConfiguration. NOTIFICATION_CONF_TASK_CHANGED, created_by=get_current_user(), created_at__gte=timezone.now() - timedelta(seconds=60), defaults={ 'title': _("Task {title} has changed".format(title=instance.title)), 'message': html_message, 'created_at': timezone.now(), })
def create_notification_based_on_delete_user_from_project( sender, instance, *args, **kwargs): """Notifies the attended user that he was removed from the project""" if get_current_user().is_anonymous: return assigned_user = instance.user # check that the role of this instance has the view_project role if not instance.role.permissions.filter( codename='view_project', content_type=Project.get_content_type()).exists(): # this role does not have view_project, so we are not sending a notification return if assigned_user != get_current_user(): project = instance.project # render html message context = {'user': assigned_user, 'instance': project} html_message = render_to_string( 'notification/project_remove_user.html', context) Notification.objects.create( user=assigned_user, title=_("You have been removed from project {project}").format( project=project.name), message=html_message, content_type=project.get_content_type(), object_id=project.pk, notification_type=NotificationConfiguration. NOTIFICATION_CONF_PROJECT_USER_CHANGED)
def create_notification_based_on_meeting_changes(sender, instance, *args, **kwargs): """Notifies the user that something has changed in the meeting""" if get_current_user().is_anonymous: return # save the instance here before the refresh, so it can be used to determine the date_time_start # for scheduled notifications to_be_saved_instance = instance # refresh meeting from DB instance = Meeting.objects.prefetch_common().get(pk=instance.pk) # if there's a ScheduledNotification related to this meeting, # it's deletion-status needs to be updated based on the meeting's status try: scheduled_notification = ScheduledNotification.objects.get( object_id=instance.pk) # Don't send reminder notifications if the to_be_saved meeting date_time_start is already in the past if scheduled_notification and to_be_saved_instance.local_date_time_start > timezone.now( ): scheduled_notification.scheduled_date_time = ScheduledNotification.calculate_scheduled_date_time( scheduled_notification.timedelta_unit, scheduled_notification.timedelta_value, instance.date_time_start) scheduled_notification.deleted = instance.deleted # make sure the reminder is sent again after meeting details have changed scheduled_notification.processed = False scheduled_notification.save() except ScheduledNotification.DoesNotExist: pass attending_users = instance.attending_users.all().exclude( pk=get_current_user().pk) for attended_user in attending_users: context = {'user': attended_user, 'instance': instance} html_message = render_to_string('notification/meeting_changed.html', context) Notification.objects.update_or_create( user=attended_user, content_type=instance.get_content_type(), object_id=instance.pk, read=False, sent__isnull=True, notification_type=NotificationConfiguration. NOTIFICATION_CONF_MEETING_CHANGED, created_by=get_current_user(), created_at__gte=timezone.now() - timedelta(seconds=60), defaults={ 'title': _("Appointment {title} has changed").format( title=instance.title), 'message': html_message, 'created_at': timezone.now() })
def save(self, *args, **kwargs): if self.pk is None: super().save(*args, **kwargs) user = get_current_user() if not user.is_anonymous: UserTeam.objects.create(user=get_current_user(), team=self, role=UserTeam.ROLE_EDITOR) else: super().save(*args, **kwargs)
def editable(self, *args, **kwargs): """ The current user may update its own notifications (e.g., read) and the user that created a notification may update it :param args: :param kwargs: :return: """ return self.filter( Q(user=get_current_user()) | Q(created_by=get_current_user()) )
def set_deleted(self, deleted=True): if deleted: self.deleted_via_caldav_on = now() self.deleted_via_caldav_by = get_current_user() else: self.deleted_via_caldav_on = None self.deleted_via_caldav_by = None
def create(self, validated_data): """ Creates a copy of a contact for another user. """ # take created_for field out of validated data receiving_user = validated_data.pop('created_for') sending_user = get_current_user() # pass usual data to standard contact serializer instance = super().create(validated_data) # hand over access to the receiving user if receiving_user != sending_user: # add access privilege for the receiving user (created_for field) from eric.model_privileges.models import ModelPrivilege ModelPrivilege.objects.update_or_create( user=receiving_user, content_type=instance.get_content_type(), object_id=instance.pk, defaults={ 'full_access_privilege': ModelPrivilege.ALLOW, }) # remove access privilege from self initial_privilege = ModelPrivilege.objects.filter( user=sending_user, object_id=instance.pk, content_type=instance.get_content_type(), ) initial_privilege.delete() return instance
def create(self, validated_data): user = get_current_user() validated_data['user_id'] = user.id instance = super().create(validated_data) return instance
def process_response(self, request, response): s = getattr(response, 'status_code', 0) r = "by %s, status %s, " % (str(get_current_user()), str(s)) if s in (300, 301, 302, 307): r += ' redirect to %s' % response.get('Location', '?') elif hasattr(response, "content") and response.content: r += ' sent %d bytes' % len(response.content) elif hasattr(response, "streaming_content") and response.streaming_content: r += ' streaming / downloading' # if status code is 2xx and debug mode is activated if 200 <= s <= 299 and settings.DEBUG: total_time = 0 for query in connection.queries: query_time = query.get('time') if query_time is None: # django-debug-toolbar monkeypatches the connection # cursor wrapper and adds extra information in each # item in connection.queries. The query time is stored # under the key "duration" rather than "time" and is # in milliseconds, not seconds. query_time = query.get('duration', 0) / 1000 total_time += float(query_time) r += ', %d queries, %0.4f seconds' % (len(connection.queries), total_time) self.log_message(request, 'response', r) return response
def auto_create_owner_entity_permission(instance, created, *args, **kwargs): """ Automatically creates an entity permission assignment with "is_owner = True" for a given entity, if "can_have_special_permissions = True" is set for the model :return: """ # ignore raw inserts (e.g. from fixtures) and updates (not created) if kwargs.get('raw') or not created: return # ignore elements that do not have can_have_special_permissions if not hasattr(instance._meta, "can_have_special_permissions"): return # ignore can_have_special_permissions = False if not instance._meta.can_have_special_permissions: return current_user = get_current_user() if not current_user or current_user.is_anonymous: logger.warning("In auto_create_owner_entity_permission: current_user is anonymous, " "not creating entity permission assignment") return # now can_have_special_permissions = True, and we need to create the assignment ModelPrivilege.objects.create( user=current_user, full_access_privilege=ModelPrivilege.ALLOW, content_object=instance )
def perform_create(self, serializer): """ Ensure that the current user is always attending the meeting they created, unless the current user creates a meeting for another user through calendar access privileges We need to do this here (rather than in a pre_save/post_save handler) """ instance = serializer.save() if instance.create_for: User = get_user_model() create_for_user = User.objects.filter( pk=instance.create_for).first() if create_for_user: # add the create_for_user to attending users here UserAttendsMeeting.objects.get_or_create( meeting=instance, user=create_for_user, ) # also give the create_for_user full access privileges for the meeting ModelPrivilege.objects.get_or_create( content_type=Meeting.get_content_type(), object_id=instance.pk, user=create_for_user, full_access_privilege=ModelPrivilege.ALLOW) else: UserAttendsMeeting.objects.get_or_create( meeting=instance, user=get_current_user(), )
def ms_office_cleanup_sequence(sender, instance, *args, **kwargs): """ Detects when a MS Office File is edited in WebDav and starts a cleanup sequence that: 1) When a file is just opened in MS Office the lock will be removed. Also a cleanup happens where unneeded tmp files are deleted 2) When a file is being edited in MS Office the lock on the original will be set. 3) When a file is saved in MS Office the original will be untrashed, a new file an entry will be created with the content of the last tmp save """ # check if this is a file that can be cleaned up, if not just return before doing anything else if not has_ms_office_extension(instance.name): return # get the user of the request user = get_current_user() # get a list of Q queries with all extensions, that can later be used in filters. clauses = (Q(original_filename__endswith=extension) for extension in MS_OFFICE_EXTENSIONS) extension_query = reduce(operator.or_, clauses) # a file is opened or closed if instance.name.startswith(OFFICE_TEMP_FILE_PREFIX) and instance.deleted: handle_opened_or_closed_ms_office_file(instance, user, extension_query) # a file is being edited if instance.name.startswith( OFFICE_TEMP_FILE_PREFIX) and not instance.deleted: handle_edited_ms_office_file(instance, user) # a file is being saved by ms office if instance.name.endswith('.tmp') and instance.deleted: handle_saved_ms_office_file(instance, extension_query)
def viewable(self): from teams.models import UserTeam user = get_current_user() return self.filter( Q(user=user, ) | Q(team__in=UserTeam.objects.filter( user=user).values_list('team', flat=True)))
def check_soft_delete_and_restore_roles(mng, instance, old_instance): """ Checks for soft delete and trash roles of the provided instance :param instance: SoftDeleteMixin """ user = get_current_user() # find out whether this is a restore or trash operation if old_instance.deleted: assert instance.deleted is False # restore instance # check if user has global restore permission if user.has_perm(get_permission_name(instance.__class__, 'restore')): return # check if object is restorable according to the objects manager if hasattr(instance, 'is_restorable') and not instance.is_restorable(): raise PermissionDenied else: assert instance.deleted is True # trash instance # check if user has global trash permission if user.has_perm(get_permission_name(instance.__class__, 'trash')): return # check if object is trashable according to the objects manager if hasattr(instance, 'is_trashable') and not instance.is_trashable(): raise PermissionDenied
def check_update_project_parent_project(instance, *args, **kwargs): """ Checks whether the current user is allowed to change the parent project of the given project instance :param instance: :param args: :param kwargs: :return: """ # do not check for raw inserts or ChangeSet or ChangeRecord insert - those are always allowed if kwargs.get('raw') or isinstance(instance, ChangeSet) or isinstance( instance, ChangeRecord): return if permission_checks_disabled(instance): return mng = instance.__class__.objects # check if instance exists if mng.filter(pk=instance.pk).count() == 0: # new instance --> ignore (check_create_roles will handle that case) return # check whether the current user is allowed to do stuff with the current project # therefore we need to remove the original sender kwargs['original_sender'] = kwargs.pop('sender') check_create_roles_for_parent_project(get_current_user(), Project, instance, *args, **kwargs)
def check_delete_roles(sender, instance, *args, **kwargs): """ On pre_delete, check delete permission for every database object where the queryset implements "deletable" raises a PermissionDenied exception on error """ if isinstance(instance, Metadata): # deleting meta data is fine return if permission_checks_disabled(instance): return # ensure that the object has been soft deleted already, else raise an exception raise_error_if_object_not_soft_deleted(sender, instance, *args, **kwargs) user = get_current_user() # check if user has global delete permission for this class if user.has_perm(get_permission_name(instance.__class__, 'delete')): return mng = instance.__class__.objects # check if deletable exists and the instance.pk is in the deletable queryset if hasattr(mng, "deletable") and callable( mng.deletable) and mng.deletable().filter( pk=instance.pk).count() == 0: raise PermissionDenied
def prevent_dmp_form_change(self): """ checks if the dmp exists before or not. It is only created when it does not exist. """ # check if object exists (= update) or if it does not exist (= create) dmp_object = Dmp.objects.filter(id=self.pk).first() if not dmp_object: # on create --> no need to do anything return # else: it's an update, check that dmp_form has not changed if dmp_object.dmp_form_id != self.dmp_form_id: raise ValidationError({ 'dmp_form': ValidationError( _('You are not allowed to change the dmp form'), params={'dmp': self}, code='invalid') }) # check if the status is set to FINAL and the user is not the creator # True --> DMP Form Data can not be changed # False --> DMP From Data can be if dmp_object.status == Dmp.FINAL and dmp_object.created_by != get_current_user( ): raise ValidationError({ 'status': ValidationError(_( 'Once the status is set to final, updates are only allowed by the user that created the DMP.' ), params={'dmp': self}, code='invalid') })
def viewable(self, *args, **kwargs): """ Returns all elements where the current user is attending, the current user is the organizer, or recurring CalDav items where the current user is the creator """ from eric.shared_elements.models import Meeting user = get_current_user() return self.filter( Q( # all CaldavItems where the current user is attending meeting__pk__in=Meeting.objects.attending().values_list('pk') ) | Q( # all CaldavItems where the current user is the organizer meeting__created_by=user ) | Q( # all CaldavItems where meeting is null # currently needed to sync recurring items, as they have no meeting, but should still be synced created_by=user, meeting__isnull=True, text__icontains='rrule' ) | Q( # all CaldavItems where meeting is null # currently needed to sync recurring items, as they have no meeting, but should still be synced created_by=user, meeting__isnull=True, text__icontains='recurrence-id' ) )
def get_filtered_schedule_elements(self): show_tasks = self.request.query_params.get('show_tasks', 1) show_meetings = self.request.query_params.get('show_meetings', 1) show_meetings_for = self.request.query_params.getlist( 'show_meetings_for', None) tasks = Task.objects.none() meetings = Meeting.objects.none() if show_tasks == 1 or show_tasks == '1': # filter all viewable tasks, that are assigned to the current user, and that have a start and due date tasks = self.get_tasks_queryset() # overwrite filter class for tasks self.filterset_class = TaskFilter tasks = self.filter_queryset(tasks) if show_meetings == 1 or show_meetings == '1': # filter all viewable meetings, that are attending to the current user if show_meetings_for: meetings = self.get_meetings_queryset(show_meetings_for) # for the ical_export we need to set the user else: current_user = get_current_user() meetings = self.get_meetings_queryset(str(current_user.pk)) return tasks, meetings
def editable(self): from teams.models import UserTeam user = get_current_user() return self.filter( team__user_teams__user=user, team__user_teams__role=UserTeam.ROLE_EDITOR).distinct()
def usable(self): """ returns all elements of the model where - the element has user_availability set to global - the element has the current user in user_availability_selected_users - the element has at least one user group of the current user in user_availability_selected_user_groups """ user = get_current_user() if user.is_anonymous: return self.none() elif user.is_superuser: return self.all() from eric.plugins.models import Plugin return self.filter( Q( # all plugins where user_availability is set to global user_availability=Plugin.GLOBAL) | Q( # all plugins where the current user is selected user_availability_selected_users=user) | Q( # all plugins where the user group of the current user is selected user_availability_selected_user_groups__pk__in=user.groups. values_list('pk'))).distinct()
def get_meetings_queryset(self, show_meetings_for): qs = Meeting.objects.none() current_user = get_current_user() # overwrite filter class for meetings self.filterset_class = MeetingFilter if str(current_user.pk) in show_meetings_for: # this is the queryset for the current user, which uses viewable and attending qs = Meeting.objects.viewable().prefetch_common().attending( ).prefetch_related('projects', ).filter( deleted=False, date_time_start__isnull=False, date_time_end__isnull=False) # we have to filter the queryset here before the union as filtering on unions isnt supported qs = self.filter_queryset(qs) # this is the queryset for users other than the current user, which uses viewable() qs_extended = Meeting.objects.viewable().prefetch_common( ).prefetch_related( 'projects', ).exclude(attending_users=current_user).filter( attending_users__in=show_meetings_for, deleted=False, date_time_start__isnull=False, date_time_end__isnull=False) # we have to filter the queryset here before the union as filtering on unions isnt supported qs_extended = self.filter_queryset(qs_extended) # now we can build the union qs = qs.union(qs_extended) return qs
def deletable(self): user = get_current_user() if user.has_perm('projects.delete_role'): return self.all() return self.none()
def get_queryset(self): """ gets a queryset with all changes to the items that the current user has access to :return: """ user = get_current_user() if user.is_anonymous: return ChangeSet.objects.none() # build a conditions list, where we will add more conditions with "OR" conditions = Q() # get all relevant search models workbench_models = self.get_models_based_on_request(self.request) # temporary fix: load ChangeSets for single models only, to avoid excessive performance hits # TODO: Fix underlying performance problem. Suspect #1: .prefetch_related('change_records') if len(workbench_models) != 1: return ChangeSet.objects.none() # iterate over search models for model in workbench_models: # add conditions to existing conditions with OR conditions = conditions | Q( object_type=model.get_content_type(), object_uuid__in=model.objects.viewable()) # query changesets with above conditions return ChangeSet.objects.filter( conditions).order_by('-date').select_related( 'user', 'user__userprofile', 'object_type').prefetch_related('change_records')
def editable(self): user = get_current_user() if user.has_perm('projects.change_role'): return self.all() return self.none()
def lock(self, webdav=False): """ Lock an element :return: """ # check if a lock exists already lock = self.get_lock_element().first() # update the lock if it does exist if lock: if lock.webdav_lock: webdav = True lock = self.update_lock(lock, webdav=webdav) # create a new one if it does not exist else: from eric.projects.models import ElementLock lock = ElementLock.objects.create( object_id=self.pk, content_type=self.get_content_type(), locked_by=get_current_user(), webdav_lock=webdav, ) # remove all existing locks for this element self.remove_all_locks(except_pk_list=[lock.pk]) return lock
def updated_by_current_user(self, *args, **kwargs): """ returns all objects that have been updated by the user (based on the django changeset model) """ user = get_current_user() return self.filter(last_modified_by=user)
def _editable(): from eric.shared_elements.models import Task user = get_current_user() # get all tasks that the current user is assigned to task_pks = Task.objects.filter(assigned_users=user).values_list('pk') return Q(pk__in=task_pks)
def my_bookings(self): """ Own bookings for an accessible resource """ from eric.projects.models.models import Resource return self.viewable().filter( resource__in=Resource.objects.viewable(), created_by=get_current_user(), )
def get_queryset(self): """ Gets all viewable notifications for the current user. """ return Notification.objects.viewable().filter( user=get_current_user()).select_related( 'content_type', 'created_by', 'created_by__userprofile', 'last_modified_by', 'last_modified_by__userprofile')
def is_staff_or_created_by_current_user(self, *args, **kwargs): """ returns all objects that have been created by the user (or if staff, all) """ user = get_current_user() if user.is_staff: return self.all() else: return self.created_by_current_user(args, kwargs)
def pre_save(self, model_instance, add): object_user = self.value_from_object(model_instance) current_user = get_current_user() if current_user.pk and (self.auto_user or (self.auto_user_add and add and not object_user)): setattr(model_instance, self.name, current_user) elif not current_user.pk: # log if the current user cannot be referenced in DB (e.g. AnonymousUser) logger.info(u"Cannot save reference for non-persistent user") return super(UserForeignKey, self).pre_save(model_instance, add)
def save_model_revision(sender, **kwargs): if not RevisionModelMixin.get_enabled(): return # do not track raw inserts/updates (e.g. fixtures) if kwargs.get('raw'): return new_instance = kwargs['instance'] # check if this is a revision model if not new_instance.pk or not isinstance(new_instance, RevisionModelMixin): return changed_fields = new_instance.changed_data # quit here if there is nothing to track. if not changed_fields: return # determine whether this is a soft delete or a restore operation is_soft_delete = False is_restore = False # get track_soft_deleted_by from the current model track_soft_delete_by = getattr(new_instance._meta, 'track_soft_delete_by', None) if track_soft_delete_by and track_soft_delete_by in changed_fields: # if len(changed_fields) > 1: # raise Exception("""Can not modify more than one field if track_soft_delete_by is changed""") # determine whether this is a soft delete or a trash change_record = changed_fields[track_soft_delete_by] # ToDo: Why are we accessing [1] here? if change_record[1] is True: is_soft_delete = True else: is_restore = True object_uuid_field_name = getattr(new_instance._meta, 'track_by', 'id') object_uuid_field = new_instance._meta.get_field(object_uuid_field_name) content_type = ContentType.objects.get_for_model(new_instance) change_set = ChangeSet() change_set.object_type = content_type if isinstance(object_uuid_field, models.UUIDField): change_set.object_uuid = getattr_orm(new_instance, object_uuid_field_name) else: change_set.object_id = getattr_orm(new_instance, object_uuid_field_name) # are there any existing changesets (without restore/soft_delete)? existing_changesets = ChangeSet.objects.filter( object_uuid=change_set.object_uuid, object_type=content_type ).exclude( changeset_type__in=[ChangeSet.RESTORE_TYPE, ChangeSet.SOFT_DELETE_TYPE] ) last_changeset = None update_existing_changeset = False if existing_changesets.exists(): # an existing changeset already exists # the operation performed can be either soft delete, restore or update if is_soft_delete: change_set.changeset_type = change_set.SOFT_DELETE_TYPE elif is_restore: change_set.changeset_type = change_set.RESTORE_TYPE else: change_set.changeset_type = change_set.UPDATE_TYPE # get the latest changeset, so we can check if the latest of existing_changeset was created by the # current user within the last couple of seconds last_changeset = existing_changesets.latest() # check if last changeset was created by the current user within the last couple of seconds if last_changeset \ and last_changeset.user == get_current_user() \ and last_changeset.date > timezone.now() - timezone.timedelta( seconds=getattr(new_instance._meta, 'aggregate_changesets_within_seconds', 0) ): # overwrite the new_changeset logger.debug("Re-using last changeset") change_set = last_changeset change_set.date = timezone.now() update_existing_changeset = True change_set.save() if update_existing_changeset: # updateing an existing changeset: need to check all change records for their existance check_number_of_change_records = False for changed_field, changed_value in changed_fields.items(): # if the changerecord for a change_set and a field already exists, it needs to be updated change_record, created = ChangeRecord.objects.get_or_create( change_set=change_set, field_name=changed_field, defaults={'old_value': changed_value[0], 'new_value': changed_value[1]}, ) if not created: # it already exists, therefore we need to update new value change_record.new_value = changed_value[1] # check if old value and new value are the same if change_record.new_value == change_record.old_value: # delete this change record change_record.delete() check_number_of_change_records = True else: # save this change record change_record.save() # we deleted a change record, lets make sure a change record still exists if check_number_of_change_records: if not change_set.change_records.all().exists(): # no change record exists for this changeset --> delete the change set change_set.delete() else: # collect change records change_records = [] # iterate over all changed fields and create a change record for them for changed_field, changed_value in changed_fields.items(): change_record = ChangeRecord( change_set=change_set, field_name=changed_field, old_value=changed_value[0], new_value=changed_value[1] ) change_records.append(change_record) # do a bulk create to increase database performance ChangeRecord.objects.bulk_create(change_records) RevisionModelMixin.save_related_revision(sender, **kwargs)