Files
edx-platform/openedx/core/djangoapps/xblock

XBlock App Suite (New)
======================

This django app and its sub-apps provide a python and REST API for the new XBlock runtime.

The API can be accessed in both the LMS and Studio, though it has different behavior in each.

The new runtime loads content out of **Blockstore** and is based on **Learning Contexts**, a more generic concept than courses. (The old runtime loads content out of modulestore/contentstore and is tied to courses.)

This runtime cannot currently be used for courses or course content in modulestore. It is currently being developed to support content libraries and (if the "pathways" plugin is installed) pathways (short linear playlists of XBlock content).

Ideally this API and the XBlock runtime it contains would run in a separate process from the LMS/Studio. However, that is currently too difficult to achieve. We should nevertheless strive to keep dependencies on other parts of edxapp to an absolute minimum, so that this can eventually be extracted to an independent app.

High level design goals
-----------------------

The goals for this code are:

* To exist and operate independently of the existing runtime
   - We want to build, test, and iterate on this design and the overall integration with Blockstore without fear of breaking existing content or LMS features. To that end, this new runtime is designed to work in parallel and share minimal core code nor data with the existing runtime(s)/modulestore/etc.
* To provide a complete runtime environment for XBlocks, with little or no support for legacy XModules
   - Avoiding XModule support as much as possible keeps the code cleaner
   - All XModule compatibility code that we do include must be isolated within the ``shims.py`` file for eventual removal
   - As a firm rule, any block with code split into separate ``Module`` and ``Descriptor`` classes will not be supported.
