-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: custom roles and view-user mapping
- Loading branch information
Showing
18 changed files
with
770 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
"""New roles for FutureX Open edX Extensions.""" | ||
from __future__ import annotations | ||
|
||
import logging | ||
from typing import Any | ||
|
||
from common.djangoapps.student.admin import CourseAccessRoleForm | ||
from common.djangoapps.student.roles import REGISTERED_ACCESS_ROLES, CourseRole, OrgRole, RoleBase | ||
|
||
from futurex_openedx_extensions.helpers.exceptions import FXCodedException, FXExceptionCodes | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
def register_custom_access_role(cls: Any) -> Any: | ||
""" | ||
Decorator that adds the new access role to the list of registered access roles to be accessible in the Django admin. | ||
Note: roles inheritances is not supported | ||
:param cls: The class to register | ||
:type cls: Any | ||
""" | ||
def _hacky_update_django_admin_choices(choices: list[tuple[str, str]]) -> None: | ||
""" | ||
Update the choices of the role field in django admin. | ||
:param choices: The choices to update | ||
:type choices: list[tuple[str, str]] | ||
""" | ||
CourseAccessRoleForm.COURSE_ACCESS_ROLES = choices | ||
CourseAccessRoleForm.declared_fields['role'].choices = choices | ||
|
||
try: | ||
role_name = cls.ROLE | ||
if role_name in REGISTERED_ACCESS_ROLES: | ||
raise FXCodedException( | ||
code=FXExceptionCodes.CUSTOM_ROLE_DUPLICATE_DECLARATION, | ||
message=f'Trying to register a custom role {role_name} that is already registered!' | ||
) | ||
except AttributeError: | ||
log.exception('Role class %s does not have a ROLE attribute', cls.__name__) | ||
except FXCodedException as exc: | ||
log.exception(str(exc)) | ||
else: | ||
REGISTERED_ACCESS_ROLES[role_name] = cls | ||
_hacky_update_django_admin_choices([(role.ROLE, role.ROLE) for role in REGISTERED_ACCESS_ROLES.values()]) | ||
|
||
return cls | ||
|
||
|
||
@register_custom_access_role | ||
class FXAPIAccessRoleCourse(CourseRole): # pylint: disable=too-few-public-methods | ||
"""Course specific access to the FutureX APIs.""" | ||
ROLE = 'fx_api_access' | ||
|
||
def __init__(self, *args: Any, **kwargs: Any) -> None: | ||
super().__init__(self.ROLE, *args, **kwargs) | ||
|
||
|
||
class FXAPIAccessRoleOrg(OrgRole): # pylint: disable=too-few-public-methods | ||
"""Tenant-wide access to the FutureX APIs.""" | ||
ROLE = 'fx_api_access' | ||
|
||
def __init__(self, *args: Any, **kwargs: Any) -> None: | ||
super().__init__(self.ROLE, *args, **kwargs) | ||
|
||
|
||
@register_custom_access_role | ||
class FXAPIAccessRoleGlobal(RoleBase): # pylint: disable=too-few-public-methods | ||
"""Global access to the FutureX APIs.""" | ||
ROLE = 'fx_api_access_global' | ||
|
||
def __init__(self, *args: Any, **kwargs: Any) -> None: | ||
super().__init__(self.ROLE, *args, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
futurex_openedx_extensions/helpers/migrations/0005_view_user_mapping.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Generated by Django 3.2.25 on 2024-11-19 12:09 | ||
|
||
from django.conf import settings | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
import simple_history.models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
('fx_helpers', '0004_dataexporttask'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='HistoricalViewUserMapping', | ||
fields=[ | ||
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), | ||
('view_name', models.CharField(max_length=255)), | ||
('enabled', models.BooleanField(default=True)), | ||
('expires_at', models.DateTimeField(blank=True, null=True)), | ||
('history_id', models.AutoField(primary_key=True, serialize=False)), | ||
('history_date', models.DateTimeField()), | ||
('history_change_reason', models.CharField(max_length=100, null=True)), | ||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), | ||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), | ||
('user', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
'verbose_name': 'historical View-User Mapping', | ||
'ordering': ('-history_date', '-history_id'), | ||
'get_latest_by': 'history_date', | ||
}, | ||
bases=(simple_history.models.HistoricalChanges, models.Model), | ||
), | ||
migrations.CreateModel( | ||
name='ViewUserMapping', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('view_name', models.CharField(max_length=255)), | ||
('enabled', models.BooleanField(default=True)), | ||
('expires_at', models.DateTimeField(blank=True, null=True)), | ||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
'verbose_name': 'View-User Mapping', | ||
'verbose_name_plural': 'Views-Users Mapping', | ||
'unique_together': {('user', 'view_name')}, | ||
}, | ||
), | ||
] |
Oops, something went wrong.