diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1ff0e970..2fb8d9df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,13 @@ instructions, because git commits are used to generate release notes:
+
+## v18.0.0 (2024-06-19)
+
+- 💥[Feature] Upgrade to Redwood (by @hinakhadim)
+- [Feature] Enable `atlas pull` on all Micro-frontends. (by @omarithawi)
+- 💥[Feature] Use `ATLAS_OPTIONS` for `atlas pull`. This breaks the `i18n-merge.js` custom locale Tutor MFE feature in favor of [OEP-58](https://docs.openedx.org/en/latest/developers/concepts/oep58.html) `atlas pull` options. (by @omarithawi)
+
## v17.0.1 (2024-03-26)
diff --git a/README.rst b/README.rst
index 6ead5527..0a1a9c51 100644
--- a/README.rst
+++ b/README.rst
@@ -130,6 +130,7 @@ Adding new MFEs
- As of Tutor v16 (Palm release) it is no longer possible to add new MFEs by creating ``*_MFE_APP`` settings. Instead, users must implement the approach described below.
- As of Tutor v17 (Quince release) you must make sure that the git URL of your MFE repository ends with ``.git``. Otherwise the plugin build will fail.
+- As of Tutor v18 (Redwood release) all MFEs must provide a ``make pull_translations`` command. Otherwise the plugin build will fail. Providing an empty command is enough to bypass this requirement. See the `Custom translations section <#mfe-custom-translations>`_ for more information.
Other MFE developers can take advantage of this plugin to deploy their own MFEs. To declare a new MFE, create a Tutor plugin and add your MFE configuration to the ``tutormfe.hooks.MFE_APPS`` filter. This configuration should include the name, git repository (and optionally: git branch or tag) and development port. For example:
@@ -163,32 +164,26 @@ To disable an existing MFE, remove the corresponding entry from the ``MFE_APPS``
mfes.pop("profile")
return mfes
-Adding custom translations to your MFEs
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This plugin makes it possible to change existing and add new translation strings to MFEs. Here is how to do it:
-
-1. Identify the ID of the string you would like to translate. For instance, the ID of the "Account Information" string in the account MFE is "account.settings.section.account.information" (see `source `__).
-2. Create a folder and i18n file corresponding to your MFE app and language in the Tutor root. This location of this file should be ``/path/to/tutor/env/plugins/mfe/build/mfe/i18n//.json``. For instance, to add French ("fr") translation strings to the account MFE, run::
-
- cd "$(tutor config printroot)/env/plugins/mfe/build/mfe/i18n/"
- mkdir account
- touch account/fr.json
+Using custom translations to your MFEs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-3. Add your entries to this file in JSON format, where the key is the string ID and the value is the actual string. For instance:
+.. _mfe-custom-translations:
-.. code-block::json
+During docker image build, this plugin runs ``make pull_translations`` for each Micro-frontend. This
+program is used in the ``Dockerfile`` to pull translations from the `openedx/openedx-translations repository `_ via `openedx-atlas `_.
- {
- "account.settings.section.account.information": "Information du compte"
- }
+The ``make pull_translations`` command passes the ``ATLAS_OPTIONS`` environment variable to the ``atlas pull`` command. This allows specifying a custom repository or branch to pull translations from.
-4. Rebuild the MFE image and restart the MFE with::
+Translations in the MFE plugin as well as other Tutor plugins can be customized with the following configuration
+variables:
- tutor images build mfe
- tutor local start -d
+- ``ATLAS_REVISION`` (default: ``"main"`` on nightly and ``"{{ OPENEDX_COMMON_VERSION }}"`` if a named release is used)
+- ``ATLAS_REPOSITORY`` (default: ``"openedx/openedx-translations"``).
+- ``ATLAS_OPTIONS`` (default: ``""``) Pass additional arguments to ``atlas pull``. Refer to the `atlas documentations `_ for more information.
-Your custom translation strings should now appear in your app.
+The
+`Getting and customizing Translations `_
+section in the Tutor configuration documentation explains how to do this.
Customising MFEs
~~~~~~~~~~~~~~~~
diff --git a/setup.py b/setup.py
index 1234f35c..6e427edd 100644
--- a/setup.py
+++ b/setup.py
@@ -40,8 +40,8 @@ def load_about():
packages=find_packages(exclude=["tests*"]),
include_package_data=True,
python_requires=">=3.8",
- install_requires=["tutor>=17.0.0,<18.0.0"],
- extras_require={"dev": ["tutor[dev]>=17.0.0,<18.0.0"]},
+ install_requires=["tutor>=18.0.0,<19.0.0"],
+ extras_require={"dev": ["tutor[dev]>=18.0.0,<19.0.0"]},
entry_points={"tutor.plugin.v1": ["mfe = tutormfe.plugin"]},
classifiers=[
"Development Status :: 5 - Production/Stable",
diff --git a/tutormfe/__about__.py b/tutormfe/__about__.py
index dba3a77b..c6a8b8ed 100644
--- a/tutormfe/__about__.py
+++ b/tutormfe/__about__.py
@@ -1 +1 @@
-__version__ = "17.0.1"
+__version__ = "18.0.0"
diff --git a/tutormfe/hooks.py b/tutormfe/hooks.py
index e62c2eb2..e23d461d 100644
--- a/tutormfe/hooks.py
+++ b/tutormfe/hooks.py
@@ -3,6 +3,7 @@
the tutor-mfe hooks would be created in the context of some other plugin that imports
them.
"""
+
from __future__ import annotations
import typing as t
diff --git a/tutormfe/patches/openedx-lms-development-settings b/tutormfe/patches/openedx-lms-development-settings
index 2fcc22e8..79da7401 100644
--- a/tutormfe/patches/openedx-lms-development-settings
+++ b/tutormfe/patches/openedx-lms-development-settings
@@ -36,9 +36,11 @@ MFE_CONFIG["ACCOUNT_SETTINGS_URL"] = ACCOUNT_MICROFRONTEND_URL
{% endif %}
{% if get_mfe("course-authoring") %}
-MFE_CONFIG["ENABLE_NEW_EDITOR_PAGES"] = True
-MFE_CONFIG["ENABLE_PROGRESS_GRAPH_SETTINGS"] = True
MFE_CONFIG["COURSE_AUTHORING_MICROFRONTEND_URL"] = "http://{{ MFE_HOST }}:{{ get_mfe("course-authoring")["port"] }}/course-authoring"
+MFE_CONFIG["ENABLE_ASSETS_PAGE"] = "true"
+MFE_CONFIG["ENABLE_HOME_PAGE_COURSE_API_V2"] = "true"
+MFE_CONFIG["ENABLE_PROGRESS_GRAPH_SETTINGS"] = "true"
+MFE_CONFIG["ENABLE_TAGGING_TAXONOMY_PAGES"] = "true"
{% endif %}
{% if get_mfe("discussions") %}
diff --git a/tutormfe/patches/openedx-lms-production-settings b/tutormfe/patches/openedx-lms-production-settings
index 37b79471..d7b7a42a 100644
--- a/tutormfe/patches/openedx-lms-production-settings
+++ b/tutormfe/patches/openedx-lms-production-settings
@@ -37,9 +37,11 @@ MFE_CONFIG["ACCOUNT_SETTINGS_URL"] = ACCOUNT_MICROFRONTEND_URL
{% endif %}
{% if get_mfe("course-authoring") %}
-MFE_CONFIG["ENABLE_NEW_EDITOR_PAGES"] = True
-MFE_CONFIG["ENABLE_PROGRESS_GRAPH_SETTINGS"] = True
MFE_CONFIG["COURSE_AUTHORING_MICROFRONTEND_URL"] = "{% if ENABLE_HTTPS %}https://{% else %}http://{% endif %}{{ MFE_HOST }}/course-authoring"
+MFE_CONFIG["ENABLE_ASSETS_PAGE"] = "true"
+MFE_CONFIG["ENABLE_HOME_PAGE_COURSE_API_V2"] = "true"
+MFE_CONFIG["ENABLE_PROGRESS_GRAPH_SETTINGS"] = "true"
+MFE_CONFIG["ENABLE_TAGGING_TAXONOMY_PAGES"] = "true"
{% endif %}
{% if get_mfe("discussions") %}
diff --git a/tutormfe/templates/mfe/apps/mfe/webpack.dev-tutor.config.js b/tutormfe/templates/mfe/apps/mfe/webpack.dev-tutor.config.js
index 8a46d4c8..9bfda27a 100644
--- a/tutormfe/templates/mfe/apps/mfe/webpack.dev-tutor.config.js
+++ b/tutormfe/templates/mfe/apps/mfe/webpack.dev-tutor.config.js
@@ -4,7 +4,7 @@ const fs = require('fs');
const baseDevConfig = (
fs.existsSync('./webpack.dev.config.js')
? require('./webpack.dev.config.js')
- : require('@edx/frontend-build/config/webpack.dev.config.js')
+ : require('@openedx/frontend-build/config/webpack.dev.config.js')
);
module.exports = merge(baseDevConfig, {
diff --git a/tutormfe/templates/mfe/build/mfe/Dockerfile b/tutormfe/templates/mfe/build/mfe/Dockerfile
index 732000f2..fe62ff24 100644
--- a/tutormfe/templates/mfe/build/mfe/Dockerfile
+++ b/tutormfe/templates/mfe/build/mfe/Dockerfile
@@ -20,16 +20,6 @@ RUN mkdir -p /openedx/app /openedx/env
WORKDIR /openedx/app
ENV PATH /openedx/app/node_modules/.bin:${PATH}
-######## i18n strings
-FROM base AS i18n
-COPY ./i18n /openedx/i18n
-RUN chmod a+x /openedx/i18n/*.js
-RUN echo "copying i18n data" \
- {%- for app_name, app in iter_mfes() %}
- && mkdir -p /openedx/i18n/{{ app_name }} \
- {%- endfor %}
- echo "done."
-
{% for app_name, app in iter_mfes() %}
####################### {{ app_name }} MFE
######## {{ app_name }} (git)
@@ -42,14 +32,6 @@ ADD --keep-git-dir=true {{ app["repository"] }}#{{ app.get("version", MFE_COMMON
FROM scratch as {{ app_name }}-src
COPY --from={{ app_name }}-git /openedx/app /
-######## {{ app_name }} (i18n)
-FROM base AS {{ app_name }}-i18n
-COPY --from={{ app_name }}-src / /openedx/app
-COPY --from=i18n /openedx/i18n/{{ app_name }} /openedx/i18n/{{ app_name }}
-COPY --from=i18n /openedx/i18n/i18n-merge.js /openedx/i18n/i18n-merge.js
-RUN stat /openedx/app/src/i18n/messages 2> /dev/null || (echo "missing messages folder" && mkdir -p /openedx/app/src/i18n/messages)
-RUN /openedx/i18n/i18n-merge.js /openedx/app/src/i18n/messages /openedx/i18n/{{ app_name }} /openedx/app/src/i18n/messages
-
######## {{ app_name }} (common)
FROM base AS {{ app_name }}-common
COPY --from={{ app_name }}-src /package.json /openedx/app/package.json
@@ -65,13 +47,8 @@ RUN --mount=type=cache,target=/root/.npm,sharing=shared npm clean-install --no-a
{{ patch("mfe-dockerfile-post-npm-install") }}
{{ patch("mfe-dockerfile-post-npm-install-{}".format(app_name)) }}
COPY --from={{ app_name }}-src / /openedx/app
-COPY --from={{ app_name }}-i18n /openedx/app/src/i18n/messages /openedx/app/src/i18n/messages
-# Whenever a new MFE supports Atlas, it should be added to this list.
-# When all MFEs support Atlas, this if-statement should be removed.
-{% if app_name in ["communications"] %}
-RUN make OPENEDX_ATLAS_PULL=true pull_translations
-{% endif %}
+RUN make OPENEDX_ATLAS_PULL=true ATLAS_OPTIONS="--repository={{ ATLAS_REPOSITORY }} --revision={{ ATLAS_REVISION }} {{ ATLAS_OPTIONS }}" pull_translations
EXPOSE {{ app['port'] }}
diff --git a/tutormfe/templates/mfe/build/mfe/i18n/i18n-merge.js b/tutormfe/templates/mfe/build/mfe/i18n/i18n-merge.js
deleted file mode 100755
index c8cceda5..00000000
--- a/tutormfe/templates/mfe/build/mfe/i18n/i18n-merge.js
+++ /dev/null
@@ -1,47 +0,0 @@
-#! /usr/bin/env node
-// Add to the current folder your custom translation strings, with the following file hierarchy:
-//
-// i18n/
-// /
-// .json
-//
-// For instance, to override French strings from the payment app:
-//
-// i18n/
-// payment/
-// fr.json
-//
-// Your custom translation strings will automatically be compiled in the MFE Docker image.
-
-const fs = require('fs');
-const path = require('path');
-
-function main() {
- // Merge the messages from multiple directories and aggregate the content in a single directory.
- // This is certainly not great idiomatic nodejs code. Please open a PR to improve this bit!
- merge(process.argv[2], process.argv[3], process.argv[4])
-}
-
-function merge(dir1, dir2, outputDir) {
- fs.readdirSync(dir1, {
- withFileTypes: true
- }).forEach(file1 => {
- if (file1.isFile() && file1.name.endsWith(".json")) {
- var path1 = path.resolve(path.join(dir1, file1.name));
- var data1 = require(path1);
- var path2 = path.resolve(path.join(dir2, file1.name));
- fs.access(path2, (err) => {
- if (err) {
- return;
- }
- var pathOutput = path.resolve(path.join(outputDir, file1.name));
- console.log("Merging i18 strings from " + path1 + " with " + path2 + " to " + pathOutput);
- var data2 = require(path2);
- var final = Object.assign(data1, data2);
- fs.writeFileSync(pathOutput, JSON.stringify(final, null, 2));
- });
- }
- });
-}
-
-main()
diff --git a/tutormfe/templates/mfe/tasks/lms/init b/tutormfe/templates/mfe/tasks/lms/init
index 69e364f0..cf10abb6 100644
--- a/tutormfe/templates/mfe/tasks/lms/init
+++ b/tutormfe/templates/mfe/tasks/lms/init
@@ -24,20 +24,54 @@ site-configuration unset --domain={{ LMS_HOST }}:8000 ENABLE_PROFILE_MICROFRONTE
{% if is_mfe_enabled("learning") %}
(./manage.py lms waffle_flag --list | grep course_home.course_home_mfe_progress_tab) || ./manage.py lms waffle_flag --create --everyone course_home.course_home_mfe_progress_tab
+(./manage.py lms waffle_flag --list | grep courseware.enable_navigation_sidebar) || ./manage.py lms waffle_flag --create --deactivate courseware.enable_navigation_sidebar
+(./manage.py lms waffle_flag --list | grep courseware.always_open_auxiliary_sidebar) || ./manage.py lms waffle_flag --create --deactivate courseware.always_open_auxiliary_sidebar
{% else %}
./manage.py lms waffle_delete --flags course_home.course_home_mfe_progress_tab
+./manage.py lms waffle_delete --flags courseware.enable_navigation_sidebar
+./manage.py lms waffle_delete --flags courseware.always_open_auxiliary_sidebar
{% endif %}
{% if is_mfe_enabled("course-authoring") %}
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_advanced_settings_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_advanced_settings_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_certificates_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_certificates_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_course_outline_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_course_outline_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_course_team_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_course_team_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_custom_pages) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_custom_pages
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_export_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_export_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_files_uploads_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_files_uploads_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_grading_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_grading_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_group_configurations_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_group_configurations_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_import_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_import_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_schedule_details_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_schedule_details_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_textbooks_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_textbooks_page
+(./manage.py lms waffle_flag --list | grep contentstore.new_studio_mfe.use_new_updates_page) || ./manage.py lms waffle_flag --create --everyone contentstore.new_studio_mfe.use_new_updates_page
(./manage.py lms waffle_flag --list | grep discussions.pages_and_resources_mfe) || ./manage.py lms waffle_flag --create --everyone discussions.pages_and_resources_mfe
-(./manage.py lms waffle_flag --list | grep new_core_editors.use_new_text_editor) || ./manage.py lms waffle_flag --create --deactivate new_core_editors.use_new_text_editor
-(./manage.py lms waffle_flag --list | grep new_core_editors.use_new_video_editor) || ./manage.py lms waffle_flag --create --deactivate new_core_editors.use_new_video_editor
-(./manage.py lms waffle_flag --list | grep new_core_editors.use_new_problem_editor) || ./manage.py lms waffle_flag --create --deactivate new_core_editors.use_new_problem_editor
+(./manage.py lms waffle_flag --list | grep new_core_editors.use_new_problem_editor) || ./manage.py lms waffle_flag --create --everyone new_core_editors.use_new_problem_editor
+(./manage.py lms waffle_flag --list | grep new_core_editors.use_new_text_editor) || ./manage.py lms waffle_flag --create --everyone new_core_editors.use_new_text_editor
+(./manage.py lms waffle_flag --list | grep new_core_editors.use_new_video_editor) || ./manage.py lms waffle_flag --create --everyone new_core_editors.use_new_video_editor
+(./manage.py lms waffle_flag --list | grep new_studio_mfe.use_new_home_page) || ./manage.py lms waffle_flag --create --everyone new_studio_mfe.use_new_home_page
+(./manage.py lms waffle_flag --list | grep new_studio_mfe.use_tagging_taxonomy_list_page) || ./manage.py lms waffle_flag --create --everyone new_studio_mfe.use_tagging_taxonomy_list_page
{% else %}
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_advanced_settings_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_certificates_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_course_outline_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_course_team_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_custom_pages
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_export_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_files_uploads_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_grading_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_group_configurations_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_import_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_schedule_details_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_textbooks_page
+./manage.py lms waffle_delete --flags contentstore.new_studio_mfe.use_new_updates_page
./manage.py lms waffle_delete --flags discussions.pages_and_resources_mfe
+./manage.py lms waffle_delete --flags new_core_editors.use_new_problem_editor
./manage.py lms waffle_delete --flags new_core_editors.use_new_text_editor
./manage.py lms waffle_delete --flags new_core_editors.use_new_video_editor
-./manage.py lms waffle_delete --flags new_core_editors.use_new_problem_editor
+./manage.py lms waffle_delete --flags new_studio_mfe.use_new_home_page
+./manage.py lms waffle_delete --flags new_studio_mfe.use_tagging_taxonomy_list_page
{% endif %}
{% if is_mfe_enabled("discussions") %}