* To support learning experiences other than courses
   - The new runtime is built around the concept of "learning contexts," which are more generic and flexible than courses. See `Learning Contexts`_ below.
   - Additional learning contexts and related functionality can be added via plugins.
   - The new runtime makes as few assumptions as possible about how XBlocks are structured (doesn't expect a rooted tree hierarchy like every course and library in the old runtime must have).
* To avoid any frontend code
   - This new runtime is API-first, designed to be consumed by SPA frontends or mobile apps rather than rendering HTML into templates and sending them to the browser. Since it is based on XBlocks, there are some circumstances where it returns HTML generated by the XBlock views themselves, but it returns that HTML as an API response only, wrapped in a JSON "Fragment" object. In no other circumstances does this new runtime produce HTML.
   - This is in contrast to the old runtime in which key parts of the LMS UI were added to HTML output by the runtime and/or the XBlocks themselves (e.g. the ``sequential`` XBlock rendered next/prev navigation buttons).
* To provide first-class support for anonymous usage of XBlocks (use by unregistered users)
   - If the user is unregistered, field data will be saved into the user's django session rather than the database.
* To support sandboxed execution of XBlocks
   - The runtime API provides a mechanism for calling XBlock handlers without session authentication (using a secure token in the URL), so that XBlocks can be run in sandboxed IFrames that cannot access the user's LMS cookies or interact with the LMS other than through the XBlock JavaScript API.

Code Layout and Separation of Concerns
--------------------------------------

All of this code is found in this ``openedx.core.djangoapps.xblock`` folder:

* `api.py <./api.py>`_ : **Python API** for this new XBlock runtime. Provides methods for loading and interacting with XBlocks given a user and a usage ID, such as: ``load_block()``, ``get_block_metadata()``, ``render_block_view()``, and ``get_handler_url()``

  - Implementation differences in how Studio and the LMS implement these APIs are encapsulated in ``lms_impl.py`` and ``studio_impl.py``
* `rest_api <./rest_api/>`_ : **REST API** for this new XBlock runtime. Provides `endpoints <./rest_api/urls.py>`_ for things like getting an XBlock's metadata, rendering an XBlock's view, calling an XBlock's handler, etc.
  - An example URL is https://studio.example.com/api/xblock/v1/xblocks/lb:library1:html:introduction/view/student_view/
* `runtime <./runtime/>`_ : **Blockstore-based XBlock runtime** which implements the complete XBlock runtime API and many Open edX-specific additions and runtime services.

  - ``Scope.content`` and ``Scope.settings`` field data is handled by ``BlockstoreFieldData`` in `blockstore_field_data.py <./runtime/blockstore_field_data.py>`_ which knows how to read OLX files out of Blockstore given a ``BundleDefinitionLocator`` key. Since reading OLX out of Blockstore may be "slow", reads are cached per bundle (per bundle so that the cache is easily invalidated if Blockstore notifies us that the bundle has changed in any way). In the future, the LMS implementation of this runtime will likely load field data from a more optimized store, i.e. a more persistent version of the same cache.
  - ``Scope.user_*`` and ``Scope.preferences`` field data is stored in the user's session (in Studio) or in new database tables (for the LMS) analogous to the existing runtime's ``StudentModule`` table.
  - Each instance of the new runtime is designed to act as the runtime for multiple XBlocks at once (as long as they are requested by the same user), unlike the previous runtime which had to be instantiated separately for each XBlock. This is intended to be a memory and performance improvement, though it does require changes to some of the Open edX-specific runtime API extensions.
  - Runtime code and services which can be shared among all instances of the runtime are encapsulated in a new ``XBlockRuntimeSystem`` singleton, again for memory and performance reasons.
* `learning_context <./learning_context/>`_ : **Learning Context** management code: defines the abstract base class for learning contexts, the base opaque keys that learning contexts use, and provides a plugin-based API to get the learning context given any usage key. See `Learning Contexts`_ below.

Learning Contexts
-----------------

A "Learning Context" is a course, a library, a program, or some other collection of content where learning happens.

To work with this runtime, all learnable XBlock content for a learning context must ultimately be stored in a Blockstore bundle, but the runtime does not impose any restrictions beyond that. The relationship between a learning context and a bundle will usually be 1:1, but some future types of learning context may not stick to that, and the runtime doesn't require it.

Currently only `the "Content Library" learning context <./learning_context/content_library/>`_ is implemented. Additional learning contexts can be implemented as plugins, by subclassing the ``LearningContext`` class and registering in the ``openedx.learning_context`` entry point.

A learning context is responsible for:

* Definining whether or not a given XBlock [usage key] exists in the learning context (e.g. "Does the content library X contain block Y?")

  - This is done via its ``definition_for_usage()`` method which must return ``None`` if the usage key doesn't exist in the context.
  - There is **no** general method to "list all XBlocks" in the learning context, because it's expected that learning contexts may be dynamic, e.g. with content assigned just in time via adaptive learning. However, if a learning context is static it can certainly implement an API to list all the blocks it contains.
* Determining whether or not a given user has **permission to view/use** a given XBlock and/or to **edit** that XBlock.

  - This is done by via its ``can_view_block()`` and ``can_edit_block()`` methods.
  - For example, "pathways" might allow any user to view any XBlock, but "courses" require enrollment, cohort, and due date checks as part of this permissions logic.

* Mapping usage keys to ``BundleDefinitionLocator`` keys.

  - The XBlock runtime and other parts of the system do not know nor prescribe how a usage locator like ``lb:library15:html:introduction`` (HTML block with usage ID "introduction" in library with slug "library15") maps to an OLX file in Blockstore like "``html/introduction/definition.xml` in bundle with UUID `11111111-1111-1111-1111-111111111111`" - that logic is left to the learning context.
  - This is done via its ``definition_for_usage()`` method.

* Definining field overrides: Learning contexts may optionally implement some mechanism for overriding field data found in the Blockstore definitions based on arbitrary criteria.

  - For example, a course may specify a list of XBlock field override rules, such as:

    + "All ``problem`` XBlocks in this course override the ``num_attempts`` field to have a value of ``5``" or
    + "Users in the ``class B`` group have the ``due_date`` field of all XBlocks adjusted by ``+2 weeks``"

* Implementing other useful Studio or LMS APIs: Each learning context may also be a django app plugin that implements any additional python/REST APIs it deems useful.

  - For example, the Content Libraries learning context implements Studio python/REST API methods to:

    + Add/remove an XBlock to/from the content library
    + Set/get metadata of an XBlock in Studio (this refers to metadata like tags; setting XBlock fields is done via standard XBlock view/handler APIs).
    + Publish draft changes
    + Discard draft changes