From 4cf03ad6462d70bb216f2819d06b4206c2645286 Mon Sep 17 00:00:00 2001 From: dzhuang Date: Thu, 13 Apr 2017 13:55:04 +0800 Subject: [PATCH] add view_participant_masked_profile permission, which is not activated by default. Display "Me" for the request user when masked. Enable masking username in email title and contents. --- accounts/models.py | 16 ++++++++ course/auth.py | 6 +++ course/constants.py | 6 ++- course/flow.py | 34 ++++++++++++++-- course/page/base.py | 10 ++++- course/templates/course/flow-page.html | 2 +- course/templates/course/grade-flow-page.html | 20 +++++++--- .../course/grade-internal-notes-notify.txt | 2 +- course/templates/course/grade-notify.txt | 2 +- course/templates/course/gradebook-by-opp.html | 40 +++++++++++++------ .../templates/course/gradebook-opp-list.html | 1 - .../course/gradebook-participant.html | 32 +++++++++------ course/templates/course/gradebook-single.html | 6 ++- course/templates/course/gradebook.html | 31 +++++++++++--- .../templates/course/participation-table.html | 32 +++++++++++---- course/templates/course/submit-notify.txt | 2 +- course/templatetags/coursetags.py | 27 +++++++++++++ course/utils.py | 17 ++++++++ relate/settings.py | 1 + 19 files changed, 232 insertions(+), 55 deletions(-) diff --git a/accounts/models.py b/accounts/models.py index 51c97f38..e0d8b670 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -171,6 +171,22 @@ class User(AbstractBaseUser, PermissionsMixin): return full_name.strip() + def get_masked_profile(self): + """ + Returns the masked user profile. + """ + + def default_mask_method(user): + return "%s%s" % (_("User"), str(user.pk)) + + from django.conf import settings + mask_method = getattr( + settings, + "RELATE_USER_PROFILE_MASK_METHOD", + default_mask_method) + + return str(mask_method(self)).strip() + def get_short_name(self): "Returns the short name for the user." return self.first_name diff --git a/course/auth.py b/course/auth.py index 84fe455b..30da4277 100644 --- a/course/auth.py +++ b/course/auth.py @@ -88,6 +88,12 @@ def may_impersonate(impersonator, impersonee): status=participation_status.active) for part in my_participations: + # FIXME: if a TA is not allowed to view participants' + # profile in one course, then he/she is not able to impersonate + # any user, even in courses he/she is allow to view profiles + # of all users. + if part.has_permission(pperm.view_participant_masked_profile): + return False impersonable_roles = [ argument for perm, argument in part.permissions() diff --git a/course/constants.py b/course/constants.py index 00cce6b4..33142023 100644 --- a/course/constants.py +++ b/course/constants.py @@ -92,6 +92,7 @@ class participation_permission: # noqa issue_exam_ticket = "issue_exam_ticket" batch_issue_exam_ticket = "batch_issue_exam_ticket" + view_participant_masked_profile = "view_participant_masked_profile" view_flow_sessions_from_role = "view_flow_sessions_from_role" view_gradebook = "view_gradebook" edit_grading_opportunity = "edit_grading_opportunity" @@ -161,9 +162,12 @@ PARTICIPATION_PERMISSION_CHOICES = ( (participation_permission.batch_issue_exam_ticket, pgettext_lazy("Participation permission", "Batch issue exam ticket")), + (participation_permission.view_participant_masked_profile, + pgettext_lazy("Participation permission", + "View participants' masked profile only")), (participation_permission.view_flow_sessions_from_role, pgettext_lazy("Participation permission", - "View flow sessions from role ")), + "View flow sessions from role")), (participation_permission.view_gradebook, pgettext_lazy("Participation permission", "View gradebook")), (participation_permission.edit_grading_opportunity, diff --git a/course/flow.py b/course/flow.py index a00af1b3..761e10ab 100644 --- a/course/flow.py +++ b/course/flow.py @@ -2335,6 +2335,14 @@ def send_email_about_flow_page(pctx, flow_session_id, ordinal): from django.utils import translation with translation.override(settings.RELATE_ADMIN_EMAIL_LOCALE): from django.template.loader import render_to_string + from course.utils import will_use_masked_profile_for_email + use_masked_profile_for_email = ( + will_use_masked_profile_for_email(recipient_list) + ) + if use_masked_profile_for_email: + username = pctx.participation.user.get_masked_profile() + else: + username = pctx.participation.user.get_full_name() message = render_to_string( "course/flow-page-interaction-email.txt", { "page_id": page_id, @@ -2342,7 +2350,7 @@ def send_email_about_flow_page(pctx, flow_session_id, ordinal): "course": pctx.course, "question_text": form.cleaned_data["message"], "review_uri": review_uri, - "username": pctx.participation.user.get_full_name() + "username": username }) from django.core.mail import EmailMessage @@ -2354,7 +2362,7 @@ def send_email_about_flow_page(pctx, flow_session_id, ordinal): 'identifier': pctx.course_identifier, 'flow_id': flow_session_id, 'page_id': page_id, - 'username': pctx.participation.user.get_full_name() + 'username': username }, body=message, from_email=from_email, @@ -2570,6 +2578,11 @@ def finish_flow_session_view(pctx, flow_session_id): if (hasattr(fctx.flow_desc, "notify_on_submit") and fctx.flow_desc.notify_on_submit): + from course.utils import will_use_masked_profile_for_email + staff_email = ( + fctx.flow_desc.notify_on_submit + [fctx.course.notify_email]) + use_masked_profile = will_use_masked_profile_for_email(staff_email) + if (grading_rule.grade_identifier and flow_session.participation is not None): from course.models import get_flow_grading_opportunity @@ -2590,17 +2603,30 @@ def finish_flow_session_view(pctx, flow_session_id): with translation.override(settings.RELATE_ADMIN_EMAIL_LOCALE): from django.template.loader import render_to_string + participation = flow_session.participation message = render_to_string("course/submit-notify.txt", { "course": fctx.course, "flow_session": flow_session, + "use_masked_profile": use_masked_profile, "review_uri": pctx.request.build_absolute_uri(review_uri) }) + participation_desc = repr(participation) + if use_masked_profile: + participation_desc = _( + "%(user)s in %(course)s as %(role)s") % { + "user": participation.user.get_masked_profile(), + "course": flow_session.course, + "role": "/".join( + role.identifier + for role in participation.roles.all()) + } + from django.core.mail import EmailMessage msg = EmailMessage( string_concat("[%(identifier)s:%(flow_id)s] ", - _("Submission by %(participation)s")) - % {'participation': flow_session.participation, + _("Submission by %(participation_desc)s")) + % {'participation_desc': participation_desc, 'identifier': fctx.course.identifier, 'flow_id': flow_session.flow_id}, message, diff --git a/course/page/base.py b/course/page/base.py index 3704ba05..a8259034 100644 --- a/course/page/base.py +++ b/course/page/base.py @@ -981,6 +981,8 @@ class PageBaseWithHumanTextFeedback(PageBase): if grading_form.cleaned_data["notify"] and page_context.flow_session: with translation.override(settings.RELATE_ADMIN_EMAIL_LOCALE): from django.template.loader import render_to_string + from course.utils import will_use_masked_profile_for_email + staff_email = [page_context.course.notify_email, request.user.email] message = render_to_string("course/grade-notify.txt", { "page_title": self.title(page_context, page_data), "course": page_context.course, @@ -988,6 +990,8 @@ class PageBaseWithHumanTextFeedback(PageBase): "feedback_text": grade_data["feedback_text"], "flow_session": page_context.flow_session, "review_uri": page_context.page_uri, + "use_masked_profile": + will_use_masked_profile_for_email(staff_email) }) from django.core.mail import EmailMessage @@ -1015,6 +1019,8 @@ class PageBaseWithHumanTextFeedback(PageBase): and page_context.flow_session): with translation.override(settings.RELATE_ADMIN_EMAIL_LOCALE): from django.template.loader import render_to_string + from course.utils import will_use_masked_profile_for_email + staff_email = [page_context.course.notify_email, request.user.email] message = render_to_string("course/grade-internal-notes-notify.txt", { "page_title": self.title(page_context, page_data), @@ -1023,7 +1029,9 @@ class PageBaseWithHumanTextFeedback(PageBase): "notes_text": grade_data["notes"], "flow_session": page_context.flow_session, "review_uri": page_context.page_uri, - "sender": request.user + "sender": request.user, + "use_masked_profile": + will_use_masked_profile_for_email(staff_email) }) from django.core.mail import EmailMessage diff --git a/course/templates/course/flow-page.html b/course/templates/course/flow-page.html index 1636ad64..d1e62554 100644 --- a/course/templates/course/flow-page.html +++ b/course/templates/course/flow-page.html @@ -21,7 +21,7 @@ {% trans "Viewing session for partipant" %} {% if flow_session.participation != None %} - {{ flow_session.participation.user.get_full_name }}. + {% if not pperm.view_participant_masked_profile %}{{ flow_session.participation.user.username }}{% else %}{{ flow_session.participation.user.get_masked_profile }}{% endif %}. {% else %} {% trans "(unspecified participant)" %}. {% endif %} diff --git a/course/templates/course/grade-flow-page.html b/course/templates/course/grade-flow-page.html index abc3c700..8345fbd9 100644 --- a/course/templates/course/grade-flow-page.html +++ b/course/templates/course/grade-flow-page.html @@ -97,12 +97,20 @@ {{ flow_identifier }} - {# Translators: the grade information "for" a participant with fullname + (username) #} - {% blocktrans trimmed with full_name=flow_session.participation.user.get_full_name username=flow_session.participation.user.username %} - for - {{ full_name }} - ({{ username }}) - {% endblocktrans %} + {% if not pperm.view_participant_masked_profile %} + {# Translators: the grade information "for" a participant #} + {% blocktrans trimmed with full_name=flow_session.participation.user.get_full_name username=flow_session.participation.user.username %} + for + {{ full_name }} + ({{ username }}) + {% endblocktrans %} + {% else %} + {# Translators: the grade information "for" a participant #} + {% blocktrans trimmed with masked_user_profile=flow_session.participation.user.get_masked_profile %} + for + {{ masked_user_profile }} + {% endblocktrans %} + {% endif %} diff --git a/course/templates/course/grade-internal-notes-notify.txt b/course/templates/course/grade-internal-notes-notify.txt index 7f9c8884..007d699d 100644 --- a/course/templates/course/grade-internal-notes-notify.txt +++ b/course/templates/course/grade-internal-notes-notify.txt @@ -1,5 +1,5 @@ {% load i18n %} -{% blocktrans with username=participation.user.get_email_appellation flow_id=flow_session.flow_id flow_id=flow_session.flow_id course_identifier=course.identifier feedback_text=feedback_text|safe %}Hi there, +{% if use_masked_profile %}{% participation.user.get_masked_profile as username %}{% else %}{% participation.user.get_email_appellation as username %}{% blocktrans with flow_id=flow_session.flow_id flow_id=flow_session.flow_id course_identifier=course.identifier feedback_text=feedback_text|safe %}Hi there, I am sending you the internal grading notes regarding {{username}}'s work on the page with title '{{ page_title }}' in '{{ flow_id }}' of '{{ course_identifier }}'. The full text of the notes follows. ------------------------------------------------------------------- {{ notes_text }} diff --git a/course/templates/course/grade-notify.txt b/course/templates/course/grade-notify.txt index bd307256..e726c56e 100644 --- a/course/templates/course/grade-notify.txt +++ b/course/templates/course/grade-notify.txt @@ -1,4 +1,4 @@ -{% load i18n %}{% blocktrans trimmed with username=participation.user.get_email_appellation %}Dear {{username}},{% endblocktrans %} +{% load i18n %}{% if use_masked_profile %}{% trans "Dear user" %},{% else %}{% blocktrans trimmed with username=participation.user.get_email_appellation %}Dear {{username}},{% endblocktrans %}{% endif %} {% blocktrans with flow_id=flow_session.flow_id flow_id=flow_session.flow_id course_identifier=course.identifier feedback_text=feedback_text|safe %} You have a new notification regarding your work on the page with title '{{ page_title }}' in '{{ flow_id }}' of '{{ course_identifier }}'. The full text of the feedback follows. ------------------------------------------------------------------- diff --git a/course/templates/course/gradebook-by-opp.html b/course/templates/course/gradebook-by-opp.html index 433f7154..1605719a 100644 --- a/course/templates/course/gradebook-by-opp.html +++ b/course/templates/course/gradebook-by-opp.html @@ -114,8 +114,12 @@ - - + {% if not pperm.view_participant_masked_profile %} + + + {% else %} + + {% endif %} {% if view_page_grades %} {% for i in page_numbers %} @@ -129,16 +133,29 @@ {% for participation, grade_info in grade_table %} - + + {% if not pperm.view_participant_masked_profile %} + + {% endif %} {% if view_page_grades %} {% for page_idx, grade in grade_info.grades %}
{% trans "User ID" %}{% trans "Name" context "real name of a user" %}{% trans "User ID" %}{% trans "Name" context "real name of a user" %}{% trans "User" %}{{ i }}
- {{ participation.user.username }} - - - {{ participation.user.get_full_name }} - - {% if participation.role != participation_role.student %} - ({{ participation.role }}) + + {% if not pperm.view_participant_masked_profile %} + {{ participation.user.username }} + {% else %} + {% if participation.user == user %}{% trans "Me" %}{% else %}{{ participation.user.get_masked_profile }}{% endif %} + {% endif %} + + {% if pperm.view_participant_masked_profile %} + {% if participation|has_permission:"view_gradebook" %} + ({{ participation.get_role_desc }}) + {% endif %} {% endif %} - + + {{ participation.user.get_full_name }} + + {% if participation|has_permission:"view_gradebook" %} + ({{ participation.get_role_desc }}) + {% endif %} +
- {% load coursetags %} {% get_current_js_lang_name as LANG %}