diff --git a/common/djangoapps/student/views/dashboard.py b/common/djangoapps/student/views/dashboard.py index 38d4ef1fdad06d22eba37e26b0291056efc1dcda..96a57c693484aec361d400773a4a4d338a4b8109 100644 --- a/common/djangoapps/student/views/dashboard.py +++ b/common/djangoapps/student/views/dashboard.py @@ -47,6 +47,8 @@ from openedx.core.djangoapps.catalog.utils import ( get_visible_sessions_for_entitlement ) from openedx.core.djangoapps.credit.email_utils import get_credit_provider_attribute_values, make_providers_strings +from openedx.core.djangoapps.plugins.plugin_contexts import get_plugins_view_context +from openedx.core.djangoapps.plugins import constants as plugin_constants from openedx.core.djangoapps.programs.models import ProgramsApiConfig from openedx.core.djangoapps.programs.utils import ProgramDataExtender, ProgramProgressMeter from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers @@ -880,6 +882,13 @@ def student_dashboard(request): # TODO START: clean up as part of REVEM-199 (END) } + context_from_plugins = get_plugins_view_context( + plugin_constants.ProjectType.LMS, + plugin_constants.PluginContexts.VIEWS.STUDENT_DASHBOARD, + context + ) + context.update(context_from_plugins) + if ecommerce_service.is_enabled(request.user): context.update({ 'use_ecommerce_payment_flow': True, diff --git a/openedx/core/djangoapps/plugins/README.rst b/openedx/core/djangoapps/plugins/README.rst index 2013200231ed5b2055e8ff622738b194c4a26a2b..dfab6e0d267824f2f7294e947efdb34318c5140e 100644 --- a/openedx/core/djangoapps/plugins/README.rst +++ b/openedx/core/djangoapps/plugins/README.rst @@ -106,7 +106,7 @@ class:: from django.apps import AppConfig from openedx.core.djangoapps.plugins.constants import ( - ProjectType, SettingsType, PluginURLs, PluginSettings + ProjectType, SettingsType, PluginURLs, PluginSettings, PluginContexts ) class MyAppConfig(AppConfig): name = u'full_python_path.my_app' @@ -184,6 +184,19 @@ class:: PluginSignals.SENDER_PATH: u'full_path_to_sender_app.ModelZ', }], } + }, + + # Configuration setting for Plugin Contexts for this app. + PluginContexts.CONFIG: { + + # Configure the Plugin Signals for each Project Type, as needed. + ProjectType.LMS: { + + # Key is the view that the app wishes to add context to and the value + # is the function within the app that will return additional context + # when called with the original context + PluginContexts.VIEWS.STUDENT_DASHBOARD: u'my_app.context_api.get_dashboard_context' + } } } @@ -217,6 +230,11 @@ OR use string constants when they cannot import from djangoapps.plugins:: u'sender_path': u'full_path_to_sender_app.ModelZ', }], } + }, + u'context_config': { + u'lms.djangoapp': { + 'student_dashboard': u'my_app.context_api.get_dashboard_context' + } } } diff --git a/openedx/core/djangoapps/plugins/constants.py b/openedx/core/djangoapps/plugins/constants.py index 56949535dffe2cb41e11b7bb53ff2f850dd89c90..8683429ce86fa1218b342e23e7175a6ea9a2dd0a 100644 --- a/openedx/core/djangoapps/plugins/constants.py +++ b/openedx/core/djangoapps/plugins/constants.py @@ -76,3 +76,19 @@ class PluginSignals(object): RELATIVE_PATH = u'relative_path' DEFAULT_RELATIVE_PATH = u'signals' + + +class PluginContextsViews(object): + STUDENT_DASHBOARD = u'student_dashboard' + + +class PluginContexts(object): + """ + The PluginContexts enum defines dictionary field names (and defaults) + that can be specified by a Plugin App in order to configure the + additional views it would like to add context into. + """ + CONFIG = u"context_config" + + VIEWS = PluginContextsViews + diff --git a/openedx/core/djangoapps/plugins/plugin_contexts.py b/openedx/core/djangoapps/plugins/plugin_contexts.py new file mode 100644 index 0000000000000000000000000000000000000000..abf482c67cfdb7d3b3234dd328a7ac060e61cd44 --- /dev/null +++ b/openedx/core/djangoapps/plugins/plugin_contexts.py @@ -0,0 +1,35 @@ +from importlib import import_module + +from logging import getLogger +from . import constants, registry + +log = getLogger(__name__) + + +def get_plugins_view_context(project_type, view_name, existing_context={}): + """ + Returns a dict of additonal view context. Will check if any plugin apps + have that view in their context_config, and if so will call their + selected function to get their context dicts. + """ + aggregate_context = {} + + for app_config in registry.get_app_configs(project_type): + context_function_path = _get_context_function(app_config, project_type, view_name) + if context_function_path: + module_path, _, name = context_function_path.rpartition('.') + context_function = getattr(import_module(module_path), name) + plugin_context = context_function(existing_context) + + # NOTE: If two plugins have try to set the same context keys, the last one + # called will overwrite the others. + aggregate_context.update(plugin_context) + + return aggregate_context + + +def _get_context_function(app_config, project_type, view_name): + plugin_config = getattr(app_config, constants.PLUGIN_APP_CLASS_ATTRIBUTE_NAME, {}) + context_config = plugin_config.get(constants.PluginContexts.CONFIG, {}) + project_type_settings = context_config.get(project_type, {}) + return project_type_settings.get(view_name)