-
-
Notifications
You must be signed in to change notification settings - Fork 304
5971: added organizations to invitation model #6008
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
42018be
ab659c6
7fa5c77
0386527
e9013a2
d5091bd
48e7c19
25c6337
e06f0f7
8f585e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Generated by Django 3.2.24 on 2026-07-02 04:15 | ||
| import django.db.models.deletion | ||
| from django.db import migrations | ||
| from django.db import models | ||
|
ArthurMousatov marked this conversation as resolved.
|
||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
ArthurMousatov marked this conversation as resolved.
|
||
|
|
||
| dependencies = [ | ||
| ("contentcuration", "0168_alter_assessmentitem_type"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AddField( | ||
| model_name="invitation", | ||
| name="organization", | ||
| field=models.ForeignKey( | ||
| blank=True, | ||
| null=True, | ||
| on_delete=django.db.models.deletion.CASCADE, | ||
| to="contentcuration.organization", | ||
| ), | ||
| ), | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -79,13 +79,17 @@ | |
| from contentcuration.constants import feedback | ||
| from contentcuration.constants import user_history | ||
| from contentcuration.constants.contentnode import kind_activity_map | ||
| from contentcuration.constants.organization_roles import ORGANIZATION_ADMIN | ||
| from contentcuration.constants.organization_roles import ORGANIZATION_EDITOR | ||
| from contentcuration.constants.organization_roles import organization_role_choices | ||
| from contentcuration.constants.organization_roles import ORGANIZATION_ROLE_STATUS_ACTIVE | ||
| from contentcuration.constants.organization_roles import ( | ||
| organization_role_status_choices, | ||
| ) | ||
| from contentcuration.constants.organization_roles import ( | ||
| ORGANIZATION_ROLE_STATUS_PENDING, | ||
| ) | ||
| from contentcuration.constants.organization_roles import ORGANIZATION_VIEWER | ||
| from contentcuration.db.models.expressions import Array | ||
| from contentcuration.db.models.functions import ArrayRemove | ||
| from contentcuration.db.models.functions import Unnest | ||
|
|
@@ -102,6 +106,7 @@ | |
|
|
||
| EDIT_ACCESS = "edit" | ||
| VIEW_ACCESS = "view" | ||
| ADMIN_ACCESS = "admin" | ||
|
|
||
| DEFAULT_CONTENT_DEFAULTS = { | ||
| "license": None, | ||
|
|
@@ -3730,6 +3735,9 @@ class Invitation(models.Model): | |
| ) | ||
| first_name = models.CharField(max_length=100, blank=True) | ||
| last_name = models.CharField(max_length=100, blank=True, null=True) | ||
| organization = models.ForeignKey( | ||
|
ArthurMousatov marked this conversation as resolved.
|
||
| "Organization", null=True, blank=True, on_delete=models.CASCADE | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. blocking:
Either way, add a test for the violation. |
||
| ) | ||
|
|
||
| class Meta: | ||
|
ArthurMousatov marked this conversation as resolved.
ArthurMousatov marked this conversation as resolved.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. blocking: Still no constraint enforcing
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be handled in follow up work at the API layer.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understood, thanks for confirming — sounds good to track that in follow-up work. |
||
| verbose_name = "Invitation" | ||
|
|
@@ -3738,13 +3746,38 @@ class Meta: | |
| def accept(self): | ||
| user = User.objects.filter(email__iexact=self.email).first() | ||
| if self.channel: | ||
| # channel is a nullable field, so check that it exists. | ||
| if self.share_mode == VIEW_ACCESS: | ||
| self.channel.editors.remove(user) | ||
| self.channel.viewers.add(user) | ||
| else: | ||
| self.channel.viewers.remove(user) | ||
| self.channel.editors.add(user) | ||
| self._accept_channel_invitation(user) | ||
| if self.organization: | ||
| self._accept_organization_invitation(user) | ||
|
|
||
| def _accept_channel_invitation(self, user): | ||
| if self.share_mode == VIEW_ACCESS: | ||
| self.channel.editors.remove(user) | ||
| self.channel.viewers.add(user) | ||
| else: | ||
| self.channel.viewers.remove(user) | ||
|
ArthurMousatov marked this conversation as resolved.
|
||
| self.channel.editors.add(user) | ||
|
|
||
| def _accept_organization_invitation(self, user): | ||
|
ArthurMousatov marked this conversation as resolved.
|
||
| role = None | ||
| if self.share_mode == VIEW_ACCESS: | ||
| role = ORGANIZATION_VIEWER | ||
| elif self.share_mode == EDIT_ACCESS: | ||
| role = ORGANIZATION_EDITOR | ||
| elif self.share_mode == ADMIN_ACCESS: | ||
| role = ORGANIZATION_ADMIN | ||
| else: | ||
| raise ValidationError(f"Invalid share_mode: {self.share_mode}") | ||
|
ArthurMousatov marked this conversation as resolved.
ArthurMousatov marked this conversation as resolved.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. blocking:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. blocking:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be caught in the API layer and reraised as a DRF validation error - using one in the model code is also incorrect, as it's not DRF focused.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checked the current code again: That said, you're right that having the model import |
||
|
|
||
| update_create_defaults = { | ||
| "role": role, | ||
| "status": ORGANIZATION_ROLE_STATUS_ACTIVE, | ||
| } | ||
| OrganizationRole.objects.update_or_create( | ||
| user=user, | ||
| organization=self.organization, | ||
| defaults=update_create_defaults, | ||
| ) | ||
|
|
||
| @classmethod | ||
| def filter_edit_queryset(cls, queryset, user): | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ Resolved — addressed in the current code.
blocking: This migration and
unstable's new0168_alter_assessmentitem_type.pyboth depend on0167_add_organizationand both claim number0168, creating two leaf nodes. CI fails with:Rebase onto current
unstable, renumber this to0169_invitation_organization.py, and updatedependenciesto point at0168_alter_assessmentitem_type(or runmanage.py makemigrations --merge).