Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/backend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:

defaults:
run:
working-directory: "./backend"
working-directory: "./platform-plugin-sample"

jobs:
run_tests:
Expand Down Expand Up @@ -46,4 +46,4 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unittests
fail_ci_if_error: true
working-directory: "./backend"
working-directory: "./platform-plugin-sample"
22 changes: 11 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,22 @@ jobs:
git_committer_name: "github-actions"
git_committer_email: "actions@users.noreply.github.com"
changelog: "false"
directory: './backend'
directory: './platform-plugin-sample'

- name: Publish | Upload to GitHub Release Assets
uses: python-semantic-release/publish-action@v10.5.3
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.OPENEDX_SEMANTIC_RELEASE_GITHUB_TOKEN }}
tag: ${{ steps.release.outputs.tag }}
directory: './backend'
directory: './platform-plugin-sample'

- name: Upload | Backend Distribution Artifacts
uses: actions/upload-artifact@v4
if: steps.release.outputs.released == 'true'
with:
name: backend-distribution-artifacts
path: backend/dist
path: platform-plugin-sample/dist
if-no-files-found: error

- name: Build | Tutor Plugin
Expand All @@ -69,14 +69,14 @@ jobs:
# setuptools-scm picks it up at build time.
if: steps.release.outputs.released == 'true'
run: pip install build && SETUPTOOLS_SCM_PRETEND_VERSION=${{ steps.release.outputs.version }} python -m build
working-directory: './tutor'
working-directory: './tutor-contrib-sample'

- name: Upload | Tutor Plugin Distribution Artifacts
uses: actions/upload-artifact@v4
if: steps.release.outputs.released == 'true'
with:
name: tutor-distribution-artifacts
path: tutor/dist
path: tutor-contrib-sample/dist
if-no-files-found: error

outputs:
Expand All @@ -102,12 +102,12 @@ jobs:
id: artifact-download
with:
name: backend-distribution-artifacts
path: backend/dist
path: platform-plugin-sample/dist

- name: Publish to PyPi
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: backend/dist
packages-dir: platform-plugin-sample/dist
user: __token__
password: ${{ secrets.PYPI_UPLOAD_TOKEN }}

Expand All @@ -125,12 +125,12 @@ jobs:
uses: actions/download-artifact@v4
with:
name: tutor-distribution-artifacts
path: tutor/dist
path: tutor-contrib-sample/dist

- name: Publish to PyPi
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: tutor/dist
packages-dir: tutor-contrib-sample/dist
user: __token__
password: ${{ secrets.PYPI_UPLOAD_TOKEN }}

Expand All @@ -151,12 +151,12 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: './frontend/.nvmrc'
node-version-file: './frontend-plugin-sample/.nvmrc'

- name: Update the package version and publish
run: |
npm install --include=dev
npm version ${{ needs.release.outputs.version }}
npm run build
npm publish
working-directory: './frontend'
working-directory: './frontend-plugin-sample'
32 changes: 16 additions & 16 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ This is a **sample plugin repository** that demonstrates all major Open edX plug
- **Target Audience**: Developers new to Open edX plugin development

**Repository Structure:**
- `backend/` - Django app plugin with models, APIs, events, and filters
- `frontend/` - React component for MFE slot customization
- `tutor/` - Tutor plugin for easy deployment
- `platform-plugin-sample/` - Django app plugin with models, APIs, events, and filters
- `frontend-plugin-sample/` - React component for MFE slot customization
- `tutor-contrib-sample/` - Tutor plugin for easy deployment
- Each directory has comprehensive README.md files with TOCs

**When Making Changes:**
Expand All @@ -25,21 +25,21 @@ This is a **sample plugin repository** that demonstrates all major Open edX plug
- Keep examples realistic but not overly complex

