Skip to content

Commit

Permalink
feat: theme-agnostic view to fetch theme assets (#29461)
Browse files Browse the repository at this point in the history
It is possible to set custom logos in microfrontends, for instance with the
LOGO_URL setting. Ideally, we would like that MFEs share the same logos as the
LMS. But this is made difficult when comprehensive theming is enabled, and the
logo is overridden by a custom theme. In that scenario, the logo url becomes
/static/mytheme/images/logo.png. But the MFEs do no know that the "mytheme"
theme is enabled. To resolve this issue, we introduce here a view, at the
"/theming/asset/<path>" url, that redirects to the corresponding asset in the
theme that is currently enabled. Thus, MFEs only have to set
`LOGO_URL=http://lmshost/theming/asset/images/logo.png` to point to the themed
logo.

Related issue: overhangio/tutor-mfe#25
  • Loading branch information
regisb authored Dec 2, 2021
1 parent 02b9e05 commit 1334283
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 34 deletions.
51 changes: 27 additions & 24 deletions openedx/core/djangoapps/theming/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
"""
Tests for comprehensive them
"""

from unittest.mock import patch

from django.conf import settings
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sites.models import Site
from django.test import TestCase
from django.test import TestCase, override_settings
from django.urls import reverse

from common.djangoapps.student.tests.factories import GlobalStaffFactory
from openedx.core.djangoapps.theming.middleware import CurrentSiteThemeMiddleware
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.student.tests.factories import GlobalStaffFactory, UserFactory
from openedx.core.djangoapps.theming.models import SiteTheme

THEMING_ADMIN_URL = '/theming/admin'
TEST_THEME_NAME = 'test-theme'
Expand All @@ -21,23 +20,6 @@ class TestThemingViews(TestCase):
"""
Test theming views.
"""
def setUp(self):
"""
Initialize middleware and related objects
"""
super().setUp()

self.site_theme_middleware = CurrentSiteThemeMiddleware()
self.user = UserFactory.create()

def initialize_mock_request(self, request):
"""
Initialize a test request.
"""
request.user = self.user
request.site, __ = Site.objects.get_or_create(domain='test', name='test')
request.session = {}
MessageMiddleware().process_request(request)

def test_preview_theme_access(self):
"""
Expand All @@ -57,7 +39,8 @@ def test_preview_theme_access(self):
)

# Logged in non-global staff get a 404
self.client.login(username=self.user.username, password=TEST_PASSWORD)
non_global_staff_user = UserFactory.create()
self.client.login(username=non_global_staff_user.username, password=TEST_PASSWORD)
response = self.client.get(THEMING_ADMIN_URL)
assert response.status_code == 404

Expand Down Expand Up @@ -108,3 +91,23 @@ def test_preview_theme(self):
response,
f'<option value="{TEST_THEME_NAME}">'
)

def test_asset_no_theme(self):
"""
Fetch theme asset when no theme is set.
"""
response = self.client.get(reverse("theming:openedx.theming.asset", kwargs={"path": "images/logo.png"}))
assert response.status_code == 302
assert response.url == "/static/images/logo.png"

@override_settings(STATICFILES_STORAGE="openedx.core.storage.DevelopmentStorage")
def test_asset_with_theme(self):
"""
Fetch theme asset when a theme is set.
"""
SiteTheme.objects.create(site=Site.objects.get(), theme_dir_name="red-theme")
with patch("openedx.core.storage.DevelopmentStorage.themed") as mock_is_themed:
response = self.client.get(reverse("theming:openedx.theming.asset", kwargs={"path": "images/logo.png"}))
mock_is_themed.assert_called_once_with("images/logo.png", "red-theme")
assert response.status_code == 302
assert response.url == "/static/red-theme/images/logo.png"
27 changes: 17 additions & 10 deletions openedx/core/djangoapps/theming/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,26 @@


from django.conf.urls import url
from django.urls import path

from .helpers import is_comprehensive_theming_enabled
from .views import ThemingAdministrationFragmentView
from . import helpers
from . import views

app_name = 'openedx.core.djangoapps.theming'
app_name = "openedx.core.djangoapps.theming"

if is_comprehensive_theming_enabled():
urlpatterns = [
urlpatterns = [
path(
"asset/<path:path>",
views.themed_asset,
name="openedx.theming.asset",
),
]

if helpers.is_comprehensive_theming_enabled():
urlpatterns += [
url(
r'^admin',
ThemingAdministrationFragmentView.as_view(),
name='openedx.theming.update_theme_fragment_view',
r"^admin",
views.ThemingAdministrationFragmentView.as_view(),
name="openedx.theming.update_theme_fragment_view",
),
]
else:
urlpatterns = []
16 changes: 16 additions & 0 deletions openedx/core/djangoapps/theming/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from common.djangoapps.student.roles import GlobalStaff

from .helpers import theme_exists
from .helpers_static import get_static_file_url
from .models import SiteTheme

PREVIEW_SITE_THEME_PREFERENCE_KEY = 'preview-site-theme'
Expand Down Expand Up @@ -135,3 +136,18 @@ def standalone_page_title(self, request, fragment, **kwargs):
Returns the page title for the standalone update page.
"""
return _('Theming Administration')


def themed_asset(request, path):
"""
Redirect to themed asset.
This view makes it easy to link to theme assets without knowing what is the
currently enabled theme. For instance, applications outside of the LMS may
want to link to the LMS logo.
Note that the redirect is not permanent because the theme may change from
one run to the next.
"""
themed_url = get_static_file_url(path)
return redirect(themed_url, permanent=False)

0 comments on commit 1334283

Please sign in to comment.