Merge pull request #15372 from edx/andya/pluggable-course-tools
Introduce a course_tool plugin entry point and convert Updates, Bookmarks and Reviews into entry points.
This commit is contained in:
40
openedx/features/course_bookmarks/plugins.py
Normal file
40
openedx/features/course_bookmarks/plugins.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
Platform plugins to support course bookmarks.
|
||||
"""
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from openedx.features.course_experience.course_tools import CourseTool
|
||||
|
||||
|
||||
class CourseBookmarksTool(CourseTool):
|
||||
"""
|
||||
The course bookmarks tool.
|
||||
"""
|
||||
@classmethod
|
||||
def is_enabled(cls, course_key):
|
||||
"""
|
||||
Always show the bookmarks tool.
|
||||
"""
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def title(cls):
|
||||
"""
|
||||
Returns the title of this tool.
|
||||
"""
|
||||
return _('Bookmarks')
|
||||
|
||||
@classmethod
|
||||
def icon_classes(cls):
|
||||
"""
|
||||
Returns the icon classes needed to represent this tool.
|
||||
"""
|
||||
return 'fa fa-bookmark'
|
||||
|
||||
@classmethod
|
||||
def url(cls, course_key):
|
||||
"""
|
||||
Returns the URL for this tool for the specified course key.
|
||||
"""
|
||||
return reverse('openedx.course_bookmarks.home', args=[course_key])
|
||||
67
openedx/features/course_experience/course_tools.py
Normal file
67
openedx/features/course_experience/course_tools.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
Support for course tool plugins.
|
||||
"""
|
||||
from openedx.core.lib.api.plugins import PluginManager
|
||||
|
||||
# Stevedore extension point namespace
|
||||
COURSE_TOOLS_NAMESPACE = 'openedx.course_tool'
|
||||
|
||||
|
||||
class CourseTool(object):
|
||||
"""
|
||||
This is an optional base class for Course Tool plugins.
|
||||
|
||||
Plugin implementations inside this repo should subclass CourseTool to get
|
||||
useful default behavior, and to add clarity to the code. This base class is
|
||||
not a requirement, and plugin implementations outside of this repo should
|
||||
simply follow the contract defined below.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def is_enabled(cls, course_key):
|
||||
"""
|
||||
Returns true if this tool is enabled for the specified course key.
|
||||
"""
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def title(cls, course_key):
|
||||
"""
|
||||
Returns the title for the course tool.
|
||||
"""
|
||||
raise NotImplementedError("Must specify a title for a course tool.")
|
||||
|
||||
@classmethod
|
||||
def icon_classes(cls, course_key):
|
||||
"""
|
||||
Returns the icon classes needed to represent this tool.
|
||||
|
||||
For example, return an icon from font-awasome.css, like 'fa fa-star'.
|
||||
"""
|
||||
raise NotImplementedError("Must specify an icon for a course tool.")
|
||||
|
||||
@classmethod
|
||||
def url(cls, course_key):
|
||||
"""
|
||||
Returns the URL for this tool for the specified course key.
|
||||
"""
|
||||
raise NotImplementedError("Must specify a url for a course tool.")
|
||||
|
||||
|
||||
class CourseToolsPluginManager(PluginManager):
|
||||
"""
|
||||
Manager for all of the course tools that have been made available.
|
||||
|
||||
Course tool implementation can subclass `CourseTool` or can implement
|
||||
the required class methods themselves.
|
||||
"""
|
||||
NAMESPACE = COURSE_TOOLS_NAMESPACE
|
||||
|
||||
@classmethod
|
||||
def get_course_tools(cls):
|
||||
"""
|
||||
Returns the list of available course tools in their canonical order.
|
||||
"""
|
||||
course_tools = cls.get_available_plugins().values()
|
||||
course_tools.sort(key=lambda course_tool: course_tool.title())
|
||||
return course_tools
|
||||
79
openedx/features/course_experience/plugins.py
Normal file
79
openedx/features/course_experience/plugins.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
Platform plugins to support the course experience.
|
||||
|
||||
This includes any locally defined CourseTools.
|
||||
"""
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from . import UNIFIED_COURSE_TAB_FLAG, SHOW_REVIEWS_TOOL_FLAG
|
||||
from views.course_reviews import CourseReviewsModuleFragmentView
|
||||
from course_tools import CourseTool
|
||||
|
||||
|
||||
class CourseUpdatesTool(CourseTool):
|
||||
"""
|
||||
The course updates tool.
|
||||
"""
|
||||
@classmethod
|
||||
def title(cls):
|
||||
"""
|
||||
Returns the title of this tool.
|
||||
"""
|
||||
return _('Updates')
|
||||
|
||||
@classmethod
|
||||
def icon_classes(cls):
|
||||
"""
|
||||
Returns icon classes needed to represent this tool.
|
||||
"""
|
||||
return 'fa fa-newspaper-o'
|
||||
|
||||
@classmethod
|
||||
def is_enabled(cls, course_key):
|
||||
"""
|
||||
Returns True if this tool is enabled for the specified course key.
|
||||
"""
|
||||
return UNIFIED_COURSE_TAB_FLAG.is_enabled(course_key)
|
||||
|
||||
@classmethod
|
||||
def url(cls, course_key):
|
||||
"""
|
||||
Returns the URL for this tool for the specified course key.
|
||||
"""
|
||||
return reverse('openedx.course_experience.course_updates', args=[course_key])
|
||||
|
||||
|
||||
class CourseReviewsTool(CourseTool):
|
||||
"""
|
||||
The course reviews tool.
|
||||
"""
|
||||
@classmethod
|
||||
def title(cls):
|
||||
"""
|
||||
Returns the title of this tool.
|
||||
"""
|
||||
return _('Reviews')
|
||||
|
||||
@classmethod
|
||||
def icon_classes(cls):
|
||||
"""
|
||||
Returns icon classes needed to represent this tool.
|
||||
"""
|
||||
return 'fa fa-star'
|
||||
|
||||
@classmethod
|
||||
def is_enabled(cls, course_key):
|
||||
"""
|
||||
Returns True if this tool is enabled for the specified course key.
|
||||
"""
|
||||
reviews_configured = CourseReviewsModuleFragmentView.is_configured()
|
||||
return SHOW_REVIEWS_TOOL_FLAG.is_enabled(course_key) and reviews_configured
|
||||
|
||||
@classmethod
|
||||
def url(cls, course_key):
|
||||
"""
|
||||
Returns the URL for this tool for the specified course key.
|
||||
"""
|
||||
return reverse('openedx.course_experience.course_reviews', args=[course_key])
|
||||
@@ -15,6 +15,7 @@ from django_comment_client.permissions import has_permission
|
||||
from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_string
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG, SHOW_REVIEWS_TOOL_FLAG
|
||||
from openedx.features.course_experience.course_tools import CourseToolsPluginManager
|
||||
%>
|
||||
|
||||
<%block name="content">
|
||||
@@ -66,33 +67,26 @@ from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG, SHOW_REV
|
||||
${HTML(outline_fragment.body_html())}
|
||||
</main>
|
||||
<aside class="course-sidebar layout-col layout-col-a">
|
||||
<div class="section section-tools">
|
||||
<h3 class="hd-6">${_("Course Tools")}</h3>
|
||||
<ul class="list-unstyled">
|
||||
<li>
|
||||
<a href="${reverse('openedx.course_bookmarks.home', args=[course_key])}">
|
||||
<span class="icon fa fa-bookmark" aria-hidden="true"></span>
|
||||
${_("Bookmarks")}
|
||||
</a>
|
||||
</li>
|
||||
% if SHOW_REVIEWS_TOOL_FLAG.is_enabled(course.id) and show_reviews_link:
|
||||
<li>
|
||||
<a href="${reverse('openedx.course_experience.course_reviews', args=[course.id])}">
|
||||
<span class="icon fa fa-star" aria-hidden="true"></span>
|
||||
${_("Reviews")}
|
||||
</a>
|
||||
</li>
|
||||
% endif
|
||||
% if UNIFIED_COURSE_TAB_FLAG.is_enabled(course.id):
|
||||
<li>
|
||||
<a href="${reverse('openedx.course_experience.course_updates', args=[course.id])}">
|
||||
<span class="icon fa fa-newspaper-o" aria-hidden="true"></span>
|
||||
${_("Updates")}
|
||||
</a>
|
||||
</li>
|
||||
% endif
|
||||
</ul>
|
||||
</div>
|
||||
<%
|
||||
course_tools = CourseToolsPluginManager.get_course_tools()
|
||||
%>
|
||||
% if course_tools:
|
||||
<div class="section section-tools">
|
||||
<h3 class="hd-6">${_("Course Tools")}</h3>
|
||||
<ul class="list-unstyled">
|
||||
% for course_tool in course_tools:
|
||||
% if course_tool.is_enabled(course_key):
|
||||
<li>
|
||||
<a href="${course_tool.url(course_key)}">
|
||||
<span class="icon ${course_tool.icon_classes()}" aria-hidden="true"></span>
|
||||
${course_tool.title()}
|
||||
</a>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
% endif
|
||||
<div class="section section-dates">
|
||||
${HTML(dates_fragment.body_html())}
|
||||
</div>
|
||||
|
||||
13
setup.py
13
setup.py
@@ -6,17 +6,15 @@ from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="Open edX",
|
||||
version="0.6",
|
||||
version="0.7",
|
||||
install_requires=["setuptools"],
|
||||
requires=[],
|
||||
# NOTE: These are not the names we should be installing. This tree should
|
||||
# be reorganized to be a more conventional Python tree.
|
||||
packages=[
|
||||
"openedx.core.djangoapps.course_groups",
|
||||
"openedx.core.djangoapps.credit",
|
||||
"openedx.core.djangoapps.user_api",
|
||||
"lms",
|
||||
"cms",
|
||||
"lms",
|
||||
"openedx",
|
||||
],
|
||||
entry_points={
|
||||
"openedx.course_tab": [
|
||||
@@ -38,6 +36,11 @@ setup(
|
||||
"textbooks = lms.djangoapps.courseware.tabs:TextbookTabs",
|
||||
"wiki = lms.djangoapps.course_wiki.tab:WikiTab",
|
||||
],
|
||||
"openedx.course_tool": [
|
||||
"course_bookmarks = openedx.features.course_bookmarks.plugins:CourseBookmarksTool",
|
||||
"course_updates = openedx.features.course_experience.plugins:CourseUpdatesTool",
|
||||
"course_reviews = openedx.features.course_experience.plugins:CourseReviewsTool",
|
||||
],
|
||||
"openedx.user_partition_scheme": [
|
||||
"random = openedx.core.djangoapps.user_api.partition_schemes:RandomUserPartitionScheme",
|
||||
"cohort = openedx.core.djangoapps.course_groups.partition_scheme:CohortPartitionScheme",
|
||||
|
||||
Reference in New Issue
Block a user