**Key Files and Their Relationships:**
- `backend/sample_plugin/apps.py` - Plugin registration and Django integration
- `backend/sample_plugin/signals.py` - Open edX Events handlers
- `backend/sample_plugin/pipeline.py` - Open edX Filters implementation
- `backend/sample_plugin/models.py` - CourseArchiveStatus model (business logic)
- `backend/sample_plugin/views.py` - REST API endpoints consumed by frontend
- `frontend/src/plugin.jsx` - React component that replaces course list slot
- `tutor/sample_plugin.py` - Deployment configuration (currently basic template)
- `platform-plugin-sample/openedx_sample_plugin/apps.py` - Plugin registration and Django integration
- `platform-plugin-sample/openedx_sample_plugin/signals.py` - Open edX Events handlers
- `platform-plugin-sample/openedx_sample_plugin/pipeline.py` - Open edX Filters implementation
- `platform-plugin-sample/openedx_sample_plugin/models.py` - CourseArchiveStatus model (business logic)
- `platform-plugin-sample/openedx_sample_plugin/views.py` - REST API endpoints consumed by frontend
- `frontend-plugin-sample/src/plugin.jsx` - React component that replaces course list slot
- `tutor-contrib-sample/tutorsample/plugin.py` - Deployment configuration (currently basic template)

## Build/Lint/Test Commands
- Make sure to set the following so that test output is not too verbose: `export PYTEST_ADDOPTS="--disable-warnings --no-header --tb=short"`
- Backend testing: `cd backend && pytest` or `cd backend && make test`
- Run a single test: `cd backend && pytest tests/test_models.py::test_placeholder`
- Quality checks: `cd backend && make quality`
- Install requirements: `cd backend && make requirements`
- Compile requirements: `cd backend && make compile-requirements`
- Backend testing: `cd platform-plugin-sample && pytest` or `cd platform-plugin-sample && make test`
- Run a single test: `cd platform-plugin-sample && pytest tests/test_models.py::test_placeholder`
- Quality checks: `cd platform-plugin-sample && make quality`
- Install requirements: `cd platform-plugin-sample && make requirements`
- Compile requirements: `cd platform-plugin-sample && make compile-requirements`

## Code Style Guidelines
- Python: Follow PEP 8 with max line length of 120
Expand All @@ -61,7 +61,7 @@ Always run `make quality` and fix issues before creating a PR to ensure consiste
- **Realistic Complexity**: Keep examples practical but not overly complex

### Code Relationships to Preserve
- **Backend ↔ Frontend**: CourseArchiveStatus API in `views.py` consumed by `frontend/src/plugin.jsx`
- **Backend ↔ Frontend**: CourseArchiveStatus API in `views.py` consumed by `frontend-plugin-sample/src/plugin.jsx`
- **Events ↔ Models**: Signal handlers in `signals.py` can update models in `models.py`
- **Settings ↔ Filters**: Filter registration in `settings/common.py` must match classes in `pipeline.py`
- **Apps.py ↔ All**: Plugin configuration affects URL routing, settings, and signal registration
Expand Down
60 changes: 32 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ This sample plugin showcases the **Open edX Hooks Extension Framework**, which a

| Plugin Type | What It Does | Official Documentation | Sample Code | When To Use |
|-------------|--------------|------------------------|-------------|-------------|
| **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/`](./backend/) | Adding new functionality, APIs, or data models |
| **Events (Signals)** | React to platform events | [Open edX Events Guide](https://docs.openedx.org/projects/openedx-events/en/latest/) | [`backend/sample_plugin/signals.py`](./backend/sample_plugin/signals.py) | Integrating with external systems, audit logging |
| **Filters** | Modify platform behavior | [Using Open edX Filters](https://docs.openedx.org/projects/openedx-filters/en/latest/how-tos/using-filters.html) | [`backend/sample_plugin/pipeline.py`](./backend/sample_plugin/pipeline.py) | Customizing business logic, URL redirects |
| **Frontend Slots** | Customize MFE interfaces | [Frontend Plugin Slots](https://docs.openedx.org/en/latest/site_ops/how-tos/use-frontend-plugin-slots.html) | [`frontend/`](./frontend/) | UI customization, adding new components |
| **Brand Packages** | Customize theming | [Open edX Brand Package Interface](https://github.com/openedx/brand-openedx) | [`brand/`](./brand/) | UI theming |
| **Tutor Plugin** | Deploy plugins easily | [Tutor Plugin Development](https://docs.tutor.edly.io/) | [`tutor/`](./tutor/) | Simplified deployment and configuration |
| **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) | [`platform-plugin-sample/`](./platform-plugin-sample/) | Adding new functionality, APIs, or data models |
| **Events (Signals)** | React to platform events | [Open edX Events Guide](https://docs.openedx.org/projects/openedx-events/en/latest/) | [`platform-plugin-sample/openedx_sample_plugin/signals.py`](./platform-plugin-sample/openedx_sample_plugin/signals.py) | Integrating with external systems, audit logging |
| **Filters** | Modify platform behavior | [Using Open edX Filters](https://docs.openedx.org/projects/openedx-filters/en/latest/how-tos/using-filters.html) | [`platform-plugin-sample/openedx_sample_plugin/pipeline.py`](./platform-plugin-sample/openedx_sample_plugin/pipeline.py) | Customizing business logic, URL redirects |
| **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 |
| **Brand Packages** | Customize theming | [Open edX Brand Package Interface](https://github.com/openedx/brand-openedx) | [`brand-sample/`](./brand-sample/) | UI theming |
| **Tutor Plugin** | Deploy plugins easily | [Tutor Plugin Development](https://docs.tutor.edly.io/) | [`tutor-contrib-sample/`](./tutor-contrib-sample/) | Simplified deployment and configuration |

## Quick Start Guide

Expand All @@ -46,21 +46,25 @@ This sample plugin showcases the **Open edX Hooks Extension Framework**, which a
### Option 1: Development with Tutor (Recommended)

```bash
# Backend plugin setup
tutor mounts add "$PWD/backend"
tutor dev launch # Rebuilds image, runs migrations, reboots containers.
# Bind-mount backend source into Tutor image and containers.
# Tutor automatically recognizes the `platform-plugin-*` prefix and knows to
# treat it as an openedx-platform plugin.
tutor mounts add "$PWD/platform-plugin-sample"

