From a2a0cff43dfbef8aa56b8d759c537291212c0ab3 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Wed, 15 Sep 2021 16:45:16 +0200 Subject: [PATCH 1/3] add GuardianContactUpdateViews --- castellum/execution/urls.py | 6 ++++++ castellum/execution/views.py | 35 ++++++++++++++++++++++++++++++++++ castellum/recruitment/urls.py | 6 ++++++ castellum/recruitment/views.py | 31 ++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+) diff --git a/castellum/execution/urls.py b/castellum/execution/urls.py index 0e34dd7db..e6b867a74 100644 --- a/castellum/execution/urls.py +++ b/castellum/execution/urls.py @@ -36,6 +36,7 @@ from .views import ContactUpdateView from .views import DataProtectionUpdateView from .views import ExclusionCriteriaView from .views import ExportView +from .views import GuardianContactUpdateView from .views import NewsMailView from .views import ParticipationAppointmentsUpdateView from .views import ParticipationDetailView @@ -116,6 +117,11 @@ urlpatterns = [ AdditionalInfoUpdateView.as_view(), name='additional-info-update', ), + path( + '//guardians//update-contact/', + GuardianContactUpdateView.as_view(), + name='guardian-contact-update', + ), ] if settings.CASTELLUM_PSEUDONYMS_API_ENABLED: diff --git a/castellum/execution/views.py b/castellum/execution/views.py index 44ba07f65..182b88751 100644 --- a/castellum/execution/views.py +++ b/castellum/execution/views.py @@ -27,6 +27,7 @@ from django.contrib import messages from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import PermissionDenied from django.db import models +from django.http import Http404 from django.http import HttpResponse from django.shortcuts import get_object_or_404 from django.shortcuts import redirect @@ -61,6 +62,7 @@ from castellum.studies.mixins import StudyMixin from castellum.studies.models import Study from castellum.subjects.mixins import BaseAdditionalInfoUpdateView from castellum.subjects.mixins import BaseDataProtectionUpdateView +from castellum.subjects.mixins import SubjectMixin from castellum.subjects.models import Subject from castellum.utils.mail import MailContext @@ -608,3 +610,36 @@ class TagView(StudyMixin, PermissionRequiredMixin, TemplateView): tag.delete() messages.success(request, _('Tag has been deleted.')) return redirect('execution:execution-tags', self.study.pk) + + +class GuardianContactUpdateView(StudyMixin, SubjectMixin, BaseContactUpdateView): + base_template = 'execution/base.html' + study_status = [Study.EXECUTION] + + def get_success_url(self): + return self.request.path + + def form_valid(self, form, *args): + messages.success(self.request, _('Data has been saved.')) + return super().form_valid(form, *args) + + def get_permission_required(self): + permission_required = {'recruitment.conduct_study'} + permission_required.update(super().get_permission_required()) + return permission_required + + def get_object(self): + participation = get_object_or_404( + Participation, + pk=self.kwargs['participation_pk'], + study=self.study, + status=Participation.INVITED, + ) + if not self.request.user.has_privacy_level(participation.subject.privacy_level): + return self.handle_no_permission() + + for guardian in participation.subject.contact.guardians.all(): + if guardian.subject.slug == self.kwargs['slug']: + return guardian + + raise Http404 diff --git a/castellum/recruitment/urls.py b/castellum/recruitment/urls.py index bac076d75..b5069b83e 100644 --- a/castellum/recruitment/urls.py +++ b/castellum/recruitment/urls.py @@ -32,6 +32,7 @@ from .views import CalendarView from .views import ContactUpdateView from .views import ContactView from .views import DataProtectionUpdateView +from .views import GuardianContactUpdateView from .views import MailRecruitmentCleanupView from .views import MailRecruitmentRemindView from .views import MailRecruitmentView @@ -94,4 +95,9 @@ urlpatterns = [ AdditionalInfoUpdateView.as_view(), name='additional-info-update', ), + path( + '//guardians//update-contact/', + GuardianContactUpdateView.as_view(), + name='guardian-contact-update', + ), ] diff --git a/castellum/recruitment/views.py b/castellum/recruitment/views.py index 3307bdea9..018ec99d5 100644 --- a/castellum/recruitment/views.py +++ b/castellum/recruitment/views.py @@ -60,6 +60,7 @@ from castellum.studies.models import StudySession from castellum.studies.models import StudyTypeEventFeed from castellum.subjects.mixins import BaseAdditionalInfoUpdateView from castellum.subjects.mixins import BaseDataProtectionUpdateView +from castellum.subjects.mixins import SubjectMixin from castellum.subjects.models import Subject from castellum.utils import cached_request from castellum.utils import contrast_color @@ -550,6 +551,36 @@ class AdditionalInfoUpdateView(RecruitmentUpdateMixin, BaseAdditionalInfoUpdateV return self.participation.subject +class GuardianContactUpdateView(StudyMixin, SubjectMixin, BaseContactUpdateView): + base_template = 'recruitment/base.html' + study_status = [Study.EXECUTION] + + def get_success_url(self): + return self.request.path + + def form_valid(self, form, *args): + messages.success(self.request, _('Data has been saved.')) + return super().form_valid(form, *args) + + def get_permission_required(self): + permission_required = {'recruitment.recruit'} + permission_required.update(super().get_permission_required()) + return permission_required + + def get_object(self): + participation = get_object_or_404( + Participation, pk=self.kwargs['participation_pk'], study=self.study + ) + if not self.request.user.has_privacy_level(participation.subject.privacy_level): + return self.handle_no_permission() + + for guardian in participation.subject.contact.guardians.all(): + if guardian.subject.slug == self.kwargs['slug']: + return guardian + + raise Http404 + + class CalendarView(StudyMixin, PermissionRequiredMixin, BaseCalendarView): model = Study permission_required = 'appointments.change_appointment' -- GitLab From 16558c68369da73543231aee720bdef4951c0703 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Wed, 22 Sep 2021 14:13:00 +0200 Subject: [PATCH 2/3] link to relevant update view based on context --- .../templates/contacts/__guardian_item.html | 13 +++++++++---- .../contacts/templates/contacts/contact_form.html | 8 ++++++-- castellum/execution/views.py | 1 + castellum/recruitment/views.py | 1 + castellum/subjects/views.py | 1 + 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/castellum/contacts/templates/contacts/__guardian_item.html b/castellum/contacts/templates/contacts/__guardian_item.html index 82fbd160a..2f5832f91 100644 --- a/castellum/contacts/templates/contacts/__guardian_item.html +++ b/castellum/contacts/templates/contacts/__guardian_item.html @@ -1,4 +1,4 @@ -{% load i18n auth %} +{% load i18n %}
@@ -10,9 +10,14 @@ {% endif %}
- {% has_perm 'subjects.view_subject' user as can_view_subject %} - {% if slug and can_view_subject %} - {% translate 'Details' %} + {% if guardian %} + {% if view.context == 'subject_management' %} + {% translate 'Details' %} + {% elif view.context == 'recruitment' %} + {% translate 'Details' %} + {% elif view.context == 'execution' %} + {% translate 'Details' %} + {% endif %} {% endif %} diff --git a/castellum/contacts/templates/contacts/contact_form.html b/castellum/contacts/templates/contacts/contact_form.html index d0856e847..5d7e46812 100644 --- a/castellum/contacts/templates/contacts/contact_form.html +++ b/castellum/contacts/templates/contacts/contact_form.html @@ -42,7 +42,7 @@ {% for widget in form.guardians_remove %} - {% include 'contacts/__guardian_item.html' with name=form.guardians_remove.name pk=widget.data.value label=widget.choice_label slug=widget.choice_label.subject.slug removed=widget.data.selected %} + {% include 'contacts/__guardian_item.html' with name=form.guardians_remove.name pk=widget.data.value label=widget.choice_label guardian=widget.choice_label.subject removed=widget.data.selected %} {% endfor %} {% for subject in form.cleaned_data.guardians_add %} @@ -59,8 +59,12 @@
{{ contact }}
- {% if can_view_subject %} + {% if view.context == 'subject_management' %} {% translate 'Details' %} + {% elif view.context == 'recruitment' %} + {% translate 'Details' %} + {% elif view.context == 'execution' %} + {% translate 'Details' %} {% endif %} {% endfor %} diff --git a/castellum/execution/views.py b/castellum/execution/views.py index 182b88751..584a8343b 100644 --- a/castellum/execution/views.py +++ b/castellum/execution/views.py @@ -476,6 +476,7 @@ class ExecutionUpdateMixin(ParticipationMixin): class ContactUpdateView(ExecutionUpdateMixin, BaseContactUpdateView): subtab = 'contact' + context = 'execution' def get_object(self): return self.participation.subject.contact diff --git a/castellum/recruitment/views.py b/castellum/recruitment/views.py index 018ec99d5..10a0e21a8 100644 --- a/castellum/recruitment/views.py +++ b/castellum/recruitment/views.py @@ -525,6 +525,7 @@ class RecruitmentUpdateMixin(ParticipationMixin): class ContactUpdateView(RecruitmentUpdateMixin, BaseContactUpdateView): subtab = 'contact' + context = 'recruitment' def get_object(self): return self.participation.subject.contact diff --git a/castellum/subjects/views.py b/castellum/subjects/views.py index 5854bf290..fec839ead 100644 --- a/castellum/subjects/views.py +++ b/castellum/subjects/views.py @@ -346,6 +346,7 @@ class AdditionalInfoUpdateView(SubjectUpdateMixin, BaseAdditionalInfoUpdateView) class ContactUpdateView(SubjectUpdateMixin, BaseContactUpdateView): tab = 'contact' + context = 'subject_management' def get_object(self): subject = get_object_or_404(Subject, slug=self.kwargs['slug']) -- GitLab From 17f76d07c4f9be77afd4a8987ede8a3294bd3777 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Wed, 22 Sep 2021 14:23:21 +0200 Subject: [PATCH 3/3] use study specific hash instead of subject.slug --- .../templates/contacts/__guardian_item.html | 8 +++-- .../templates/contacts/contact_form.html | 8 +++-- castellum/execution/urls.py | 2 +- castellum/execution/views.py | 3 +- .../recruitment/templatetags/recruitment.py | 34 +++++++++++++++++++ castellum/recruitment/urls.py | 2 +- castellum/recruitment/views.py | 3 +- 7 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 castellum/recruitment/templatetags/recruitment.py diff --git a/castellum/contacts/templates/contacts/__guardian_item.html b/castellum/contacts/templates/contacts/__guardian_item.html index 2f5832f91..25285b53e 100644 --- a/castellum/contacts/templates/contacts/__guardian_item.html +++ b/castellum/contacts/templates/contacts/__guardian_item.html @@ -1,4 +1,4 @@ -{% load i18n %} +{% load i18n recruitment %}
@@ -14,9 +14,11 @@ {% if view.context == 'subject_management' %} {% translate 'Details' %} {% elif view.context == 'recruitment' %} - {% translate 'Details' %} + {% study_guardian_hash study guardian as hash %} + {% translate 'Details' %} {% elif view.context == 'execution' %} - {% translate 'Details' %} + {% study_guardian_hash study guardian as hash %} + {% translate 'Details' %} {% endif %} {% endif %} diff --git a/castellum/contacts/templates/contacts/contact_form.html b/castellum/contacts/templates/contacts/contact_form.html index 5d7e46812..7daa4d63e 100644 --- a/castellum/contacts/templates/contacts/contact_form.html +++ b/castellum/contacts/templates/contacts/contact_form.html @@ -1,5 +1,5 @@ {% extends view.base_template|default:"subjects/base.html" %} -{% load static i18n auth bootstrap4 %} +{% load static i18n auth recruitment bootstrap4 %} {% block title %} {% if object %} @@ -62,9 +62,11 @@ {% if view.context == 'subject_management' %} {% translate 'Details' %} {% elif view.context == 'recruitment' %} - {% translate 'Details' %} + {% study_guardian_hash study contact.subject as hash %} + {% translate 'Details' %} {% elif view.context == 'execution' %} - {% translate 'Details' %} + {% study_guardian_hash study contact.subject as hash %} + {% translate 'Details' %} {% endif %}
{% endfor %} diff --git a/castellum/execution/urls.py b/castellum/execution/urls.py index e6b867a74..dfc453e5e 100644 --- a/castellum/execution/urls.py +++ b/castellum/execution/urls.py @@ -118,7 +118,7 @@ urlpatterns = [ name='additional-info-update', ), path( - '//guardians//update-contact/', + '//guardians//update-contact/', GuardianContactUpdateView.as_view(), name='guardian-contact-update', ), diff --git a/castellum/execution/views.py b/castellum/execution/views.py index 584a8343b..b694cf28c 100644 --- a/castellum/execution/views.py +++ b/castellum/execution/views.py @@ -58,6 +58,7 @@ from castellum.recruitment.models import ExecutionTag from castellum.recruitment.models import NewsMailBatch from castellum.recruitment.models import Participation from castellum.recruitment.models import ReliabilityEntry +from castellum.recruitment.templatetags.recruitment import study_guardian_hash from castellum.studies.mixins import StudyMixin from castellum.studies.models import Study from castellum.subjects.mixins import BaseAdditionalInfoUpdateView @@ -640,7 +641,7 @@ class GuardianContactUpdateView(StudyMixin, SubjectMixin, BaseContactUpdateView) return self.handle_no_permission() for guardian in participation.subject.contact.guardians.all(): - if guardian.subject.slug == self.kwargs['slug']: + if study_guardian_hash(self.study, guardian.subject) == self.kwargs['hash']: return guardian raise Http404 diff --git a/castellum/recruitment/templatetags/recruitment.py b/castellum/recruitment/templatetags/recruitment.py new file mode 100644 index 000000000..404d6b799 --- /dev/null +++ b/castellum/recruitment/templatetags/recruitment.py @@ -0,0 +1,34 @@ +# (c) 2018-2021 +# MPIB , +# MPI-CBS , +# MPIP +# +# This file is part of Castellum. +# +# Castellum is free software; you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Castellum is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public +# License along with Castellum. If not, see +# . + +import hashlib + +from django import template + +register = template.Library() + + +@register.simple_tag +def study_guardian_hash(study, subject): + m = hashlib.sha256() + m.update(str(study.pk).encode('ascii')) + m.update(subject.slug.encode('ascii')) + return m.hexdigest() diff --git a/castellum/recruitment/urls.py b/castellum/recruitment/urls.py index b5069b83e..8d454a70c 100644 --- a/castellum/recruitment/urls.py +++ b/castellum/recruitment/urls.py @@ -96,7 +96,7 @@ urlpatterns = [ name='additional-info-update', ), path( - '//guardians//update-contact/', + '//guardians//update-contact/', GuardianContactUpdateView.as_view(), name='guardian-contact-update', ), diff --git a/castellum/recruitment/views.py b/castellum/recruitment/views.py index 10a0e21a8..a3559f8d8 100644 --- a/castellum/recruitment/views.py +++ b/castellum/recruitment/views.py @@ -76,6 +76,7 @@ from .mixins import get_recruitable from .models import MailBatch from .models import Participation from .models.attributes import get_description_by_statistics_rank +from .templatetags.recruitment import study_guardian_hash logger = logging.getLogger(__name__) @@ -576,7 +577,7 @@ class GuardianContactUpdateView(StudyMixin, SubjectMixin, BaseContactUpdateView) return self.handle_no_permission() for guardian in participation.subject.contact.guardians.all(): - if guardian.subject.slug == self.kwargs['slug']: + if study_guardian_hash(self.study, guardian.subject) == self.kwargs['hash']: return guardian raise Http404 -- GitLab