From 15c8dec041e088b4a995b140103e0d1353bf7867 Mon Sep 17 00:00:00 2001 From: Markos Gogoulos Date: Fri, 16 Jan 2026 13:44:04 +0200 Subject: [PATCH] you --- cms/version.py | 2 +- lti/deep_linking.py | 33 +++++++++++++++++++++++++++------ lti/urls.py | 2 ++ lti/views.py | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/cms/version.py b/cms/version.py index efd9ce7e..47d48c0c 100644 --- a/cms/version.py +++ b/cms/version.py @@ -1 +1 @@ -VERSION = "7.8" +VERSION = "7.8124" diff --git a/lti/deep_linking.py b/lti/deep_linking.py index c25fccb0..df64e137 100644 --- a/lti/deep_linking.py +++ b/lti/deep_linking.py @@ -38,10 +38,14 @@ class SelectMediaView(View): def get(self, request): """Display media selection interface""" - # Get deep link session data - deep_link_data = request.session.get('lti_deep_link') - if not deep_link_data: - return JsonResponse({'error': 'No deep linking session data found'}, status=400) + # Check if this is a TinyMCE request (no deep linking session required) + is_tinymce = request.GET.get('mode') == 'tinymce' + + if not is_tinymce: + # Get deep link session data for regular deep linking flow + deep_link_data = request.session.get('lti_deep_link') + if not deep_link_data: + return JsonResponse({'error': 'No deep linking session data found'}, status=400) # Reuse MediaList logic to get media with proper permissions media_list_view = MediaList() @@ -54,8 +58,25 @@ class SelectMediaView(View): if show_my_media_only: media_queryset = media_queryset.filter(user=request.user) - # Order by recent and limit for performance - media_list = media_queryset.order_by('-add_date')[:100] + # Order by recent + media_queryset = media_queryset.order_by('-add_date') + + # TinyMCE mode: Use pagination + if is_tinymce: + from django.core.paginator import Paginator + + paginator = Paginator(media_queryset, 24) # 24 items per page + page_number = request.GET.get('page', 1) + page_obj = paginator.get_page(page_number) + + context = { + 'media_list': page_obj, + 'page_obj': page_obj, + } + return render(request, 'lti/tinymce_select_media.html', context) + + # Deep linking mode: Limit for performance + media_list = media_queryset[:100] context = { 'media_list': media_list, diff --git a/lti/urls.py b/lti/urls.py index 99c1e94d..31b25b2a 100644 --- a/lti/urls.py +++ b/lti/urls.py @@ -21,4 +21,6 @@ urlpatterns = [ path('embed//', views.EmbedMediaLTIView.as_view(), name='embed_media'), # Manual sync path('sync///', views.ManualSyncView.as_view(), name='manual_sync'), + # TinyMCE integration (reuses select-media with mode=tinymce parameter) + path('tinymce-embed//', views.TinyMCEGetEmbedView.as_view(), name='tinymce_embed'), ] diff --git a/lti/views.py b/lti/views.py index bb3f7818..01b4a264 100644 --- a/lti/views.py +++ b/lti/views.py @@ -407,3 +407,39 @@ class ManualSyncView(APIView): except Exception as e: return Response({'error': 'Sync failed', 'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@method_decorator(xframe_options_exempt, name='dispatch') +class TinyMCEGetEmbedView(View): + """ + API endpoint to get embed code for a specific media item (for TinyMCE integration). + + Returns JSON with the embed code for the requested media. + Requires: User must be logged in (via LTI session) + """ + + def get(self, request, friendly_token): + """Get embed code for the specified media.""" + # Verify user is authenticated + if not request.user.is_authenticated: + return JsonResponse({'error': 'Authentication required'}, status=401) + + # Verify media exists + media = Media.objects.filter(friendly_token=friendly_token).first() + + if not media: + return JsonResponse({'error': 'Media not found'}, status=404) + + # Build embed URL + embed_url = request.build_absolute_uri(reverse('get_embed') + f'?m={friendly_token}') + + # Generate iframe embed code + embed_code = f'' + + return JsonResponse( + { + 'embedCode': embed_code, + 'title': media.title, + 'thumbnail': media.thumbnail_url if hasattr(media, 'thumbnail_url') else '', + } + )