Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion cms/djangoapps/models/settings/course_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,15 @@ def fetch_all(cls, block, filter_fields=None):
'display_name': _(field.display_name), # pylint: disable=translation-of-non-string
'help': field_help,
'deprecated': field.runtime_options.get('deprecated', False),
'hide_on_enabled_publisher': field.runtime_options.get('hide_on_enabled_publisher', False)
'hide_on_enabled_publisher': field.runtime_options.get('hide_on_enabled_publisher', False),
# The field's class name (e.g. "String", "Boolean", "Integer", "List", "Dict") lets the
# frontend pick the right input type without inferring it from the value's shape.
'type': field.__class__.__name__,
# The field's declared choices, when it has any. For enum-like settings this is a list of
# {"display_name", "value"} dicts; for numeric ranges it may be a {"min"/"max"} dict; for
# free-form settings it is None. Exposing it here keeps the valid options as a single source
# of truth in the backend instead of being hardcoded in the frontend.
'options': field.values,
}
return result

Expand Down
2 changes: 2 additions & 0 deletions xmodule/course_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from openedx.core.lib.teams_config import TeamsConfig # pylint: disable=unused-import
from xmodule import course_metadata_utils
from xmodule.course_metadata_utils import DEFAULT_GRADING_POLICY, DEFAULT_START_DATE
from xmodule.course_settings_field_options import CERTIFICATES_DISPLAY_BEHAVIOR_FIELD_OPTIONS
from xmodule.data import CertificatesDisplayBehaviors
from xmodule.graders import grader_from_conf
from xmodule.seq_block import SequenceBlock
Expand Down Expand Up @@ -605,6 +606,7 @@ class CourseFields: # pylint: disable=missing-class-docstring
),
scope=Scope.settings,
default=CertificatesDisplayBehaviors.END.value,
values=CERTIFICATES_DISPLAY_BEHAVIOR_FIELD_OPTIONS,
)
course_image = String(
display_name=_("Course About Page Image"),
Expand Down
60 changes: 60 additions & 0 deletions xmodule/course_settings_field_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
Canonical option lists ("values") for course-level Advanced Settings fields
that should be presented as dropdowns in Studio.

These describe the valid choices for enum-like course settings so the Advanced
Settings API can expose them to the frontend, instead of the frontend
hardcoding them. Each list follows the XBlock ``values`` format:
``[{"display_name": ..., "value": ...}, ...]``.

``display_name`` values are plain strings (not wrapped in gettext) for two
reasons: this module is imported by ``xmodule/modulestore/inheritance.py``,
which explicitly forbids importing Django, and the existing enum ``values`` in
``xmodule/course_block.py`` already use plain-string labels. User-facing
translation of these labels is handled by the frontend.

NOTE: ``showanswer`` / ``rerandomize`` / ``show_correctness`` also exist as
problem-level fields in ``xmodule/capa_block.py`` with their own inline
``values``. Those should eventually be migrated to reference these constants so
there is a single source of truth. They are mirrored here (matching the
SHOWANSWER / RANDOMIZATION / ShowCorrectness constants) to keep this module
dependency-light and avoid import cycles.
"""

# Mirrors SHOWANSWER in xmodule/capa_block.py
SHOWANSWER_FIELD_OPTIONS = [
{"display_name": "Always", "value": "always"},
{"display_name": "Answered", "value": "answered"},
{"display_name": "Attempted or Past Due", "value": "attempted"},
{"display_name": "Closed", "value": "closed"},
{"display_name": "Finished", "value": "finished"},
{"display_name": "Correct or Past Due", "value": "correct_or_past_due"},
{"display_name": "Past Due", "value": "past_due"},
{"display_name": "Never", "value": "never"},
{"display_name": "After Some Number of Attempts", "value": "after_attempts"},
{"display_name": "After All Attempts", "value": "after_all_attempts"},
{"display_name": "After All Attempts or Correct", "value": "after_all_attempts_or_correct"},
{"display_name": "Attempted", "value": "attempted_no_past_due"},
]

# Mirrors RANDOMIZATION in xmodule/capa_block.py
RERANDOMIZE_FIELD_OPTIONS = [
{"display_name": "Always", "value": "always"},
{"display_name": "On Reset", "value": "onreset"},
{"display_name": "Never", "value": "never"},
{"display_name": "Per Student", "value": "per_student"},
]

# Mirrors ShowCorrectness in the xblock.scorable library
SHOW_CORRECTNESS_FIELD_OPTIONS = [
{"display_name": "Always", "value": "always"},
{"display_name": "Never", "value": "never"},
{"display_name": "Past Due", "value": "past_due"},
]

# Mirrors CertificatesDisplayBehaviors in xmodule/data.py
CERTIFICATES_DISPLAY_BEHAVIOR_FIELD_OPTIONS = [
{"display_name": "End of course", "value": "end"},
{"display_name": "End of course, with date", "value": "end_with_date"},
{"display_name": "Immediately upon earning", "value": "early_no_info"},
]
8 changes: 8 additions & 0 deletions xmodule/modulestore/inheritance.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
from xblock.fields import Boolean, Date, Dict, Float, Integer, List, Scope, String, Timedelta
from xblock.runtime import KeyValueStore, KvsFieldData

from xmodule.course_settings_field_options import (
RERANDOMIZE_FIELD_OPTIONS,
SHOW_CORRECTNESS_FIELD_OPTIONS,
SHOWANSWER_FIELD_OPTIONS,
)
from xmodule.error_block import ErrorBlock
from xmodule.partitions.partitions import UserPartition

Expand Down Expand Up @@ -97,6 +102,7 @@ class InheritanceMixin(XBlockMixin):
),
scope=Scope.settings,
default="finished",
values=SHOWANSWER_FIELD_OPTIONS,
)

show_correctness = String(
Expand All @@ -109,6 +115,7 @@ class InheritanceMixin(XBlockMixin):
),
scope=Scope.settings,
default="always",
values=SHOW_CORRECTNESS_FIELD_OPTIONS,
)

rerandomize = String(
Expand All @@ -123,6 +130,7 @@ class InheritanceMixin(XBlockMixin):
),
scope=Scope.settings,
default="never",
values=RERANDOMIZE_FIELD_OPTIONS,
)
days_early_for_beta = Float(
display_name=_("Days Early for Beta Users"),
Expand Down
Loading