Skip to content

Commit

Permalink
Update organization invitation tests and email templates
Browse files Browse the repository at this point in the history
  • Loading branch information
rajpatel24 committed Jan 6, 2025
1 parent af29b03 commit 31ac077
Show file tree
Hide file tree
Showing 15 changed files with 276 additions and 82 deletions.
22 changes: 13 additions & 9 deletions kobo/apps/organizations/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from functools import partial
from typing import Literal

from django.apps import apps
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
Expand Down Expand Up @@ -321,10 +322,15 @@ def send_acceptance_email(self):
Mailer.send(email_message)

def send_invite_email(self):
"""
Sends an email to invite a user to join a team as an admin.
"""
is_registered_user = bool(self.invitee)
to_email = (
self.invitee.email
if is_registered_user
else self.invitee_identifier
)
# To avoid circular import
User = apps.get_model('kobo_auth', 'User')
has_multiple_accounts = User.objects.filter(email=to_email).count() > 1
template_variables = {
'sender_name': self.invited_by.extra_details.data['name'],
'sender_username': self.invited_by.username,
Expand All @@ -334,10 +340,12 @@ def send_invite_email(self):
if is_registered_user
else self.invitee_identifier
),
'recipient_role': self.invitee_role,
'organization_name': self.invited_by.organization.name,
'base_url': settings.KOBOFORM_URL,
'invite_uid': self.guid,
'is_registered_user': is_registered_user,
'has_multiple_accounts': has_multiple_accounts,
}

if is_registered_user:
Expand All @@ -348,12 +356,8 @@ def send_invite_email(self):
text_template = 'emails/unregistered_user_invite.txt'

email_message = EmailMessage(
to=(
self.invitee.email
if is_registered_user
else self.invitee_identifier
),
subject='Invitation to Join the Organization',
to=to_email,
subject='Invitation to Join the KoboToolbox Organization',
plain_text_content_or_template=text_template,
template_variables=template_variables,
html_content_or_template=html_template,
Expand Down
84 changes: 41 additions & 43 deletions kobo/apps/organizations/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,47 @@ class Meta:
'modified'
]

def _handle_invitee_assignment(self, instance):
"""
Assigns the invitee to the invite after the external user registers
and accepts the invite
"""
invitee_identifier = instance.invitee_identifier
if invitee_identifier and not instance.invitee:
try:
instance.invitee = User.objects.get(email=invitee_identifier)
instance.save(update_fields=['invitee'])
except User.DoesNotExist:
raise NotFound({'detail': t(INVITE_NOT_FOUND_ERROR)})

def _handle_status_update(self, instance, status):
instance.status = getattr(
OrganizationInviteStatusChoices, status.upper()
)
instance.save(update_fields=['status'])
self._send_status_email(instance, status)

def _send_status_email(self, instance, status):
status_map = {
'accepted': instance.send_acceptance_email,
'declined': instance.send_refusal_email,
'resent': instance.send_invite_email
}

email_func = status_map.get(status)
if email_func:
email_func()

def _update_invitee_organization(self, instance):
"""
Update the organization of the invitee after accepting the invitation
"""
org_user = OrganizationUser.objects.get(user=instance.invitee)
Organization.objects.filter(organization_users=org_user).delete()
org_user.organization = instance.invited_by.organization
org_user.is_admin = instance.invitee_role == 'admin'
org_user.save()