# Rebuild image, run migrations, reboot containers:
tutor dev launch

# Frontend Plugin Setup (for learner-dashboard MFE development)
npm install $PWD/frontend
# Add env.config.jsx and module.config.js (see frontend/README.md)
npm install $PWD/frontend-plugin-sample
# Add env.config.jsx and module.config.js (see frontend-plugin-sample/README.md)
npm start
```

### Option 2: Development without Tutor

```bash
# In your edx-platform directory
pip install -e /path/to/sample-plugin/backend
pip install -e /path/to/sample-plugin/platform-plugin-sample

# Enable Learner Dashboard MFE
# Go to http://localhost:18000/admin/waffle/flag/
Expand All @@ -87,10 +91,10 @@ python manage.py lms migrate
Use the table above to identify which type of plugin matches your needs. You can combine multiple types in one plugin.

### 3. Study the Sample Code
- **Backend**: Start with [`backend/sample_plugin/apps.py`](./backend/sample_plugin/apps.py) to understand plugin registration
- **Events**: Examine [`backend/sample_plugin/signals.py`](./backend/sample_plugin/signals.py) for event handling patterns
- **Filters**: Review [`backend/sample_plugin/pipeline.py`](./backend/sample_plugin/pipeline.py) for behavior modification
- **Frontend**: Explore [`frontend/src/plugin.jsx`](./frontend/src/plugin.jsx) for UI customization
- **Backend**: Start with [`platform-plugin-sample/openedx_sample_plugin/apps.py`](./platform-plugin-sample/openedx_sample_plugin/apps.py) to understand plugin registration
- **Events**: Examine [`platform-plugin-sample/openedx_sample_plugin/signals.py`](./platform-plugin-sample/openedx_sample_plugin/signals.py) for event handling patterns
- **Filters**: Review [`platform-plugin-sample/openedx_sample_plugin/pipeline.py`](./platform-plugin-sample/openedx_sample_plugin/pipeline.py) for behavior modification
- **Frontend**: Explore [`frontend-plugin-sample/src/plugin.jsx`](./frontend-plugin-sample/src/plugin.jsx) for UI customization

