Adding a new course tab
This document describes how to extend the edX platform with a new tab that can be added to courses.
Background
An edX course has a notion of a set of tabs that present the student with information about an aspect of their experience:
In Studio, a course author can manage the list of tabs (called Pages in the UX):
Introducing a new course tab
The edX platform provides a Python entry point to allow new course tabs to be registered as plugins. At a high level, we recommend that you do the following:
- Implement your new feature as its own Django app following these guidelines: How to add a new feature to LMS or Studio
- For now, this Django app must live inside the edx-platform repo as your plugin must subclass
CourseTab
or one of its subclasses - We hope to remove this restriction soon by moving this subclass into a separate
edx-core
repo - Our current recommendation is to place your new Django app in the folder
openedx/features
.
- For now, this Django app must live inside the edx-platform repo as your plugin must subclass
- Render the tab as a Web Fragment, as this has a number of benefits:
- Allows your tab to be rendered standalone in the mobile apps
- Allows alternate LMS implementations to dynamically render your fragment as part of a single page application
- Removes the need for your template to render the full page using Mako in order to include the platform's main.html template
- Potentially allows your feature to live outside of the edx-platform repo (although this is not currently possible due to the dependency upon the
CourseTab
base class)
The following is a more detailed list of the steps required to add a new course tab.
1. Add an entry point like this to your Python library's setup.py
Note that new_tab
is the id of your tab, and example.NewTab
is the fully qualified name of your new tab class.
entry_points={ "openedx.course_tab": [ "new_tab = example.NewTab", ] }
2. Define your new tab class
Define your new tab class as a subclass of EnrolledTab,
if the tab should only be shown to enrolled users, or CourseTab
if unenrolled users should also be able to see it.
from courseware.tabs import CourseTab class NewTab(CourseTab): """A new course tab.""" name = "new_tab" title = ugettext_noop("New Tab") # We don't have the user in this context, so we don't want to translate it at this level. view_name = "new_tab_view" @classmethod def is_enabled(cls, course, user=None): """Returns true if this tab is enabled.""" return settings.FEATURES.get('NEW_TAB_ENABLED', False)
The key properties of a course tab are as follows:
In addition, a tab can override any of the following class methods:
Method | Signature | Description |
---|---|---|
is_enabled | def is_enabled(cls, course, user=None) | Returns true if this tab is enabled for the current course for the specified user. If user is None, this method is being called by Studio and should generally return True if the tab will be shown to at least some students. |
validate | def validate(cls, tab_dict, raise_error=True) | Validates a dictionary representing a course tab. If raise_error then issues are raised as exceptions, else the function should return True if the dictionary is valid. |
The course stores a static list of its tabs in the database, and this list is only updated when one of the following actions take place:
- You create a new course.
- You update the advanced settings for your course.
This means that if you have a pre-existing course then it won't immediately show a tab even after you've registered the new entry point.
3. Implement the view
You have two choices when implementing your new tab view:
- Implement a view which renders only the contents of your new tab
- This view must subclass
DjangoView
and use the mixinDjangoFragmentViewMixin.
- Implement the
render_to_fragment
method which should render the content. - See the Discussions tab example below to see how this works.
- This view must subclass
- Implement a full page view
You must render a mako template with the correct boilerplate to show the tabs correctly. An example would be as follows:
## mako <%! from django.utils.translation import ugettext as _ %> <%namespace name='static' file='/static_content.html'/> <%inherit file="/main.html" /> <%block name="bodyclass">view-PAGE-SLUG is-in-course course</%block> <%block name="pagetitle">${_("PAGE NAME")}</%block> <%block name="headextra"> <%static:css group='style-course'/> </%block> <%include file="/courseware/course_navigation.html" args="active_page='PAGE-SLUG'" /> TEMPLATE CODE
Be sure to fill in PAGE-SLUG and PAGE NAME with the correct slug and name for your new page. Replace
TEMPLATE_CODE
with the Mako markup and code for the rest of the page.- See the Teams tab example below to see how this works.
Examples
Fragment Example: Discussions tab
The Discussions feature renders its tab as a web fragment. Here's how it works:
- the "Discussion" tab is defined here:
- it refers to the fragment view class
DiscussionBoardFragmentView
: - the fragment's URL is declared here:
- the "Discussion" tab is then registered here:
Full Page Example: Teams tab
This example is to explain how legacy tabs were created. For new tabs, please follow the fragment example.
A simple example of a full Django View is the "Teams" tab. Here's how it gets added to the platform:
- there is a Teams Django app that provides the entire feature
- the "Teams" tab is defined in the file
plugins.py
: - the tab is registered in the main
setup.py
for the LMS- https://github.com/edx/edx-platform/blob/master/setup.py#L36
- Note: this has to live in the LMS's
setup.py
because the Teams app is not Pip installed
Troubleshooting
If your new tab isn't showing up, try the following:
- You can put a breakpoint in this method to see whether or not your tab type is being added:
- The method which returns all possible tab types is here:
- https://github.com/edx/edx-platform/blob/master/openedx/core/lib/course_tabs.py#L20
- You can print the tab_types to see if tab isn't returned in the full list of types.
If your new tab type isn't returned then that means that the entry point registration didn't work.
- Try the following:
- check that you registered your new tab in setup.py.
- If you added your new Django app into edx-platform, then add it here:
- make sure that you've increased the version number so that pip knows that it needs to reinstall
- try manually reinstalling the app. For an edx-platform extension, do the following in devstack:
sudo su edxapp pip install -e /edx/app/edxapp/edx-platform
See Also
- We use Stevedore to manage the entry points: http://docs.openstack.org/developer/stevedore/