How to add a new feature to LMS or Studio
New Guidance
This wiki document may be out of date - please check Options for Extending the Open edX Platform for newer information.
Introduction
This document describes the different options available to you to add a new feature to the edX platform.
Note: If you have not contributed to edX before, be sure to read this guide: Process for Contributing Code.
Decide where to host your code
The first thing you should decide is where you will host your new feature. There are five options to consider which are listed here in order of preference:
- Create a new XBlock in its own Git repository
- XBlocks allows you to define new types of components that can be used in courseware, e.g. new problem types
- For edX to consume your XBlock, it should just be added as a requirement to: https://github.com/edx/edx-platform/blob/master/requirements/edx/github.in
- Consider the following before choosing to create an XBlock:
- Examples of XBlocks:
- Recommender XBlock: https://github.com/pmitros/RecommenderXBlock
- Staff Graded Assignment XBlock: https://github.com/mitodl/edx-sga
- XBlock Poll: https://github.com/open-craft/xblock-poll
- See the documentation below: Create a new XBlock in its own Git repository
- Create a new IDA (independently deployable application)
- If a feature doesn't need to be tightly integrated into Studio or the LMS then it can be built as an independent app
- It can make use of the Open edX ReST APIs to get any platform data that it requires
- IDAs are a good choice when:
- They need to scale independently of the edX platform
- They will be built, deployed and managed by a separate team
- LMS and Studio should continue unaffected even if the application goes down
- Consider the following before choosing to make a new IDA:
- There is more operational complexity for the community to deploy and manage your feature
- There are performance implications with introducing extra network calls
- There is more application complexity when working with related data from external APIs, e.g. joins and referential integrity must be implemented in the application layer
- Examples of IDAs:
- edX Analytics Dashboard: https://github.com/edx/edx-analytics-dashboard
- edX eCommerce Service: https://github.com/edx/ecommerce
- edX Student Notes API: https://github.com/edx/edx-notes-api
- See the documentation below: Create a new IDA
- Create a new Django app in its own Git repository
- New Django apps can be implemented in a separate repo and pip installed into the platform
- A Django app in its own repository is a good choice when:
- it provides new services that will be consumed by other parts of the platform
- if it requires new UI for LMS or Studio, it provides it via extensions and does not introduce new URLs
- See OEP-12 for the recommended way to add pluggable user interfaces
- Consider the following:
- If the feature has no UI then it may be better suited as an IDA if it doesn't require direct access to platform data models
- See /wiki/spaces/TNL/pages/40862230 for descriptions of some of the challenges involved with this approach
- Examples of Django apps in their own repositories:
- ORA 2: https://github.com/edx/edx-ora2
- Submissions: https://github.com/edx/edx-submissions
- Milestones: https://github.com/edx/edx-milestones
- See the documentation below: Create a new Django app in its own Git repository
- Add a new Django app to the edX platform
- If your feature cannot be built using the existing extension points, then it can be added directly to the edx-platform
- Consider the following:
- It would be better to introduce a platform extension point and then use it from a separate app
- It is best to build your new Django app in its own directory so that there is an option to move it into its own repo later
- Our current thinking is to add your new Django app to the directory
openedx/features
- Our current thinking is to add your new Django app to the directory
- See /wiki/spaces/ArchiveEng/pages/157888616 for the recommended way to add pluggable user interfaces
- Examples of Django apps in the edX platform:
- See the documentation below: Adding a new Django app to edx-platform
- Update the platform directly
- This is the option of last resort
- When should you update the platform directly?
- When the feature you are implementing updates core functionality in fundamental ways
- When there are no extension points that you can use to build the functionality you need
- Consider the following before doing this:
- Features which update the platform directly require the most scrutiny in code review
- There is far more risk of unexpected fallout from your changes
- Consider whether it is possible to introduce a platform extension point and then use it
- Be sure to discuss your proposed approach with the Open edX community first to ensure that this is the best way to move forward
- Examples:
- See the documentation below: Update the platform directly
For more background when making this decision, see these architecture council meeting notes: 2015.09.23 - IDA discussion.
How to add a new XBlock in its own Git repository
- Create your new project using the cookiecutter: https://github.com/edx/cookiecutter-xblock
- See the XBlock documentation for more details: https://xblock.readthedocs.org/en/latest/index.html
How to create a new IDA
- Be sure to propose your new IDA to the Architecture and Engineering: Home
- See the architecture council meeting notes on IDAs: 2015.09.23 - IDA discussion
- Create your new project using the cookiecutter: https://github.com/edx/cookiecutter-django-ida
- Consider how the IDA will integrate with the edX platform
- Ideally these integrations will be implemented as extension points using a Django app in a separate repo
- Determine how to test all of the integration points
How to add a new Django app in its own Git repository
- Create your new project using the cookiecutter: https://github.com/edx/cookiecutter-django-app
- See /wiki/spaces/TNL/pages/40862230 for descriptions of some of the issues involved with this approach
How to add a new Django app to the edX platform
These are the steps necessary for adding a new Django app-based feature, based on what was done to add the Teams feature to LMS.
- Create a new Django app, where all Python, mako and underscore templates, and JavaScript will live (note that there is an open story to allow sass files to also live in this app)
Our current best practice it so put new features into the platform directory
openedx/features
Run the following code in an lms-shell (run "make lms-shell" in devstack)
root@lms:/edx/app/edxapp/edx-platform# mkdir ./openedx/features/[Your App Name] root@lms:/edx/app/edxapp/edx-platform# ./manage.py lms startapp [Your App Name] ./openedx/features/[Your App Name]
- Create your Python view code, urls.py file, and unit tests
- Ideally you should render your views as /wiki/spaces/ArchiveEng/pages/157888616
- Your templates should live inside your app's
templates
directory, namespaced by the name of your app (for instance,teams/templates/teams
) - If you are rendering full-page views you must use Mako:
- Make sure your Mako files have "## mako" on the first line, so that they are loaded by our Mako template loader.
- For example: https://github.com/edx/edx-platform/blob/master/lms/djangoapps/teams/templates/teams/teams.html
- If you have JavaScript and underscore templates:
- Put your JavaScript files inside of a static directory, namespaced by the name of your app (for instance,
teams/static/teams/js
). - As a best practice, use RequireJS and Backbone for your JavaScript
- Use Underscore to handle client-side templates and load them using RequireJS Text (example).
- Put your underscore templates inside the same namespaced static directory (for instance,
teams/static/teams/templates
). - Put your Jasmine unit tests inside a spec directory within the JS directory (for instance,
teams/static/teams/js/spec
). - To enable the Jasmine unit tests as part of running LMS unit tests:
- For more details, see /wiki/spaces/ArchiveEng/pages/16646275
- Put your JavaScript files inside of a static directory, namespaced by the name of your app (for instance,
- Add the name of your app to OPTIONAL_APPS in
lms/envs/common.py
- Apps listed in OPTIONAL_APPS will be dynamically added to Django's INSTALLED_APPS if the app is present
- This is beneficial as some forks of Open edX might choose not to use your new feature
- If your feature will always be present in the platform, you can instead update INSTALLED_APPS directly
- For example: https://github.com/edx/edx-platform/blob/master/lms/envs/common.py#L2862
- This ensures that the Django pipeline will pick up all of the static assets found beneath your
static
directory - You can reference your static files as follows:
- In Mako, use
static.url
which resolves a path to the correct location in the Django static directory - In client-side code, you can use relative paths in RequireJS which will resolve to the correct location
- All other client-side URLs should be resolved on the server and then passed down to the client
- Don't assume that you can access a file directly with
/static/myApp/foo.jpg
because that bypasses the Django pipeline
- Don't assume that you can access a file directly with
- In Mako, use
- Apps listed in OPTIONAL_APPS will be dynamically added to Django's INSTALLED_APPS if the app is present
- Add your urls to
lms/urls.py
, most likely behind a feature flag or configuration setting- Be sure to have a urls.py within your Django app, and then include it from the platform's urls.py
- Write bok choy acceptance tests in
common/test/acceptance
to verify that your plugin is working.
How to update the platform directly
- Determine that there is no better way to implement your feature, as core platform changes are hard to make and have approved
- Follow the contribution process to make a pull request against the platform: Process for Contributing Code
Implementing your new feature
Once you have chosen how to provide your new feature, you can add many different types of functionality: