Skip to content

Commit 8a43b4d

Browse files
feanilclaude
andcommitted
feat: Add installable tutor plugin for the sample plugin
Replaces the non-functional sample_plugin.py placeholder with a proper installable Python package under tutor/tutorsampleplugin/. The plugin: - Installs openedx-sample-plugin (PyPI) into LMS and CMS images via the openedx-dockerfile-post-python-requirements patch - Installs @openedx/sample-plugin (npm) into all MFE images via the mfe-dockerfile-post-npm-install patch (global rather than per-MFE because env.config.jsx is a shared template rendered for all MFEs) - Imports CourseList via mfe-env-config-buildtime-imports - Configures the org.openedx.frontend.learner_dashboard.course_list.v1 slot to hide the default content and insert the sample plugin's CourseList Gracefully degrades when tutor-mfe is not installed (frontend hooks are skipped via ImportError guard). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 483ec2a commit 8a43b4d

4 files changed

Lines changed: 128 additions & 16 deletions

File tree

tutor/pyproject.toml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
[build-system]
2+
requires = ["setuptools", "setuptools-scm>8.1"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "tutor-sample-plugin"
7+
description = "Tutor plugin for the Open edX Sample Plugin"
8+
requires-python = ">=3.11"
9+
license = "Apache-2.0"
10+
authors = [
11+
{name = "Open edX Project", email = "oscm@openedx.org"},
12+
]
13+
classifiers = [
14+
"Development Status :: 3 - Alpha",
15+
"Intended Audience :: Developers",
16+
"Natural Language :: English",
17+
"Programming Language :: Python :: 3",
18+
"Programming Language :: Python :: 3.12",
19+
]
20+
keywords = ["tutor", "openedx", "plugin"]
21+
dependencies = ["tutor>=17.0.0"]
22+
dynamic = ["readme", "version"]
23+
24+
[project.entry-points."tutor.plugin.v1"]
25+
sample_plugin = "tutorsampleplugin.plugin"
26+
27+
[project.urls]
28+
Homepage = "https://github.com/openedx/sample-plugin"
29+
Repository = "https://github.com/openedx/sample-plugin"
30+
31+
[tool.setuptools.dynamic]
32+
readme = {file = ["README.md"], content-type = "text/markdown"}
33+
34+
[tool.setuptools.packages.find]
35+
where = ["."]
36+
include = ["tutorsampleplugin*"]
37+
38+
[tool.setuptools_scm]
39+
# The root for the git repo is one directory up.
40+
root = ".."
41+
version_scheme = "only-version"
42+
local_scheme = "no-local-version"

tutor/sample_plugin.py

Lines changed: 0 additions & 16 deletions
This file was deleted.

tutor/tutorsampleplugin/__init__.py

Whitespace-only changes.

tutor/tutorsampleplugin/plugin.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""
2+
Tutor plugin for the Open edX Sample Plugin.
3+
4+
Installs the backend Django app (openedx-sample-plugin from PyPI) into LMS/CMS
5+
and configures the frontend MFE slot (from @openedx/sample-plugin on npm) in the
6+
learner-dashboard.
7+
8+
Requirements:
9+
tutor>=17.0.0
10+
tutor-mfe (for frontend slot configuration)
11+
"""
12+
13+
from tutor import hooks
14+
15+
try:
16+
from tutormfe.hooks import PLUGIN_SLOTS
17+
_tutormfe_available = True
18+
except ImportError:
19+
_tutormfe_available = False
20+
21+
# ---------------------------------------------------------------------------
22+
# Backend: Install the Django app plugin into LMS and CMS images
23+
# ---------------------------------------------------------------------------
24+
# The openedx-dockerfile-post-python-requirements patch runs after pip
25+
# installs the base Open edX requirements. Plugins installed here are
26+
# available in both LMS and CMS containers.
27+
# ---------------------------------------------------------------------------
28+
29+
hooks.Filters.ENV_PATCHES.add_item((
30+
"openedx-dockerfile-post-python-requirements",
31+
"RUN pip install openedx-sample-plugin",
32+
))
33+
34+
# ---------------------------------------------------------------------------
35+
# Frontend: Install npm package and configure the learner-dashboard slot
36+
# ---------------------------------------------------------------------------
37+
# Only runs when tutor-mfe is installed, so the plugin degrades gracefully
38+
# if someone uses this plugin without the MFE plugin.
39+
# ---------------------------------------------------------------------------
40+
41+
if _tutormfe_available:
42+
# Step 1: Install the npm package into all MFE images.
43+
# Ideally this would use mfe-dockerfile-post-npm-install-learner-dashboard
44+
# to scope installation to only the MFE that needs it, but env.config.jsx
45+
# is a single shared file rendered for all MFEs. The buildtime import below
46+
# must resolve in every MFE's node_modules, so we install it globally.
47+
# The plugin slot config is still scoped to learner-dashboard at runtime.
48+
hooks.Filters.ENV_PATCHES.add_item((
49+
"mfe-dockerfile-post-npm-install",
50+
"RUN npm install @openedx/sample-plugin",
51+
))
52+
53+
# Step 2: Import the CourseList component in the MFE env config so it is
54+
# in scope when the plugin slot configuration is evaluated at runtime.
55+
# The mfe-env-config-buildtime-imports patch injects import statements
56+
# into the generated env.config.jsx file.
57+
hooks.Filters.ENV_PATCHES.add_item((
58+
"mfe-env-config-buildtime-imports",
59+
"import { CourseList } from '@openedx/sample-plugin';",
60+
))
61+
62+
# Step 3: Configure the course list plugin slot.
63+
# - Hide the default CourseList that ships with the learner-dashboard.
64+
# - Insert our custom CourseList that adds archive/unarchive functionality.
65+
#
66+
# Slot ID: org.openedx.frontend.learner_dashboard.course_list.v1
67+
# Props passed by the slot: courseListData (visibleList, numPages,
68+
# setPageNumber, filterOptions, showFilters)
69+
PLUGIN_SLOTS.add_item((
70+
"learner-dashboard",
71+
"org.openedx.frontend.learner_dashboard.course_list.v1",
72+
"""
73+
{
74+
op: PLUGIN_OPERATIONS.Hide,
75+
widgetId: 'default_contents',
76+
},
77+
{
78+
op: PLUGIN_OPERATIONS.Insert,
79+
widget: {
80+
id: 'sample_plugin_course_list',
81+
type: DIRECT_PLUGIN,
82+
priority: 50,
83+
RenderWidget: CourseList,
84+
},
85+
}""",
86+
))

0 commit comments

Comments
 (0)