Merge pull request #25503 from edx/mikix/parent-access
AA-408: Treat group_access as inheritable in gating transformer
This commit is contained in:
@@ -58,7 +58,7 @@ def update_masters_access_course(sender, instance, **kwargs): # pylint: disable
|
|||||||
masters_id = getattr(settings, 'COURSE_ENROLLMENT_MODES', {}).get('masters', {}).get('id', None)
|
masters_id = getattr(settings, 'COURSE_ENROLLMENT_MODES', {}).get('masters', {}).get('id', None)
|
||||||
verified_id = getattr(settings, 'COURSE_ENROLLMENT_MODES', {}).get('verified', {}).get('id', None)
|
verified_id = getattr(settings, 'COURSE_ENROLLMENT_MODES', {}).get('verified', {}).get('id', None)
|
||||||
if not (masters_id and verified_id):
|
if not (masters_id and verified_id):
|
||||||
log.error("Missing settings.COURSE_ENROLLMENT_MODES -> verified:%s masters:%s", verified, masters)
|
log.error("Missing settings.COURSE_ENROLLMENT_MODES -> verified:%s masters:%s", verified_id, masters_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
course_id = instance.course_id
|
course_id = instance.course_id
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class StackedConfigModelAdmin(ConfigurationModelAdmin):
|
|||||||
form = StackedConfigModelAdminForm
|
form = StackedConfigModelAdminForm
|
||||||
|
|
||||||
raw_id_fields = ('course',)
|
raw_id_fields = ('course',)
|
||||||
|
search_fields = ('site__domain', 'org', 'org_course', 'course__id')
|
||||||
|
|
||||||
def get_fieldsets(self, request, obj=None):
|
def get_fieldsets(self, request, obj=None):
|
||||||
return (
|
return (
|
||||||
@@ -65,13 +66,12 @@ class StackedConfigModelAdmin(ConfigurationModelAdmin):
|
|||||||
def stackable_fields(self):
|
def stackable_fields(self):
|
||||||
return list(self.model.STACKABLE_FIELDS)
|
return list(self.model.STACKABLE_FIELDS)
|
||||||
|
|
||||||
@property
|
def get_config_fields(self, request, obj=None):
|
||||||
def config_fields(self):
|
fields = super().get_fields(request, obj)
|
||||||
fields = super(StackedConfigModelAdmin, self).get_fields(request, obj)
|
|
||||||
return [field for field in fields if field not in self.key_fields]
|
return [field for field in fields if field not in self.key_fields]
|
||||||
|
|
||||||
def get_fields(self, request, obj=None):
|
def get_fields(self, request, obj=None):
|
||||||
return self.key_fields + self.config_fields
|
return self.key_fields + self.get_config_fields(request, obj)
|
||||||
|
|
||||||
def get_displayable_field_names(self):
|
def get_displayable_field_names(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ Limits access for certain users to certain types of content.
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
from lms.djangoapps.course_blocks.transformers.user_partitions import UserPartitionTransformer
|
||||||
from openedx.core.djangoapps.content.block_structure.transformer import BlockStructureTransformer
|
from openedx.core.djangoapps.content.block_structure.transformer import BlockStructureTransformer
|
||||||
from openedx.features.content_type_gating.helpers import CONTENT_GATING_PARTITION_ID
|
from openedx.features.content_type_gating.helpers import CONTENT_GATING_PARTITION_ID
|
||||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||||
@@ -15,6 +16,8 @@ class ContentTypeGateTransformer(BlockStructureTransformer):
|
|||||||
"""
|
"""
|
||||||
A transformer that adds a partition condition for all graded content
|
A transformer that adds a partition condition for all graded content
|
||||||
so that the content is only visible to verified users.
|
so that the content is only visible to verified users.
|
||||||
|
|
||||||
|
This transformer requires that the UserPartitionTransformer also be included in your transformer list.
|
||||||
"""
|
"""
|
||||||
WRITE_VERSION = 1
|
WRITE_VERSION = 1
|
||||||
READ_VERSION = 1
|
READ_VERSION = 1
|
||||||
@@ -48,6 +51,24 @@ class ContentTypeGateTransformer(BlockStructureTransformer):
|
|||||||
for parent_block_key in block_structure.get_parents(block_key):
|
for parent_block_key in block_structure.get_parents(block_key):
|
||||||
self._set_contains_gated_content_on_parents(block_structure, parent_block_key)
|
self._set_contains_gated_content_on_parents(block_structure, parent_block_key)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_block_group_access(block_structure, block_key):
|
||||||
|
"""
|
||||||
|
Gets the current group_access value for a block, supporting inheritance when possible.
|
||||||
|
In order to support inheritance, UserPartitionTransformer must also be used.
|
||||||
|
"""
|
||||||
|
# See user_partitions.py for the code that sets this field.
|
||||||
|
merged_access = block_structure.get_transformer_block_field(
|
||||||
|
block_key, UserPartitionTransformer, 'merged_group_access', None
|
||||||
|
)
|
||||||
|
if merged_access:
|
||||||
|
current_access = merged_access.get_allowed_groups()
|
||||||
|
else:
|
||||||
|
# This fallback code has a bug if UserPartitionTranformer is not being used -- it does not consider
|
||||||
|
# inheritance from parent blocks. This is why our class docstring recommends UserPartitionTranformer.
|
||||||
|
current_access = block_structure.get_xblock_field(block_key, 'group_access')
|
||||||
|
return current_access or {}
|
||||||
|
|
||||||
def transform(self, usage_info, block_structure):
|
def transform(self, usage_info, block_structure):
|
||||||
if not ContentTypeGatingConfig.enabled_for_enrollment(
|
if not ContentTypeGatingConfig.enabled_for_enrollment(
|
||||||
user=usage_info.user,
|
user=usage_info.user,
|
||||||
@@ -61,9 +82,7 @@ class ContentTypeGateTransformer(BlockStructureTransformer):
|
|||||||
weight_not_zero = block_structure.get_xblock_field(block_key, 'weight') != 0
|
weight_not_zero = block_structure.get_xblock_field(block_key, 'weight') != 0
|
||||||
problem_eligible_for_content_gating = graded and has_score and weight_not_zero
|
problem_eligible_for_content_gating = graded and has_score and weight_not_zero
|
||||||
if problem_eligible_for_content_gating:
|
if problem_eligible_for_content_gating:
|
||||||
current_access = block_structure.get_xblock_field(block_key, 'group_access')
|
current_access = self._get_block_group_access(block_structure, block_key)
|
||||||
if current_access is None:
|
|
||||||
current_access = {}
|
|
||||||
current_access.setdefault(
|
current_access.setdefault(
|
||||||
CONTENT_GATING_PARTITION_ID,
|
CONTENT_GATING_PARTITION_ID,
|
||||||
[settings.CONTENT_TYPE_GATE_GROUP_IDS['full_access']]
|
[settings.CONTENT_TYPE_GATE_GROUP_IDS['full_access']]
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class ContentTypeGatingFieldOverride(FieldOverrideProvider):
|
|||||||
if parent is not None:
|
if parent is not None:
|
||||||
merged_group_access = parent.merged_group_access
|
merged_group_access = parent.merged_group_access
|
||||||
if merged_group_access and CONTENT_GATING_PARTITION_ID in merged_group_access:
|
if merged_group_access and CONTENT_GATING_PARTITION_ID in merged_group_access:
|
||||||
return original_group_access
|
return default
|
||||||
|
|
||||||
original_group_access.setdefault(
|
original_group_access.setdefault(
|
||||||
CONTENT_GATING_PARTITION_ID,
|
CONTENT_GATING_PARTITION_ID,
|
||||||
@@ -67,5 +67,5 @@ class ContentTypeGatingFieldOverride(FieldOverrideProvider):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def enabled_for(cls, course):
|
def enabled_for(cls, course):
|
||||||
"""This simple override provider is always enabled"""
|
"""Check our stackable config for this specific course"""
|
||||||
return ContentTypeGatingConfig.enabled_for_course(course_key=course.scope_ids.usage_id.course_key)
|
return ContentTypeGatingConfig.enabled_for_course(course_key=course.scope_ids.usage_id.course_key)
|
||||||
|
|||||||
Reference in New Issue
Block a user