Skip to content

Commit b8f46f5

Browse files
kdmccormickclaude
andcommitted
docs: Rework READMEs
Co-Authored-By: Claude Opus 4.7 (1M Context) <noreply@anthropic.com>
1 parent e7a17d1 commit b8f46f5

5 files changed

Lines changed: 151 additions & 1875 deletions

File tree

README.md

Lines changed: 39 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -1,243 +1,66 @@
11
# Open edX Sample Plugin
22

3-
A comprehensive example demonstrating all major plugin interfaces available in the Open edX platform. This repository shows how to extend Open edX functionality without modifying core platform code.
3+
A worked example of every major Open edX plugin interface, built around a small "course archiving" feature you can run end-to-end. Use this repo as a reference when building your own Open edX plugin.
44

5-
## Table of Contents
5+
This is a monorepo of four sub-packages, each demonstrating one extension point:
66

7-
- [What This Repository Demonstrates](#what-this-repository-demonstrates)
8-
- [Plugin Types & Official Documentation](#plugin-types--official-documentation)
9-
- [Quick Start Guide](#quick-start-guide)
10-
- [Learning Path for New Plugin Developers](#learning-path-for-new-plugin-developers)
11-
- [Repository Structure](#repository-structure)
12-
- [Development Workflows](#development-workflows)
13-
- [Integration Examples](#integration-examples)
14-
- [Troubleshooting](#troubleshooting)
15-
- [Additional Resources](#additional-resources)
7+
| Sub-package | Plugin type | What it does |
8+
|---|---|---|
9+
| [`backend-plugin-sample/`](./backend-plugin-sample/) | [Django app plugin](https://docs.openedx.org/projects/edx-django-utils/en/latest/plugins/how_tos/how_to_create_a_plugin_app.html) | Adds a `CourseArchiveStatus` model with a REST API, an [Open edX Events](https://docs.openedx.org/projects/openedx-events/en/latest/) handler, and an [Open edX Filters](https://docs.openedx.org/projects/openedx-filters/en/latest/) pipeline step |
10+
| [`frontend-plugin-sample/`](./frontend-plugin-sample/) | [MFE plugin slot widget](https://docs.openedx.org/en/latest/site_ops/how-tos/use-frontend-plugin-slots.html) | Replaces the learner-dashboard course list with one that lets learners archive courses |
11+
| [`brand-sample/`](./brand-sample/) | [Paragon brand package](https://github.com/openedx/paragon) | An autumn-inspired color palette |
12+
| [`tutor-contrib-sample/`](./tutor-contrib-sample/) | [Tutor plugin](https://docs.tutor.edly.io/) | Installs and wires up the three above for a Tutor-based deployment |
1613

17-
## What This Repository Demonstrates
14+
## Development with Tutor
1815

19-
This sample plugin showcases the **Open edX Hooks Extension Framework**, which allows you to extend the platform in a stable and maintainable way. The framework provides two main types of hooks:
16+
Requires [Tutor](https://docs.tutor.edly.io/install.html) >= 20 with [tutor-mfe](https://github.com/overhangio/tutor-mfe), and an Open edX environment that supports design tokens (Paragon >= 23, "Teak" release or later).
2017

21-
- **Events**: React to things happening in the platform (e.g., when a course is published)
22-
- **Filters**: Modify platform behavior (e.g., change where course about pages redirect)
18+
### Running the demo as-is
2319

24-
**Key Concept**: All extensions are implemented as standard Django plugins that integrate seamlessly with edx-platform.
25-
26-
**Official Documentation**: [Hooks Extension Framework Overview](https://docs.openedx.org/en/latest/developers/concepts/hooks_extension_framework.html)
27-
28-
## Plugin Types & Official Documentation
29-
30-
| Plugin Type | What It Does | Official Documentation | Sample Code | When To Use |
31-
|-------------|--------------|------------------------|-------------|-------------|
32-
| **Django App Plugin** | Add models, APIs, views, and business logic | [How to create a plugin app](https://docs.openedx.org/projects/edx-django-utils/en/latest/plugins/how_tos/how_to_create_a_plugin_app.html) | [`backend-plugin-sample/`](./backend-plugin-sample/) | Adding new functionality, APIs, or data models |
33-
| **Events (Signals)** | React to platform events | [Open edX Events Guide](https://docs.openedx.org/projects/openedx-events/en/latest/) | [`backend-plugin-sample/openedx_plugin_sample/signals.py`](./backend-plugin-sample/openedx_plugin_sample/signals.py) | Integrating with external systems, audit logging |
34-
| **Filters** | Modify platform behavior | [Using Open edX Filters](https://docs.openedx.org/projects/openedx-filters/en/latest/how-tos/using-filters.html) | [`backend-plugin-sample/openedx_plugin_sample/pipeline.py`](./backend-plugin-sample/openedx_plugin_sample/pipeline.py) | Customizing business logic, URL redirects |
35-
| **Frontend Slots** | Customize MFE interfaces | [Frontend Plugin Slots](https://docs.openedx.org/en/latest/site_ops/how-tos/use-frontend-plugin-slots.html) | [`frontend-plugin-sample/`](./frontend-plugin-sample/) | UI customization, adding new components |
36-
| **Brand Packages** | Customize theming | [Open edX Brand Package Interface](https://github.com/openedx/brand-openedx) | [`brand-sample/`](./brand-sample/) | UI theming |
37-
| **Tutor Plugin** | Deploy plugins easily | [Tutor Plugin Development](https://docs.tutor.edly.io/) | [`tutor-contrib-sample/`](./tutor-contrib-sample/) | Simplified deployment and configuration |
38-
39-
## Quick Start Guide
40-
41-
### Prerequisites
42-
1. **Platform Setup**: Follow the [Open edX Development Setup](https://docs.openedx.org/en/latest/developers/how-tos/get-ready-for-python-dev.html)
43-
2. **Understanding**: Read the [Platform Overview](https://docs.openedx.org/en/latest/developers/concepts/platform_overview.html)
44-
45-
### Option 1: Development with Tutor (Recommended)
20+
The `tutor-contrib-sample` plugin in this repo installs the published backend, frontend, and brand packages and wires them into Tutor:
4621

4722
```bash
48-
# Bind-mount backend source into Tutor image and containers.
49-
tutor mounts add "$PWD/backend-plugin-sample"
50-
51-
# Rebuild image, run migrations, reboot containers:
52-
tutor dev launch
53-
54-
# Frontend Plugin Setup (for learner-dashboard MFE development)
55-
# Add env.config.jsx and module.config.js (see frontend-plugin-sample/README.md)
56-
# Then, install and run.
57-
cd path/to/frontend-app-learner-dashboard && npm ci && npm run dev
58-
```
59-
60-
### Option 2: Development without Tutor
61-
62-
```bash
63-
# In your edx-platform directory
64-
pip install -e /path/to/sample-plugin/backend-plugin-sample
65-
66-
# Enable Learner Dashboard MFE
67-
# Go to http://localhost:18000/admin/waffle/flag/
68-
# Create flag: learner_home_mfe.enabled = Yes
69-
70-
# Run migrations
71-
python manage.py lms migrate
72-
```
73-
74-
### Verification
75-
76-
1. **Backend**: Visit `http://localhost:18000/sample-plugin/api/v1/course-archive-status/`
77-
2. **Frontend**: Check learner dashboard for archive/unarchive functionality
78-
3. **Events**: Check logs for course catalog change events
79-
4. **Filters**: Course about page URLs should redirect to example.com
80-
81-
## Learning Path for New Plugin Developers
82-
83-
### 1. Understand the Architecture
84-
- **Start here**: [Hooks Extension Framework](https://docs.openedx.org/en/latest/developers/concepts/hooks_extension_framework.html)
85-
- **Deep dive**: [OEP-50: Hooks Extension Framework](https://docs.openedx.org/projects/openedx-proposals/en/latest/architectural-decisions/oep-0050-hooks-extension-framework.html)
86-
87-
### 2. Choose Your Plugin Type
88-
Use the table above to identify which type of plugin matches your needs. You can combine multiple types in one plugin.
89-
90-
### 3. Study the Sample Code
91-
- **Backend**: Start with [`backend-plugin-sample/openedx_plugin_sample/apps.py`](./backend-plugin-sample/openedx_plugin_sample/apps.py) to understand plugin registration
92-
- **Events**: Examine [`backend-plugin-sample/openedx_plugin_sample/signals.py`](./backend-plugin-sample/openedx_plugin_sample/signals.py) for event handling patterns
93-
- **Filters**: Review [`backend-plugin-sample/openedx_plugin_sample/pipeline.py`](./backend-plugin-sample/openedx_plugin_sample/pipeline.py) for behavior modification
94-
- **Frontend**: Explore [`frontend-plugin-sample/src/plugin.jsx`](./frontend-plugin-sample/src/plugin.jsx) for UI customization
95-
96-
### 4. Run This Sample
97-
Follow the [Quick Start Guide](#quick-start-guide) to see everything working together.
98-
99-
### 5. Adapt for Your Use Case
100-
Each directory contains detailed README.md files with adaptation guidance.
101-
102-
## Repository Structure
103-
104-
```
105-
sample-plugin/
106-
├── README.md # This file - overview and quick start
107-
├── backend-plugin-sample/
108-
│ ├── README.md # Backend plugin detailed guide
109-
│ ├── openedx_plugin_sample/
110-
│ │ ├── apps.py # Django plugin configuration
111-
│ │ ├── models.py # Database models example
112-
│ │ ├── views.py # REST API endpoints
113-
│ │ ├── signals.py # Event handlers (Open edX Events)
114-
│ │ ├── pipeline.py # Filter implementations (Open edX Filters)
115-
│ │ ├── settings/ # Plugin settings configuration
116-
│ │ └── urls.py # URL routing
117-
│ └── tests/ # Comprehensive test examples
118-
├── frontend-plugin-sample/
119-
│ ├── README.md # Frontend plugin detailed guide
120-
│ ├── src/
121-
│ │ ├── plugin.jsx # React component for MFE slot
122-
│ │ └── index.jsx # Export configuration
123-
│ └── package.json # NPM package configuration
124-
└── tutor-contrib-sample/
125-
├── README.md # Tutor deployment guide
126-
└── sample.py # Tutor plugin configuration
23+
pip install -e ./tutor-contrib-sample
24+
tutor plugins enable sample
25+
tutor dev launch
12726
```
12827

129-
## Development Workflows
130-
131-
### Backend Plugin Development
132-
133-
1. **Setup**: Follow backend setup in [Quick Start](#quick-start-guide)
134-
2. **Development**:
135-
- Modify models in `models.py`
136-
- Add API endpoints in `views.py`
137-
- Implement event handlers in `signals.py`
138-
- Create filters in `pipeline.py`
139-
3. **Testing**: `cd backend-plugin-sample && make test`
140-
4. **Quality**: `cd backend-plugin-sample && make quality`
141-
142-
**Detailed Guide**: See [`backend-plugin-sample/README.md`](./backend-plugin-sample/README.md)
143-
144-
### Frontend Plugin Development
145-
146-
1. **Setup**: Follow frontend setup in [Quick Start](#quick-start-guide)
147-
2. **Development**:
148-
- Modify React components in `frontend-plugin-sample/src/`
149-
- Test with local MFE development server
150-
3. **Testing**: Integration testing with MFE
151-
152-
**Detailed Guide**: See [`frontend-plugin-sample/README.md`](./frontend-plugin-sample/README.md)
28+
This is enough to see everything working: visit the learner dashboard and you should see the customized course list rendered with the brand applied. See [`tutor-contrib-sample/README.md`](./tutor-contrib-sample/README.md) for what each piece of the plugin does.
15329

154-
### Full-Stack Plugin Development
30+
### Hacking on the source
15531

156-
This sample shows how backend and frontend plugins work together:
32+
To edit code in this repo and have your changes apply inside Tutor:
15733

158-
- **Backend** provides API endpoints for course archive status
159-
- **Frontend** consumes these APIs to show archive/unarchive UI
160-
- **Events** log when course information changes
161-
- **Filters** modify course about page URLs
162-
163-
## Integration Examples
164-
165-
### Backend + Frontend Integration
166-
167-
```python
168-
# backend-plugin-sample/openedx_plugin_sample/views.py - Provides API
169-
class CourseArchiveStatusViewSet(viewsets.ModelViewSet):
170-
# API implementation
171-
```
172-
173-
```jsx
174-
// frontend-plugin-sample/src/plugin.jsx - Consumes API
175-
const response = await client.get(
176-
`${lmsBaseUrl}/sample-plugin/api/v1/course-archive-status/`
177-
);
178-
```
179-
180-
### Events + Filters Working Together
181-
182-
```python
183-
# Events: Log course changes
184-
@receiver(COURSE_CATALOG_INFO_CHANGED)
185-
def log_course_info_changed(signal, sender, catalog_info, **kwargs):
186-
logging.info(f"{catalog_info.course_key} has been updated!")
187-
188-
# Filters: Modify course about URLs
189-
class ChangeCourseAboutPageUrl(PipelineStep):
190-
def run_filter(self, url, org, **kwargs):
191-
# Custom URL logic
192-
```
34+
- **Backend**`tutor-contrib-sample` registers `backend-plugin-sample` as a mounted directory, so a single command before launch is enough:
19335

194-
## Troubleshooting
36+
```bash
37+
tutor mounts add "$PWD/backend-plugin-sample"
38+
tutor dev launch
39+
```
19540

196-
### Common Issues
41+
- **Frontend** — bind-mount a local MFE checkout into `tutor-mfe`, then point its webpack at your local `frontend-plugin-sample` checkout. See [`frontend-plugin-sample/README.md`](./frontend-plugin-sample/README.md).
19742

198-
**Plugin not loading:**
199-
- Verify `pyproject.toml` entry points are correct
200-
- Check that plugin app is in INSTALLED_APPS (should be automatic)
201-
- Review Django app plugin configuration in `apps.py`
43+
- **Brand** — use [tutor-contrib-paragon](https://github.com/openedx/openedx-tutor-plugins/tree/main/plugins/tutor-contrib-paragon) to recompile and serve the brand from disk. See [`brand-sample/README.md`](./brand-sample/README.md).
20244

203-
**Events not firing:**
204-
- Confirm signal receivers are imported in `apps.py` ready() method
205-
- Check event is being sent by platform (some events only fire in specific contexts)
206-
- Verify event data structure matches your handler signature
45+
## Development without Tutor
20746

208-
**Filters not working:**
209-
- Ensure filter is registered in Django settings
210-
- Check that filter step class inherits from `PipelineStep`
211-
- Verify `run_filter` method returns correct dictionary format
47+
Assumes you already have edx-platform running locally (bare-metal or devstack-style venv) and at least one MFE checked out.
21248

213-
**Frontend plugin not appearing:**
214-
- Check MFE slot configuration in `env.config.jsx`
215-
- Verify plugin is installed (`npm install`)
216-
- Ensure slot exists in target MFE (check MFE documentation)
49+
- **Backend** — install editable into the edx-platform Python environment and migrate:
21750

218-
### Getting Help
51+
```bash
52+
pip install -e ./backend-plugin-sample
53+
python manage.py lms migrate openedx_plugin_sample
54+
python manage.py cms migrate openedx_plugin_sample
55+
```
21956

220-
1. **Documentation**: Start with official docs linked in the [Plugin Types table](#plugin-types--official-documentation)
221-
2. **Community**: [Open edX Community Slack](https://openedx.org/slack)
222-
3. **Forums**: [Open edX Discuss Forums](https://discuss.openedx.org)
223-
4. **Issues**: Create issues in this repository for sample-specific problems
57+
- **Frontend** — in your MFE checkout, add the `module.config.js` and `env.config.jsx` shown in [`frontend-plugin-sample/README.md`](./frontend-plugin-sample/README.md), then `npm ci && npm start`.
22458

225-
## Additional Resources
59+
- **Brand** — set `PARAGON_THEME_URLS.variants.light.urls.brandOverride` in your MFE's `env.config.js[x]` (or `theme.variants.light.url` in a frontend-base `site.config.tsx`) to `https://cdn.jsdelivr.net/gh/openedx/sample-plugin@main/brand-sample/dist/light.min.css`. See [`brand-sample/README.md`](./brand-sample/README.md) for the full snippet.
22660

227-
### Official Documentation
228-
- **Platform**: [Open edX Developer Documentation](https://docs.openedx.org/en/latest/developers/)
229-
- **Architecture**: [OEP-49: Django App Patterns](https://docs.openedx.org/projects/openedx-proposals/en/latest/best-practices/oep-0049-django-app-patterns.html)
230-
- **Events**: [Open edX Events Reference](https://docs.openedx.org/projects/openedx-events/en/latest/reference/events.html)
231-
- **Filters**: [Open edX Filters Reference](https://docs.openedx.org/projects/openedx-filters/en/latest/reference/filters.html)
232-
- **Frontend**: [Available Frontend Plugin Slots](https://docs.openedx.org/en/latest/site_ops/references/frontend-plugin-slots.html)
61+
> TODO: a fully local brand-development flow without Tutor (recompile + serve from disk) is not yet documented.
23362
234-
### Community Resources
235-
- **Cookiecutter**: [Django App Template](https://github.com/openedx/cookiecutter-django-app) for creating new plugins
236-
- **Examples**: Other Open edX plugins in the [openedx organization](https://github.com/openedx)
237-
- **Best Practices**: [OEP Index](https://docs.openedx.org/projects/openedx-proposals/en/latest/) for architectural guidance
63+
## Getting help
23864

239-
### What This Sample Provides That Official Docs Don't
240-
- **Working Integration**: Complete example showing all plugin types working together
241-
- **Real Business Logic**: Realistic course archiving functionality vs. hello-world examples
242-
- **Development Workflow**: End-to-end development and testing process
243-
- **Troubleshooting**: Common plugin development issues and solutions
65+
- Open edX [community Slack](https://openedx.org/slack) and [discussion forums](https://discuss.openedx.org)
66+
- Issues with this sample specifically: [openedx/sample-plugin issues](https://github.com/openedx/sample-plugin/issues)

0 commit comments

Comments
 (0)