OEP | OEP-6 |
Title | Context-scoped XBlock Fields |
Last Modified | 2016-12-14 |
Author | Braden MacDonald <braden@opencraft.com> |
Arbiter | Calen Pennington <cale@edx.org> |
Status | Accepted |
Type | Architecture |
Created | 2016-08-03 |
Resolution |
There is currently no standard way for XBlocks to define data fields such that content, settings, or user input can be shared among multiple XBlocks within a particular “context” such as a course. Such “course-scoped” or “context-scoped” fields are a common requirement; for example, an author may wish to specify the default “number of attempts allowed” once for all problems in a course, or a video player XBlock may want to “remember” the user’s preferred video playback speed for videos within a particular course.
This OEP proposes a new mechanism for defining XBlock fields that are explicitly scoped to a particular context. The generic term “context” refers to a course, content library, blog post, or any other place where XBlocks can be used (see “Rationale”).
The XBlock API uses the abstract notion of “scopes” to associate each field
with particular environment(s) and user(s). A field’s Scope
is generally a
composite property, defined as the combination of a BlockScope
, a
UserScope
, and a name
.
For reference, the UserScope
can have three values (no changes to these are
being proposed):
NONE
: data not related any particular user, that does not change while
users interact with the XBlockALL
: data not related to any particular user, but which may be modified
during user interactionsONE
: data related to an individual userThe BlockScope
can have the following values:
USAGE
: “The data is related to a particular use of a block in a course.”DEFINITION
: “The data is related to the definition of the block” (oneblock definition can be used multiple times in a course or even among multiple
courses).
* TYPE
: “The data is related to all instances of this type of XBlock.”
* ALL
: “The data is common to all blocks.”
XBlock runtimes would generally not be expected to implement every possible
combination of BlockScope
and UserScope
. Instead, certain “named scopes”
are defined, which are the only scopes that most runtimes support:
The existing named scopes, and an example use case for each, are as follows:
Scope.content
(UserScope.NONE, BlockScope.DEFINITION):Scope.settings
(UserScope.NONE, BlockScope.USAGE)Scope.user_state
(UserScope.ONE, BlockScope.USAGE)Scope.user_state_summary
(UserScope.ALL, BlockScope.USAGE)Scope.user_info
(UserScope.ONE, BlockScope.ALL)Scope.preferences
(UserScope.ONE, BlockScope.TYPE)As one would expect, Scope.user_info
and Scope.preferences
fields in the
Open edX platform are implemented globally, and not scoped to the course that
uses them. There is currently no standard, extensible way to use XBlock fields
to store data that is scoped to a particular “context,” such as a course,
although this is a common need.
In the absence of a formal method for achieving context-scoped fields through the XBlock API, many other solutions have been put in place, such as: adding custom Django models to XBlocks, storing fields on the root “course” XBlock, defining inherited fields using InheritanceMixin, using edx-submissions as a generic data store, and more.
It is hoped that the new canonical mechanism described by this OEP can provide consistency, improve interoperability, allow new types of behavior to be implemented by XBlocks, and give both authors and learners additional control over learner interactions with XBlocks.
To support context-scoped fields, BlockScope
will allow a new value:
CONTEXT
. This new BlockScope
will denote data that is related to all
instances of this type of XBlock within a particular context.
A “context” is defined by the runtime but is usually a “course” or the nearest equivalent. In the Open edX platform, the context may be a course or a content library. Different runs of the same course would be considered different contexts and would not share data, though UserScope.NONE fields would be copied when creating a new course run. On other platforms, a “context” could be defined as a blog post or a textbook.
As is the situtation today, XBlock runtimes would generally not be expected to
implement every possible combination of BlockScope
and UserScope
.
Instead, XBlock runtimes are encouraged to implement these new named scopes,
which would be defined in the XBlock python package:
Scope.context_settings
(UserScope.NONE, BlockScope.CONTEXT)Scope.context_preferences
(UserScope.ONE, BlockScope.CONTEXT)An XBlock which uses Scope.context_settings
fields will need a way for
content authors to edit the data in those fields. (e.g. when creating a course,
the course author may wish to change course-wide settings for the discussion
XBlock used throughout that course.)
A new XBlock view, context_settings_view
shall be used for this purpose.
Just like the existing studio_view defined by edX Studio, this new view will
allow an XBlock to return an arbitrary HTML Fragment which contains a form or
other UI for editing its Scope.context_settings
fields.
It is expected (but not required) that runtimes like edX Studio which want to
support editing of context-scoped fields will define a list of XBlocks that are
enabled within a particular context/course, and will display a list of XBlocks
that define the context_settings_view
. Clicking on the name of that XBlock
would display the HTML fragment and allow the author to edit the context-scoped
fields.
When serializing a course with Scope.context_settings
fields to Open
Learning XML (OLX), any context-scoped fields could be described within a new
<xblock-settings>
XML element that is a child of the <course>
element.
This would typically be found in course/course.xml
. Each XBlock class that
has Scope.context_settings
field data could be represented as a child
element of <xblock-settings>
, with the name of the element matching the
XBlock’s entry point name.
An example follows:
<course display_name="Example Course" language="en" advanced_modules='["drag-and-drop-v2", "lti_consumer", "xblock-dalite"]'>
<chapter url_name="6915ee3dd6ab403d8c05c0ea3180a0ee"/>
<wiki slug="A.B.C"/>
<xblock-settings>
<discussion allow_anonymous_discussion_posts="true" discussion_blackout_dates='[["2015-09-15", "2015-09-21"], ["2015-10-01", "2015-10-08"]]' />
<lti_consumer lti_passports='["myapp:key1:secret1", "otherapp:key2:secret2"]' />
<xblock-dalite dalite_lti_passports='["dalite-ng-demo:https://dalite-ng-demo.example:key:secret"]' />
</xblock-settings>
</course>
The reasoning behind the above provisional spec is as follows: * Conceptually, in the OLX format, all course content and all XBlock fields are
serialized as XML nodes, within a root<course>
node (docs). The entire course’s XML may be in a single course.xml file or spread out into multiple files and subdirectories, where the subdirectory name is the XBlock entry point name (XML node name) and the file name is the block ID (url_name
).
<course>
XML element.<course>
element is not feasible as it would be messy and conflicts
with the current approach where almost every attribute is an XBlock field
belonging to the course XBlock.<xblock-settings>
is suggested as a wrapper to keep these new XML nodes
organized and indicate their purpose clearly.Many of the ideas in this proposal are driven by the desire for new types of functionality to be implemented via XBlocks, and thus to be pluggable and easily installed, enabled, or disabled for each Open edX installation or each course.
The idea of naming the new scope “context” comes from other technologies such as LTI, where the term “context” is used analogously. For example, when embedding an LTI tool into the Open edX LMS, the LMS will pass the current course ID as the LTI “context” parameter.
Previous discussions of note include:
Other related proposals of note include:
This OEP introduces new field scopes but does not remove or modify any existing functionality. XBlock authors that add new fields to their XBlock may opt-in to using this feature.
XBlock runtimes may choose to implement context-scoped fields or not. Attempting
to access a context-scoped field in a runtime that does not support such fields
should simply return the default value of the field. Attempting to modify such a
context-scoped field in a runtime that doesn’t support such fields should raise
an InvalidScopeError
.
Some existing XBlocks currently use workarounds for the lack of context-scoped fields, such as storing data in the fields of the root ‘course’ XBlock. Converting such existing techniques to use context-scoped fields instead would be best achieved with an XBlock data migration API, which currently does not exist and is outside the scope of this OEP (however, see here for an example implementation).
Details on the current implementation of field scopes in edx-platform can be found in this gist. The field test XBlock can be used to test how a runtime behaves with different field scopes.
Scope.context_preferences
fields for XBlocks used in that course?allow_reset
, multiline_editor
, list_style
, and
values_provider
as implemented in xblock-utils.BlockScope
scopes and use the first value found,
such as “check for a Scope.settings
value, then a
Scope.context_settings
value”?TBD. Sample code to implement this feature in the XBlock package and the XBlock SDK/Workbench will be produced once the draft has had some initial scrutiny and feedback.
This proposal originally included two new BlockScope
values: CONTEXT
,
and CONTEXT_ALL_TYPES
; the latter was a proposed new scope that would be
used for field data that is accessible to XBlocks of any type, within a
particular context.
It also defined two named scopes that used CONTEXT_ALL_TYPES
:
Scope.context_shared_preferences
(UserScope.ONE, BlockScope.CONTEXT_ALL_TYPES)Scope.context_shared_settings
(UserScope.NONE, BlockScope.CONTEXT_ALL_TYPES)The current proposal deliberately does not support inheritance (i.e. a “context” in the LMS would always be a course, not a “section” or any smaller unit, and field values don’t propagate from parent blocks to children), so it is not a replacement for InheritanceMixin.
Initial discussions of this OEP agreed that context-scoped fields should not participate in inheritance. Improving inheritance of fields or making inheritable fields pluggable are worthy goals, but for a separate proposal.
Currently, a BlockScope
, a UserScope
, and a name
are combined to
make a Scope for a field. Under this alternative option, a new
RelevanceScope
dimension would be added, which could have one of two values:
ALL
(default) or CONTEXT
. The ALL
value would represent the current
XBlock API’s definitions, and the CONTEXT
value would mean that the field’s
relevance is limited to one particular context (e.g. that field’s value relates
to one particular course.)
The downside of this is that it creates a large matrix/tensor of scopes:
4 BlockScopes × 3 UserScopes × 2 RelevanceScopes = 24 distinct scope types.
Not only is this more difficult to understand, but there is no value in defining
a “context-specific” version of any BlockScope.USAGE
or
BlockScope.DEFINITION
fields. As a result of that and other conflicting or
not useful Scope combinations, it is likely that fewer than half of the 24
possible Scope values would be used.
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.