-
Notifications
You must be signed in to change notification settings - Fork 89
docs: Bring in backup-restore format doc + fixes #1477
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
kdmccormick
wants to merge
3
commits into
main
Choose a base branch
from
kdmccormick/backup-restore-format
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+360
−1
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,355 @@ | ||
| .. _Library Archive Format: | ||
|
|
||
| The Library Archive Format | ||
| ########################## | ||
|
|
||
| .. tags:: educator, reference | ||
|
|
||
| The Library Archive Format is the ZIP-based format used to | ||
| :ref:`Backup and Restore a Library` on the Open edX Platform. | ||
|
|
||
| With a library archive, authors can: | ||
|
|
||
| * Move a library between different Open edX instances. | ||
| * Keep a portable backup copy of a library. | ||
| * Inspect, or even hand-edit, the contents of a library outside of Studio. | ||
|
|
||
| Because the archive keeps each component's content as OLX (open learning XML) — | ||
| the same format Studio uses internally — authors already familiar with | ||
| :ref:`OLX <What is Open Learning XML?>` will recognize the XML stored inside. | ||
|
|
||
| .. contents:: | ||
| :local: | ||
| :depth: 2 | ||
|
|
||
| Overview | ||
| ******** | ||
|
|
||
| A backup ZIP is a self-contained snapshot of one learning package. It captures | ||
| every component, collection, container (section / subsection / unit), and | ||
| static asset. For each component and container, only the current draft and | ||
| published versions are exported — the full version history is not preserved. | ||
|
|
||
| The archive uses `TOML <https://toml.io>`_ for all metadata files and keeps the | ||
| actual component XBlock content as XML (the same OLX format Studio has always | ||
| used). This makes backups both machine-readable and human-inspectable. | ||
|
|
||
|
|
||
| .. admonition:: A note on Learning Packages | ||
|
|
||
| Every user-facing *Library* is backed in the database by a *Learning Package*, | ||
| a general repository of learning content. During the restore process, | ||
| the system creates a standalone Learning Package for inspection; once the | ||
| operator confirms the content, the Learning Package is promoted into a | ||
| proper Library. Because of this relationship, you will see the term | ||
| ``learning_package`` used inside the archive's metadata files. In the | ||
| future, this same archive format may be used to restore other kinds of | ||
| Learning-Package-backed content. | ||
|
|
||
| .. admonition:: Schema versioning | ||
|
|
||
| The current archive ``format_version`` is **1**. Future incompatible changes | ||
| to the schema will increment this number so that tooling can detect them | ||
| before attempting a restore. | ||
|
|
||
| Archive Structure | ||
| ***************** | ||
|
|
||
| :: | ||
|
|
||
| <package>.zip | ||
| ├── package.toml # library metadata + archive metadata | ||
| ├── collections/ | ||
| │ └── <collection-key>.toml # one file per collection | ||
| └── entities/ | ||
| ├── <container-key>.toml # sections, subsections, units | ||
| └── xblock.v1/ | ||
| └── <block-type>/ # e.g. html, problem, video | ||
| ├── <component-code>.toml # entity metadata + version list | ||
| └── <component-code>/ | ||
| └── component_versions/ | ||
| └── v<N>/ | ||
| ├── block.xml # XBlock content (XML) | ||
| └── static/ # media assets referenced by block.xml | ||
|
|
||
| File Format Reference | ||
| ********************* | ||
|
|
||
| package.toml | ||
| ============ | ||
|
|
||
| Located at the root of the archive. Contains two sections: | ||
|
|
||
| ``[meta]`` — archive metadata (for inspection only): | ||
|
|
||
| .. list-table:: | ||
| :header-rows: 1 | ||
| :widths: 25 15 60 | ||
|
|
||
| * - Field | ||
| - Required | ||
| - Description | ||
| * - ``format_version`` | ||
| - yes | ||
| - Integer schema version; currently ``1`` | ||
| * - ``created_by`` | ||
| - no | ||
| - Username of the operator who ran the export | ||
| * - ``created_by_email`` | ||
| - no | ||
| - Email address of the exporting user | ||
| * - ``created_at`` | ||
| - yes | ||
| - UTC timestamp when the archive was created | ||
| * - ``origin_server`` | ||
| - no | ||
| - Free-form string identifying the origin CMS instance (typically a | ||
| hostname or URL; stored as-is with no format validation) | ||
|
|
||
| ``[learning_package]`` — library data. Note that ``key`` may be overridden when | ||
| the library is restored under a new reference, and ``updated`` is written to the | ||
| archive for reference but is not applied during a restore: | ||
|
|
||
| .. list-table:: | ||
| :header-rows: 1 | ||
| :widths: 25 15 60 | ||
|
|
||
| * - Field | ||
| - Required | ||
| - Description | ||
| * - ``title`` | ||
| - yes | ||
| - Human-readable name of the library | ||
| * - ``key`` | ||
| - yes | ||
| - Package reference string, e.g. ``lib:MyOrg:MyLib`` | ||
| * - ``description`` | ||
| - yes | ||
| - Free-text description (may be blank) | ||
| * - ``created`` | ||
| - yes | ||
| - UTC timestamp when the library was originally created | ||
| * - ``updated`` | ||
| - yes | ||
| - UTC timestamp of the library's last modification (written to the | ||
| archive for reference; **not** applied during restore) | ||
|
|
||
| Example:: | ||
|
|
||
| [meta] | ||
| format_version = 1 | ||
| created_by = "lp_user" | ||
| created_by_email = "lp_user@example.com" | ||
| created_at = 2025-10-05T18:23:45.180535Z | ||
| origin_server = "cms.test" | ||
|
|
||
| [learning_package] | ||
| title = "Library test" | ||
| key = "lib:WGU:LIB_C001" | ||
| description = "" | ||
| created = 2025-08-19T04:25:10.988166Z | ||
| updated = 2025-08-19T04:25:10.988166Z | ||
|
|
||
| Component entity TOML (``entities/xblock.v1/<type>/<code>.toml``) | ||
| ================================================================= | ||
|
|
||
| Each XBlock component gets one TOML file. | ||
|
|
||
| ``[entity]``: | ||
|
|
||
| .. list-table:: | ||
| :header-rows: 1 | ||
| :widths: 25 15 60 | ||
|
|
||
| * - Field | ||
| - Required | ||
| - Description | ||
| * - ``can_stand_alone`` | ||
| - yes | ||
| - Whether this component can be used independently (almost always ``true``) | ||
| * - ``key`` | ||
| - yes | ||
| - Entity reference in the form ``xblock.v1:<type>:<code>`` | ||
| * - ``created`` | ||
| - yes | ||
| - UTC creation timestamp | ||
|
|
||
| ``[entity.draft]`` / ``[entity.published]`` — each contains ``version_num`` | ||
| pointing at the current draft or published ``[[version]]`` entry respectively. | ||
| ``[entity.draft]`` is absent when the entity has no draft. | ||
| ``[entity.published]`` is **always present** — when the entity has no | ||
| published version it is written as an empty table with an explanatory comment | ||
| (see the container example below). | ||
|
|
||
| ``[[version]]`` — at most two entries: the current draft version first, then | ||
| the current published version if it differs from draft. The full version | ||
| history is not stored. | ||
|
|
||
| .. list-table:: | ||
| :header-rows: 1 | ||
| :widths: 25 15 60 | ||
|
|
||
| * - Field | ||
| - Required | ||
| - Description | ||
| * - ``title`` | ||
| - yes | ||
| - Display name of the component at this version | ||
| * - ``version_num`` | ||
| - yes | ||
| - Monotonically increasing integer starting at 1 | ||
|
|
||
| Example:: | ||
|
|
||
| [entity] | ||
| can_stand_alone = true | ||
| key = "xblock.v1:html:e32d5479-9492-41f6-9222-550a7346bc37" | ||
| created = 2025-08-19T04:25:43.685529Z | ||
|
|
||
| [entity.draft] | ||
| version_num = 5 | ||
|
|
||
| [entity.published] | ||
| version_num = 4 | ||
|
|
||
| # ### Versions | ||
|
|
||
| [[version]] | ||
| title = "Text" | ||
| version_num = 5 | ||
|
|
||
| [[version]] | ||
| title = "Text" | ||
| version_num = 4 | ||
|
|
||
| .. admonition:: Mapping archive keys to platform keys | ||
|
|
||
| Restoring a component with an archive reference key | ||
| ``xblock.v1:<type>:<component_code>`` to a library with the key | ||
| ``lib:<org_code>:<lib_code>`` yields an XBlock with the usage key | ||
| ``lb:<org_code>:<lib_code>:<type>:<component_code>``. | ||
|
|
||
|
|
||
| Container entity TOML (``entities/<key>.toml``) | ||
| =============================================== | ||
|
|
||
| Sections, subsections, and units share the same base structure with an | ||
| additional ``[entity.container.<type>]`` marker (``section``, ``subsection``, | ||
| or ``unit``) and a ``[version.container]`` table that lists child keys. | ||
|
|
||
| Example (section):: | ||
|
|
||
| [entity] | ||
| can_stand_alone = true | ||
| key = "section1-8ca126" | ||
| created = 2025-09-04T22:51:40.919872Z | ||
|
|
||
| [entity.draft] | ||
| version_num = 2 | ||
|
|
||
| [entity.published] | ||
| # unpublished: no published_version_num | ||
|
|
||
| [entity.container.section] | ||
|
|
||
| # ### Versions | ||
|
|
||
| [[version]] | ||
| title = "Section1" | ||
| version_num = 2 | ||
|
|
||
| [version.container] | ||
| children = ["subsection1-48afa3"] | ||
|
|
||
| .. admonition:: Mapping archive keys to platform keys | ||
|
|
||
| Restoring a container of kind ``<type>`` and archive reference ``<key>`` to | ||
| a library with the key ``lib:<org_code>:<lib_code>`` yields a container with | ||
| the key ``lct:<org_code>:<lib_code>:<type>:<key>``. | ||
|
|
||
|
|
||
| Collection TOML (``collections/<key>.toml``) | ||
| ============================================ | ||
|
|
||
| .. list-table:: | ||
| :header-rows: 1 | ||
| :widths: 25 15 60 | ||
|
|
||
| * - Field | ||
| - Required | ||
| - Description | ||
| * - ``title`` | ||
| - yes | ||
| - Collection display name | ||
| * - ``key`` | ||
| - yes | ||
| - Unique key within the library | ||
| * - ``description`` | ||
| - yes | ||
| - Free-text description (may be blank) | ||
| * - ``created`` | ||
| - yes | ||
| - UTC creation timestamp | ||
| * - ``entities`` | ||
| - yes | ||
| - List of entity reference strings (e.g. ``xblock.v1:html:abc123``, ``unit-xyz789``) | ||
|
|
||
| Example:: | ||
|
|
||
| [collection] | ||
| title = "Collection test1" | ||
| key = "collection-test" | ||
| description = "" | ||
| created = 2025-08-19T04:25:27.754968Z | ||
| entities = [ | ||
| "xblock.v1:html:e32d5479-9492-41f6-9222-550a7346bc37", | ||
| "xblock.v1:problem:256739e8-c2df-4ced-bd10-8156f6cfa90b", | ||
| ] | ||
|
|
||
| XBlock content (``component_versions/v<N>/block.xml``) | ||
| ====================================================== | ||
|
|
||
| The library archive format uses | ||
| :ref:`OLX (open learning XML) <What is Open Learning XML?>` to encode components, | ||
| similar to the course archive format, although there are a few notable differences: | ||
|
|
||
| * Each library component version's OLX file is simply named ``block.xml``. Its key is | ||
| separately defined in the component's TOML metadata file. In the course OLX | ||
| archive, the name of each XML file is derived from its block's key: | ||
| ``<block_id>.xml``. | ||
|
|
||
| * Each library component stores its own static assets under | ||
| ``component_versions/v<N>/static/<filename>``, and references them in its OLX | ||
| file as ``/static/<filename>``. In the course archive, the course's static | ||
| assets are all in one shared ``static/`` folder. | ||
|
|
||
| * Library HTML content is currently serialized inline using a CDATA section | ||
| within the ``.xml`` file rather than being split into a separate ``.html`` | ||
| file. This differs from course archives, which support separate ``.xml`` and | ||
| ``.html`` files. This is a known limitation of the library XBlock | ||
| serialization layer. | ||
|
|
||
| Example ``block.xml``:: | ||
|
|
||
| <html display_name="Text"> | ||
| <![CDATA[<p>Hello <img src="/static/me.png" alt="Me" /></p>]]> | ||
| </html> | ||
|
|
||
| .. seealso:: | ||
|
|
||
| :ref:`Backup and Restore a Library` (how-to) | ||
|
|
||
| :ref:`What is Open Learning XML?` (concept) | ||
|
|
||
| :ref:`OLX Documentation <OLX TOC>` (reference) | ||
|
|
||
| :ref:`OLX Directory Structure` (reference) | ||
|
|
||
|
|
||
| **Maintenance chart** | ||
|
|
||
| +--------------+-------------------------------+----------------+--------------------------------+ | ||
| | Review Date | Working Group Reviewer | Release |Test situation | | ||
| +--------------+-------------------------------+----------------+--------------------------------+ | ||
| | | | | | | ||
| +--------------+-------------------------------+----------------+--------------------------------+ | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ormsbee Despite my urge to call these "refs" as we agreed on in the OEP, I think it's best to still use the "key" nomenclature as long as the archive format is calling them "keys".