diff --git a/openedx/features/course_bookmarks/plugins.py b/openedx/features/course_bookmarks/plugins.py new file mode 100644 index 0000000000000000000000000000000000000000..3f24942ae8bf76bfe3599c51a3b9d0d005946b7f --- /dev/null +++ b/openedx/features/course_bookmarks/plugins.py @@ -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]) diff --git a/openedx/features/course_experience/course_tools.py b/openedx/features/course_experience/course_tools.py new file mode 100644 index 0000000000000000000000000000000000000000..bf36e14fe68b7f311031db657a93c3672f1c10d5 --- /dev/null +++ b/openedx/features/course_experience/course_tools.py @@ -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 diff --git a/openedx/features/course_experience/plugins.py b/openedx/features/course_experience/plugins.py new file mode 100644 index 0000000000000000000000000000000000000000..611c3483821d2d6c5e59671cb3d1e6dae592c5e0 --- /dev/null +++ b/openedx/features/course_experience/plugins.py @@ -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]) diff --git a/openedx/features/course_experience/templates/course_experience/course-home-fragment.html b/openedx/features/course_experience/templates/course_experience/course-home-fragment.html index 9b0a5133074c5d64cda346812a06183e3821547b..21abafd7fe1e374fd7dc81ea5911051b0c2d53c8 100644 --- a/openedx/features/course_experience/templates/course_experience/course-home-fragment.html +++ b/openedx/features/course_experience/templates/course_experience/course-home-fragment.html @@ -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> diff --git a/setup.py b/setup.py index 5a16348e426061487cefea3c98f34101e254cef9..6d9846c0b0607fada796081a0e75e63d3ae0f908 100644 --- a/setup.py +++ b/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",