AA-408: Treat group_access as inheritable in gating transformer

ContentTypeGateTransformer was not considering parent values of
'group_access' when providing its own default value for it.

Also fix a few minor bugs around the place.
This commit is contained in:
Michael Terry
2020-11-02 15:31:53 -05:00
parent 857f1d0de7
commit f2d4e0ccec
4 changed files with 29 additions and 10 deletions

View File

@@ -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)
verified_id = getattr(settings, 'COURSE_ENROLLMENT_MODES', {}).get('verified', {}).get('id', None)
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
course_id = instance.course_id

View File

@@ -31,6 +31,7 @@ class StackedConfigModelAdmin(ConfigurationModelAdmin):
form = StackedConfigModelAdminForm
raw_id_fields = ('course',)
search_fields = ('site__domain', 'org', 'org_course', 'course__id')
def get_fieldsets(self, request, obj=None):
return (
@@ -65,13 +66,12 @@ class StackedConfigModelAdmin(ConfigurationModelAdmin):
def stackable_fields(self):
return list(self.model.STACKABLE_FIELDS)
@property
def config_fields(self):
fields = super(StackedConfigModelAdmin, self).get_fields(request, obj)
def get_config_fields(self, request, obj=None):
fields = super().get_fields(request, obj)
return [field for field in fields if field not in self.key_fields]
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):
"""

View File

@@ -6,6 +6,7 @@ Limits access for certain users to certain types of content.
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.features.content_type_gating.helpers import CONTENT_GATING_PARTITION_ID
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
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
READ_VERSION = 1
@@ -48,6 +51,24 @@ class ContentTypeGateTransformer(BlockStructureTransformer):
for parent_block_key in block_structure.get_parents(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):
if not ContentTypeGatingConfig.enabled_for_enrollment(
user=usage_info.user,
@@ -61,9 +82,7 @@ class ContentTypeGateTransformer(BlockStructureTransformer):
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
if problem_eligible_for_content_gating:
current_access = block_structure.get_xblock_field(block_key, 'group_access')
if current_access is None:
current_access = {}
current_access = self._get_block_group_access(block_structure, block_key)
current_access.setdefault(
CONTENT_GATING_PARTITION_ID,
[settings.CONTENT_TYPE_GATE_GROUP_IDS['full_access']]

View File

@@ -56,7 +56,7 @@ class ContentTypeGatingFieldOverride(FieldOverrideProvider):
if parent is not None:
merged_group_access = parent.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(
CONTENT_GATING_PARTITION_ID,
@@ -67,5 +67,5 @@ class ContentTypeGatingFieldOverride(FieldOverrideProvider):
@classmethod
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)