### 4. Run This Sample
Follow the [Quick Start Guide](#quick-start-guide) to see everything working together.
Expand All @@ -103,9 +107,9 @@ Each directory contains detailed README.md files with adaptation guidance.
```
sample-plugin/
├── README.md # This file - overview and quick start
├── backend/
├── platform-plugin-sample/
│ ├── README.md # Backend plugin detailed guide
│ ├── sample_plugin/
│ ├── openedx_sample_plugin/
│ │ ├── apps.py # Django plugin configuration
│ │ ├── models.py # Database models example
│ │ ├── views.py # REST API endpoints
Expand All @@ -114,15 +118,15 @@ sample-plugin/
│ │ ├── settings/ # Plugin settings configuration
│ │ └── urls.py # URL routing
│ └── tests/ # Comprehensive test examples
├── frontend/
├── frontend-plugin-sample/
│ ├── README.md # Frontend plugin detailed guide
│ ├── src/
│ │ ├── plugin.jsx # React component for MFE slot
│ │ └── index.jsx # Export configuration
│ └── package.json # NPM package configuration
└── tutor/
└── tutor-contrib-sample/
├── README.md # Tutor deployment guide
└── sample_plugin.py # Tutor plugin configuration
└── openedx_sample_plugin.py # Tutor plugin configuration
```

## Development Workflows
Expand All @@ -135,20 +139,20 @@ sample-plugin/
- Add API endpoints in `views.py`
- Implement event handlers in `signals.py`
- Create filters in `pipeline.py`
3. **Testing**: `cd backend && make test`
4. **Quality**: `cd backend && make quality`
3. **Testing**: `cd platform-plugin-sample && make test`
4. **Quality**: `cd platform-plugin-sample && make quality`

**Detailed Guide**: See [`backend/README.md`](./backend/README.md)
**Detailed Guide**: See [`platform-plugin-sample/README.md`](./platform-plugin-sample/README.md)

### Frontend Plugin Development

1. **Setup**: Follow frontend setup in [Quick Start](#quick-start-guide)
2. **Development**:
- Modify React components in `frontend/src/`
- Modify React components in `frontend-plugin-sample/src/`
- Test with local MFE development server
3. **Testing**: Integration testing with MFE

**Detailed Guide**: See [`frontend/README.md`](./frontend/README.md)
**Detailed Guide**: See [`frontend-plugin-sample/README.md`](./frontend-plugin-sample/README.md)

### Full-Stack Plugin Development

Expand All @@ -164,13 +168,13 @@ This sample shows how backend and frontend plugins work together:
### Backend + Frontend Integration

```python
# backend/sample_plugin/views.py - Provides API
# platform-plugin-sample/openedx_sample_plugin/views.py - Provides API
class CourseArchiveStatusViewSet(viewsets.ModelViewSet):
# API implementation
```

```jsx
// frontend/src/plugin.jsx - Consumes API
// frontend-plugin-sample/src/plugin.jsx - Consumes API
const response = await client.get(
`${lmsBaseUrl}/sample-plugin/api/v1/course-archive-status/`
);
Expand Down
21 changes: 21 additions & 0 deletions brand-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# brand-sample

**This is a simple example brand package that changes the `brand` color to purple.**

### Before
![Screenshot of the Authn MFE with this brand package enabled](./docs/images/authn-without-theme.png)

### After
![Screenshot of the Authn MFE with this brand package enabled](./docs/images/authn-with-theme.png)

## Using this brand package

> [!IMPORTANT]
> These instructions assume you have a [tutor](https://docs.tutor.edly.io/index.html) environment that supports design tokens
> * **Paragon >= 23**
> * **Open edX "Teak" release or later (Tutor >= 20)**
> * **Tutor >= 20**

## TODO

Update this readme now that most of the instructions were moved into plugin.py
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion brand/src/README.md → brand-sample/src/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# brand-example-purple
# brand-sample

This directory contains the source design tokens for the example brand package.

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions brand/src/package.json → brand-sample/src/package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
{
"name": "brand-example-purple",
"name": "brand-sample",
"version": "0.0.1",
"description": "An example design-token-based brand package for Open edX applications.",
"repository": {
"type": "git",
"url": "git+https://github.com/openedx/sample-plugin.git",
"directory": "brand"
"directory": "brand-sample"
},
"author": "Brian Smith",
"license": "MIT",
"bugs": {
"url": "https://github.com/openedx/sample-plugin/issues"
},
"homepage": "https://github.com/openedx/sample-plugin/tree/main/brand#readme",
"homepage": "https://github.com/openedx/sample-plugin/tree/main/brand-sample#readme",
"scripts": {
"build-tokens": "paragon build-tokens --source ./tokens/src --build-dir ./paragon/css --source-tokens-only --exclude-core",
"build-scss": "paragon build-scss --themesPath ./paragon/css/themes --defaultThemeVariants light --excludeCore",
Expand Down
Loading
Loading