From bc1e9afe4bcfeeb97ebf77bbecf59580298366f5 Mon Sep 17 00:00:00 2001 From: Piotr Surowiec Date: Wed, 7 Apr 2021 15:42:12 +0200 Subject: [PATCH] feat: allow overriding unit icons (#21433) This: 1. Introduces a new override using the `pluggable_override` decorator. It is now possible to specify a custom way of getting XBlock's icon by defining `GET_UNIT_ICON_IMPL` in settings. 2. Introduces a way to add custom `XBLOCK_MIXINS` by defining `XBLOCK_EXTRA_MIXINS` in settings. This allows, e.g. to add new fields to XBlocks. --- common/lib/xmodule/xmodule/seq_module.py | 6 ++++-- docs/guides/extension_points.rst | 5 +++++ lms/devstack.yml | 1 + lms/envs/common.py | 1 + lms/envs/production.py | 3 +++ openedx/core/lib/xblock_utils/__init__.py | 11 +++++++++++ requirements/edx/base.in | 2 +- 7 files changed, 26 insertions(+), 3 deletions(-) diff --git a/common/lib/xmodule/xmodule/seq_module.py b/common/lib/xmodule/xmodule/seq_module.py index eba02ef5a6..618eefe62f 100644 --- a/common/lib/xmodule/xmodule/seq_module.py +++ b/common/lib/xmodule/xmodule/seq_module.py @@ -692,6 +692,9 @@ class SequenceBlock( display_items. Returns a list of dict objects with information about the given display_items. """ + # Avoid circular imports. + from openedx.core.lib.xblock_utils import get_icon + render_items = not context.get('exclude_units', False) is_user_authenticated = self.is_user_authenticated(context) completion_service = self.runtime.service(self, 'completion') @@ -708,8 +711,7 @@ class SequenceBlock( ] contents = [] for item in display_items: - # NOTE (CCB): This seems like a hack, but I don't see a better method of determining the type/category. - item_type = item.get_icon_class() + item_type = get_icon(item) usage_id = item.scope_ids.usage_id show_bookmark_button = False diff --git a/docs/guides/extension_points.rst b/docs/guides/extension_points.rst index 9b4b5f4e18..5a178145fe 100644 --- a/docs/guides/extension_points.rst +++ b/docs/guides/extension_points.rst @@ -134,6 +134,9 @@ Here are the different integration points that python plugins can use: * - XBlock unit tests (``xblock.test.v0``) - Assess, Limited - XBlocks can also install test code that will then be run alongside the platform's usual python unit tests. It's unclear how well-supported this is at the moment. + * - Pluggable override (``edx_django_utils.plugins.pluggable_override.pluggable_override``) + - Trial, Stable + - This decorator allows overriding any function or method by pointing to an alternative implementation in settings. Read the |pluggable_override docstring|_ to learn more. .. _Application: https://docs.djangoproject.com/en/3.0/ref/applications/ .. _Django app plugin documentation: https://github.com/edx/edx-platform/blob/master/openedx/core/djangoapps/plugins/README.rst @@ -146,6 +149,8 @@ Here are the different integration points that python plugins can use: .. _learning_context.py: https://github.com/edx/edx-platform/blob/master/openedx/core/djangoapps/xblock/learning_context/learning_context.py .. |UserPartition docstring| replace:: ``UserPartition`` docstring .. _UserPartition docstring: https://github.com/edx/edx-platform/blob/f8cc58618a39c9f7b8e9e1001eb2d7a10395797e/common/lib/xmodule/xmodule/partitions/partitions.py#L105-L120 +.. |pluggable_override docstring| replace:: ``pluggable_override`` docstring +.. _pluggable_override docstring: https://github.com/edx/edx-django-utils/blob/master/edx_django_utils/plugins/pluggable_override.py Platform Look & Feel ==================== diff --git a/lms/devstack.yml b/lms/devstack.yml index fded34d604..41a72e2938 100644 --- a/lms/devstack.yml +++ b/lms/devstack.yml @@ -563,6 +563,7 @@ VIDEO_UPLOAD_PIPELINE: ROOT_PATH: '' WIKI_ENABLED: true WRITABLE_GRADEBOOK_URL: null +XBLOCK_EXTRA_MIXINS: [] XBLOCK_FS_STORAGE_BUCKET: null XBLOCK_FS_STORAGE_PREFIX: null XBLOCK_SETTINGS: {} diff --git a/lms/envs/common.py b/lms/envs/common.py index 3ae1918626..dd2e0408f1 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -1424,6 +1424,7 @@ from xmodule.x_module import XModuleMixin # This should be moved into an XBlock Runtime/Application object # once the responsibility of XBlock creation is moved out of modulestore - cpennington XBLOCK_MIXINS = (LmsBlockMixin, InheritanceMixin, XModuleMixin, EditInfoMixin) +XBLOCK_EXTRA_MIXINS = () # .. setting_name: XBLOCK_SELECT_FUNCTION # .. setting_default: prefer_xmodules diff --git a/lms/envs/production.py b/lms/envs/production.py index 12fc4ba5de..43d8290e88 100644 --- a/lms/envs/production.py +++ b/lms/envs/production.py @@ -1047,3 +1047,6 @@ EXPLICIT_QUEUES = { } LOGO_IMAGE_EXTRA_TEXT = ENV_TOKENS.get('LOGO_IMAGE_EXTRA_TEXT', '') + +############## XBlock extra mixins ############################ +XBLOCK_MIXINS += tuple(XBLOCK_EXTRA_MIXINS) diff --git a/openedx/core/lib/xblock_utils/__init__.py b/openedx/core/lib/xblock_utils/__init__.py index 6badc53949..288a995301 100644 --- a/openedx/core/lib/xblock_utils/__init__.py +++ b/openedx/core/lib/xblock_utils/__init__.py @@ -19,6 +19,7 @@ from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imp from django.contrib.staticfiles.storage import staticfiles_storage from django.urls import reverse from django.utils.html import escape +from edx_django_utils.plugins import pluggable_override from lxml import etree, html from opaque_keys.edx.asides import AsideUsageKeyV1, AsideUsageKeyV2 from pytz import UTC @@ -554,3 +555,13 @@ def hash_resource(resource): else: md5.update(repr(data).encode('utf-8')) return md5.hexdigest() + + +@pluggable_override('OVERRIDE_GET_UNIT_ICON') +def get_icon(block): + """ + A function that returns the CSS class representing an icon to use for this particular + XBlock (in the courseware navigation bar). Mostly used for Vertical/Unit XBlocks. + It can be overridden by setting `GET_UNIT_ICON_IMPL` to an alternative implementation. + """ + return block.get_icon_class() diff --git a/requirements/edx/base.in b/requirements/edx/base.in index a6a49643e4..510fa26180 100644 --- a/requirements/edx/base.in +++ b/requirements/edx/base.in @@ -78,7 +78,7 @@ edx-celeryutils edx-completion edx-django-release-util # Release utils for the edx release pipeline edx-django-sites-extensions -edx-django-utils>=3.12.0 # Utilities for cache, monitoring, and plugins; 3.12.0+ for set_code_owner_attribute method +edx-django-utils>=3.16.0 # Utilities for cache, monitoring, and plugins; 3.16.0+ for the `pluggable_override` edx-drf-extensions edx-enterprise edx-event-routing-backends