def create(self, validated_data):
"""
Create multiple invitations for the provided invitees.
Expand Down Expand Up @@ -378,46 +419,3 @@ def validate_invitees(self, value):
USER_DOES_NOT_EXIST_ERROR.format(invitee=invitee)
)
return {'users': valid_users, 'emails': external_emails}

def _handle_invitee_assignment(self, instance):
"""
Assigns the invitee to the invite after the external user registers
and accepts the invite
"""
invitee_identifier = instance.invitee_identifier
if invitee_identifier and not instance.invitee:
try:
instance.invitee = User.objects.get(email=invitee_identifier)
instance.save(update_fields=['invitee'])
except User.DoesNotExist:
raise serializers.ValidationError(
'No user found with the specified email.'
)

def _handle_status_update(self, instance, status):
instance.status = getattr(
OrganizationInviteStatusChoices, status.upper()
)
instance.save(update_fields=['status'])
self._send_status_email(instance, status)

def _send_status_email(self, instance, status):
status_map = {
'accepted': instance.send_acceptance_email,
'declined': instance.send_refusal_email,
'resent': instance.send_invite_email
}

email_func = status_map.get(status)
if email_func:
email_func()

def _update_invitee_organization(self, instance):
"""
Update the organization of the invitee after accepting the invitation
"""
org_user = OrganizationUser.objects.get(user=instance.invitee)
Organization.objects.filter(organization_users=org_user).delete()
org_user.organization = instance.invited_by.organization
org_user.is_admin = instance.invitee_role == 'admin'
org_user.save()
2 changes: 2 additions & 0 deletions kobo/apps/organizations/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
)
def transfer_member_data_ownership_to_org(user_id: int):
sender = User.objects.get(pk=user_id)
# Get sender's organization without using the cached organization property,
# as it may be outdated.
sender_organization = Organization.objects.filter(
organization_users__user=sender
).first()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% load i18n %}
<p>{% trans "Dear" %} {{ sender_username }},</p>

<p>{% blocktrans %}{{ recipient_username }} ({{ recipient_email }}) has accepted your request to join {{ organization_name }}’s organization.{% endblocktrans %}</p>
<p>{% blocktrans %}{{ recipient_username }} ({{ recipient_email }}) has accepted your request to join {{ organization_name }} organization.{% endblocktrans %}</p>

<p>{% trans "All projects, submissions, data storage, transcription and translation usage for their projects will be transferred to you." %}</p>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% load i18n %}
{% trans "Dear" %} {{ sender_username }},

{% blocktrans %}{{ recipient_username }} ({{ recipient_email }}) has accepted your request to join {{ organization_name }}’s organization.{% endblocktrans %}
{% blocktrans %}{{ recipient_username }} ({{ recipient_email }}) has accepted your request to join {{ organization_name }} organization.{% endblocktrans %}

{% trans "All projects, submissions, data storage, transcription and translation usage for their projects will be transferred to you." %}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% load i18n %}
<p>{% trans "Dear" %} {{ sender_username }},</p>

<p>{% blocktrans %}{{ recipient }} has declined your request to join {{ organization_name }}’s organization.{% endblocktrans %}</p>
<p>{% blocktrans %}{{ recipient }} has declined your request to join {{ organization_name }} organization.{% endblocktrans %}</p>
<p>
&nbsp;-&nbsp;KoboToolbox
</p>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% load i18n %}
{% trans "Dear" %} {{ sender_username }},

{% blocktrans %}{{ recipient }} has declined your request to join {{ organization_name }}’s organization.{% endblocktrans %}
{% blocktrans %}{{ recipient }} has declined your request to join {{ organization_name }} organization.{% endblocktrans %}

- KoboToolbox
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{% load strings %}

<p>{% trans "Dear" %} {{ username }},</p>
<p>{% blocktrans %}The request you have sent to <b>{{ recipient }}</b> to join the {{ organization }}’s organization has expired.{% endblocktrans %}</p>
<p>{% blocktrans %}The request you have sent to <b>{{ recipient }}</b> to join the {{ organization }} organization has expired.{% endblocktrans %}</p>

<p>
&nbsp;-&nbsp;KoboToolbox
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
{% load strings %}

{% trans "Dear" %} {{ username }},
{% blocktrans %}The request you have sent to <b>{{ recipient }}</b> to join the {{ organization }}’s organization has expired.{% endblocktrans %}
{% blocktrans %}The request you have sent to <b>{{ recipient }}</b> to join the {{ organization }} organization has expired.{% endblocktrans %}

- KoboToolbox
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
{% load i18n %}
<p>{% trans "Hello," %}</p>

<p>{% blocktrans %}{{ sender_name }} ({{ sender_email }}, username: {{ sender_username }}) has invited you (username: {{ recipient_username }}) to join {{ organization_name }} organization.{% endblocktrans %}</p>
<p>{% blocktrans %}{{ sender_name }} ({{ sender_email }}, username: {{ sender_username }}) has invited you (username: {{ recipient_username }}) to join {{ organization_name }} organization as {{recipient_role}}.{% endblocktrans %}</p>
{% if has_multiple_accounts %}
<p>If you already have an account (or have several accounts), please sign in with the correct account.</p>
{% endif %}

<p>{% trans "What joining the team means for you:" %}</p>
<p>{% trans "What joining the organization means for you:" %}</p>
<ul>
<li>{% trans "You will benefit from higher usage limits and additional features, as well as priority user support." %}</li>
<li>{% blocktrans %}Any projects owned by your account will be transferred to {{ organization_name }} and all admins in that organization will have access to your projects and data. {% endblocktrans %}</li>
<li>{% trans "You will continue to have full management permissions for all projects previously owned by you." %}</li>
</ul>

<p>{% trans "If you want to transfer any projects to another account or remove a project, please do so before accepting the invitation. This action cannot be undone." %}</p>

<p>{% blocktrans %}To respond to this invitation, please use the following link: {{ base_url }}/#/projects/home?organization-invite={{ invite_uid }}{% endblocktrans %}</p>

<p>&nbsp;-&nbsp;KoboToolbox</p>
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
{% load i18n %}
{% trans "Hello," %}

{% blocktrans %}{{ sender_name }} ({{ sender_email }}, username: {{ sender_username }}) has invited you (username: {{ recipient_username }}) to join {{ organization_name }} organization.{% endblocktrans %}
{% blocktrans %}{{ sender_name }} ({{ sender_email }}, username: {{ sender_username }}) has invited you (username: {{ recipient_username }}) to join {{ organization_name }} organization as {{recipient_role}}.{% endblocktrans %}
{% if has_multiple_accounts %}
If you already have an account (or have several accounts), please sign in with the correct account.
{% endif %}

{% trans "What joining the team means for you:" %}
{% trans "What joining the organization means for you:" %}
* {% trans "You will benefit from higher usage limits and additional features, as well as priority user support." %}
* {% blocktrans %}Any projects owned by your account will be transferred to {{ organization_name }} and all admins in that organization will have access to your projects and data. {% endblocktrans %}
* {% trans "You will continue to have full management permissions for all projects previously owned by you." %}

{% trans "If you want to transfer any projects to another account or remove a project, please do so before accepting the invitation. This action cannot be undone." %}

{% blocktrans %}To respond to this invitation, please use the following link: {{ base_url }}/#/projects/home?organization-invite={{ invite_uid }}{% endblocktrans %}

- KoboToolbox
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{% load i18n %}
<p>{% trans "Hello," %}</p>

<p>{% blocktrans %}We’d like to give you a warm welcome to KoboToolbox. You’re invited to join {{ organization_name }} organization as a member with {{ recipient_username }}.{% endblocktrans %}</p>
<p>{% blocktrans %}We’d like to give you a warm welcome to KoboToolbox. You’re invited to join {{ organization_name }} organization as {{recipient_role}} with {{ recipient_username }}.{% endblocktrans %}</p>

<p>{% trans "What joining the team means for you:" %}</p>
<p>{% trans "What joining the organization means for you:" %}</p>
<ul>
<li>{% trans "You will benefit from higher usage limits and additional features, as well as priority user support." %}</li>
<li>{% blocktrans %}Any projects owned by your account will be transferred to {{ organization_name }} and all admins in that organization will have access to your projects and data. {% endblocktrans %}</li>
<li>{% trans "You will continue to have full management permissions for all projects previously owned by you." %}</li>
</ul>

<p>{% trans "It takes less than 2 minutes to create your account to join the organization. Please create your account here: " %}<a href="{{ base_url }}">{{ base_url }}</a>.</p>
<p>{% blocktrans %}It takes less than 2 minutes to create your account to join the organization. Please create your account here: {{ base_url }}/accounts/signup/{% endblocktrans %}</p>
<p>{% trans "Once you have finished creating your account, respond to this invitation using the following link:" %}</p>

<p>{% blocktrans %}{{ base_url }}/#/projects/home?organization-invite={{ invite_uid }}{% endblocktrans %}</p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{% load i18n %}
{% trans "Hello," %}

{% blocktrans %}We’d like to give you a warm welcome to KoboToolbox. You’re invited to join {{ organization_name }} organization as a member with {{ recipient_username }}. {% endblocktrans %}
{% blocktrans %}We’d like to give you a warm welcome to KoboToolbox. You’re invited to join {{ organization_name }} organization as {{recipient_role}} with {{ recipient_username }}. {% endblocktrans %}

{% trans "What joining the team means for you:" %}
{% trans "What joining the organization means for you:" %}
* {% trans "You will benefit from higher usage limits and additional features, as well as priority user support." %}
* {% blocktrans %}Any projects owned by your account will be transferred to {{ organization_name }} and all admins in that organization will have access to your projects and data. {% endblocktrans %}
* {% trans "You will continue to have full management permissions for all projects previously owned by you." %}

{% trans "It takes less than 2 minutes to create your account to join the organization. Please create your account here: " %}{{ base_url }}.
{% blocktrans %}It takes less than 2 minutes to create your account to join the organization. Please create your account here: {{ base_url }}/accounts/signup/{% endblocktrans %}

{% trans "Once you have finished creating your account, respond to this invitation using the following link:" %}
{% blocktrans %}{{ base_url }}/#/projects/home?organization-invite={{ invite_uid }}{% endblocktrans %}
Expand Down
Loading

0 comments on commit 31ac077

Please sign in to comment.