{"id":32374799,"url":"https://github.com/cookiecutter-openedx/openedx-plugin-example","last_synced_at":"2026-05-17T04:44:28.433Z","repository":{"id":142122404,"uuid":"468472988","full_name":"cookiecutter-openedx/openedx-plugin-example","owner":"cookiecutter-openedx","description":"Python-Django scaffolding for common use cases","archived":false,"fork":false,"pushed_at":"2024-05-12T14:24:57.000Z","size":2487,"stargazers_count":19,"open_issues_count":2,"forks_count":10,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-11T14:05:15.672Z","etag":null,"topics":["django","full-stack","openedx","python","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cookiecutter-openedx.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"lpm0073","patreon":"lpm0073"}},"created_at":"2022-03-10T18:54:11.000Z","updated_at":"2025-08-30T09:38:58.000Z","dependencies_parsed_at":"2024-04-07T17:30:57.206Z","dependency_job_id":"49a64a40-544f-4722-a74c-cfe851cd9238","html_url":"https://github.com/cookiecutter-openedx/openedx-plugin-example","commit_stats":null,"previous_names":["lpm0073/openedx-plugin-example"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/cookiecutter-openedx/openedx-plugin-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cookiecutter-openedx%2Fopenedx-plugin-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cookiecutter-openedx%2Fopenedx-plugin-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cookiecutter-openedx%2Fopenedx-plugin-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cookiecutter-openedx%2Fopenedx-plugin-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cookiecutter-openedx","download_url":"https://codeload.github.com/cookiecutter-openedx/openedx-plugin-example/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cookiecutter-openedx%2Fopenedx-plugin-example/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280878315,"owners_count":26406643,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-24T02:00:06.418Z","response_time":73,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django","full-stack","openedx","python","python3"],"created_at":"2025-10-24T22:43:00.740Z","updated_at":"2025-10-24T22:43:02.012Z","avatar_url":"https://github.com/cookiecutter-openedx.png","language":"Python","funding_links":["https://github.com/sponsors/lpm0073","https://patreon.com/lpm0073"],"categories":[],"sub_categories":[],"readme":"# Open edX Plugin Examples\n\n[![Source code](https://img.shields.io/static/v1?logo=github\u0026label=Git\u0026style=flat-square\u0026color=brightgreen\u0026message=Source%20code)](https://github.com/cookiecutter-openedx/openedx-plugin-example)\n[![Forums](https://img.shields.io/static/v1?logo=discourse\u0026label=Forums\u0026style=flat-square\u0026color=000000\u0026message=discuss.openedx.org)](https://discuss.openedx.org/tag/cookiecutter)\n[![Documentation](https://img.shields.io/static/v1?\u0026label=Documentation\u0026style=flat-square\u0026color=000000\u0026message=Documentation)](https://github.com/cookiecutter-openedx/openedx-plugin-example)\n[![PyPI releases](https://img.shields.io/pypi/v/openedx-plugin-example?logo=python\u0026logoColor=white)](https://pypi.org/project/openedx-plugin-example)\n[![AGPL License](https://img.shields.io/github/license/overhangio/tutor.svg?style=flat-square)](https://www.gnu.org/licenses/agpl-3.0.en.html)\n[![hack.d Lawrence McDaniel](https://img.shields.io/badge/hack.d-Lawrence%20McDaniel-orange.svg)](https://lawrencemcdaniel.com)\n\nAn Open edX plugin showcasing a curated collection of code samples for extending and modifying the stock functionality of an Open edX installation using the built-in plugin architecture. This plugin can be installed in any Open edX installation version Juniper or later, independent of the installation method. This plugin currently runs in native builds, tutor installations, and Cookiecutter-openedx Kubernetes, amongst other proprietary installation methodologies.\n\nOpen edX official documentation recommends that you leverage its plugin architecture rather than fork its code repositories. This project provides more than two dozen code examples to help you accomplish this.\n\n[![Watch the video](https://i3.ytimg.com/vi/4TcSrEzciHA/maxresdefault.jpg)](https://www.youtube.com/watch?v=4TcSrEzciHA)\n\nUsage:\n\n```bash\npip install openedx-plugin-example\n```\n\nTechnical features and coding techniques that are showcased in this repo include:\n\n* Iterate and introspect structured course content\n* Custom oauth backend that seamlessly integrates with Open edX third party authentication\n* Custom url to auto-enroll students in a course\n* Automatically detect and adapt to the browser OS locale setting\n* Usage examples of Open edX's extensive library of Python decorators\n* Open edX Django configuration settings\n* Open edX Django urls\n* Open edX Django logging\n* Open edX Django signals\n* Open edX Django RestFramework custom api\n* Django app setup\n* Django models\n* Django and Mako templating\n* Django static assets\n* Django Admin\n* Django middleware\n* Django manage.py custom commands\n* Django Waffle flags\n* Python environment variables\n* How to bundle multiple plugins in a single pip package\n* How to redirect Open edX urls in lms and cms to endpoints created in this plugin\n* How to automatically initialize Django model data during app startup\n* Adding unit tests to plugin code\n* Semantic version control\n* Pre-commit with linting by flake8 and black, both of which are configured to match the opinionated styling that you'll find in Open edX repositories\n* pip configuration, requirements, constraints, setup.py, pyproject.toml\n* Publishing to PyPi\n\n## Open edX Plugins in this Repository\n\n### openedx_plugin\n\nDemonstrates how to create an all-in-one Open edX plugin, with a heterogeneous collection of custom feature additions, including:\n\n* A custom third party authentication [Oauth2 client backend](./openedx_plugin/wordpress_oauth2_backend.py).\n* Extending [new user registration](./openedx_plugin/signals.py) functionality. Demonstrates how to leverage Django Signals to extend basic native Open edX operations.\n* Extending the openedx feature set with [Django Signals](./openedx_plugin/signals.py)\n* Implementing a [rest api](./openedx_plugin/api/README.md) from scratch that is accessible from an LMS url.\n* Advanced Internationalization: [customizing static page links](./openedx_plugin/locale/README.md) based on the language locale setting\n\n### openedx_plugin_api\n\nImplements a full-featured [REST API](./openedx_plugin_api/README.md) which is built from snippets of Open edX's built-in rest api libraries. Demonstrates the following:\n\n* Best practices with Django RestFramework for Open edX\n* Adding custom URL endpoints to LMS\n* Adding Django Admin views\n* Creating custom Django models for your plugin\n* How to create custom entries in the openedx app log\n* How to leverage Django Signals to create customized event-driven features\n* How to implement Waffle Switches to control optional features at run-time\n\n### openedx_plugin_cms\n\nImplements a [custom course audit report](openedx_plugin_cms/README.md) in Course Management Studio that depends on a backend Python process to iterate the course content. This process is highly instructive about the Open edX course object hierarchy. This plugin demonstrates the following:\n\n* Adding custom URL endpoints to Course Management Studio\n* Adding Django Admin views\n* Adding custom manage.py commands to CMS\n* Creating custom Django models for your plugin\n* How to create custom entries in the openedx app log\n* How to leverage Django Signals to create customized event-driven features\n* How to implement Waffle Switches to control optional features at run-time\n* Advanced usage of Mako templating within an Open edX plugin\n* How to programatically iterate and inspect course content\n* How to leverage Open edX object caching\n\n### openedx_plugin_mobile_api\n\nImplements a modified version of the Open edX LMS Mobile REST API. This plugin illustrates best practices for modifying edx-platform source code without actually forking this repository. Demonstrates the following:\n\n* How to implement Django middleware to modify the destination of existing LMS url endpoints\n* Best practices with Django RestFramework for Open edX\n\n## Getting Started\n\n### Install using Tutor\n\nSee [Installing extra xblocks and requirements](https://docs.tutor.edly.io/configuration.html#installing-extra-xblocks-and-requirements)\n\n```bash\ntutor config save       # to ensure that tutor's root folder system has been created\necho \"openedx-plugin-example\" \u003e\u003e \"$(tutor config printroot)/env/build/openedx/requirements/private.txt\"\ntutor images build openedx\ntutor local launch\n\n# you'll also need to run this on your very first install\n# -----------------------------------------------------------------------------\n\n# 1. run migrations\ntutor local run lms ./manage.py lms makemigrations\ntutor local run lms ./manage.py lms migrate\ntutor local run cms ./manage.py cms makemigrations\ntutor local run cms ./manage.py cms migrate\n\n# 2. add configuration data to custom models\ntutor local run lms ./manage.py lms openedx_plugin_init\ntutor local run lms ./manage.py lms openedx_plugin_api_init\ntutor local run lms ./manage.py lms openedx_plugin_mobile_api_init\ntutor local run cms ./manage.py cms openedx_plugin_cms_init\n```\n\n### Notes About Django-Waffle\n\n* Each of these four Open edX plugins use [django-waffle](https://waffle.readthedocs.io/en/stable/) to toggle features on and off. While edx-platform also uses waffle switches, you should note that they separately manage a wrapper project named [edx-toggles](https://edx.readthedocs.io/projects/edx-toggles/en/latest/readme.html), and therefore the source code in this repo interacts with both of these.\n\n* Waffle switches in each of these four plugins are automatically initialized. You'll therefore find the switches in the LMS Django Admin console (admin/waffle/switch/) of your Open edX installation. Additionally, you'll find the raw MySL database records in the waffle_switch table ![MySQL records](https://raw.githubusercontent.com/cookiecutter-openedx/openedx-plugin-example/main/doc/openedx_plugin_waffle_mysql.png)\n\n* Look for app startup entries in the LMS app log for diagnostics information about the state of each waffle switch ![app logs](https://raw.githubusercontent.com/cookiecutter-openedx/openedx-plugin-example/main/doc/openedx_plugin_waffle_app_log.png)\n\n### Documentation\n\nDocumentation is available here: [Documentation](https://github.com/cookiecutter-openedx/openedx-plugin-example)\n\n### Support\n\nTo get community support, go to the official Open edX discussion forum: [https://discuss.openedx.org](https://discuss.openedx.org).\n\n### Contributing\n\nWe welcome contributions! openedx-plugin-example is part of the [cookiecutter-openedx](https://github.com/cookiecutter-openedx) project. Pull requests are welcome in all repos belonging to this organization. You can also contact [Lawrence McDaniel](https://lawrencemcdaniel.com/contact) directly.\n\n### Getting Started With Local development\n\n* Use the same virtual environment that you use for edx-platform\n* Ensure that your Python interpreter to 3.8x\n* install black: \u003chttps://pypi.org/project/black/\u003e\n* install flake8: \u003chttps://flake8.pycqa.org/en/latest/\u003e\n* install flake8-coding: \u003chttps://pypi.org/project/flake8-coding/\u003e\n\n```bash\n# Run these from within your edx-platform virtual environment\npython3 -m venv venv\nsource venv/bin/activate\n\ncd /path/to/edx-platform\npip install -r requirements/edx/base.txt\npip install -r requirements/edx/coverage.txt\npip install -r requirements/edx/development.txt\npip install -r requirements/edx/pip-tools.txt\npip install -r requirements/edx/testing.txt\npip install -r requirements/edx/doc.txt\npip install -r requirements/edx/paver.txt\n\npip install pre-commit black flake8\npre-commit install\n```\n\n#### Local development good practices\n\n* run `black` on modified code before committing.\n* run `flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics`\n* run `flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics`\n* run `pre-commit run --all-files` before pushing. see: \u003chttps://pre-commit.com/\u003e\n\n#### edx-platform dependencies\n\nTo avoid freaky version conflicts in prod it's a good idea to install all of the edx-platform requirements to your local dev virtual environment.\n\n* requirements/edx/base.txt\n* requirements/edx/develop.txt,\n* requirements/edx/testing.txt\n\nAt a minimum this will give you the full benefit of your IDE's linter.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcookiecutter-openedx%2Fopenedx-plugin-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcookiecutter-openedx%2Fopenedx-plugin-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcookiecutter-openedx%2Fopenedx-plugin-example/lists"}